ordered_group.go (6032B)
1 package middleware 2 3 import "fmt" 4 5 // RelativePosition provides specifying the relative position of a middleware 6 // in an ordered group. 7 type RelativePosition int 8 9 // Relative position for middleware in steps. 10 const ( 11 After RelativePosition = iota 12 Before 13 ) 14 15 type ider interface { 16 ID() string 17 } 18 19 // orderedIDs provides an ordered collection of items with relative ordering 20 // by name. 21 type orderedIDs struct { 22 order *relativeOrder 23 items map[string]ider 24 } 25 26 // selected based on the general upper bound of # of middlewares in each step 27 // in the downstream aws-sdk-go-v2 28 const baseOrderedItems = 8 29 30 func newOrderedIDs(cap int) *orderedIDs { 31 return &orderedIDs{ 32 order: newRelativeOrder(cap), 33 items: make(map[string]ider, cap), 34 } 35 } 36 37 // Add injects the item to the relative position of the item group. Returns an 38 // error if the item already exists. 39 func (g *orderedIDs) Add(m ider, pos RelativePosition) error { 40 id := m.ID() 41 if len(id) == 0 { 42 return fmt.Errorf("empty ID, ID must not be empty") 43 } 44 45 if err := g.order.Add(pos, id); err != nil { 46 return err 47 } 48 49 g.items[id] = m 50 return nil 51 } 52 53 // Insert injects the item relative to an existing item id. Returns an error if 54 // the original item does not exist, or the item being added already exists. 55 func (g *orderedIDs) Insert(m ider, relativeTo string, pos RelativePosition) error { 56 if len(m.ID()) == 0 { 57 return fmt.Errorf("insert ID must not be empty") 58 } 59 if len(relativeTo) == 0 { 60 return fmt.Errorf("relative to ID must not be empty") 61 } 62 63 if err := g.order.Insert(relativeTo, pos, m.ID()); err != nil { 64 return err 65 } 66 67 g.items[m.ID()] = m 68 return nil 69 } 70 71 // Get returns the ider identified by id. If ider is not present, returns false. 72 func (g *orderedIDs) Get(id string) (ider, bool) { 73 v, ok := g.items[id] 74 return v, ok 75 } 76 77 // Swap removes the item by id, replacing it with the new item. Returns an error 78 // if the original item doesn't exist. 79 func (g *orderedIDs) Swap(id string, m ider) (ider, error) { 80 if len(id) == 0 { 81 return nil, fmt.Errorf("swap from ID must not be empty") 82 } 83 84 iderID := m.ID() 85 if len(iderID) == 0 { 86 return nil, fmt.Errorf("swap to ID must not be empty") 87 } 88 89 if err := g.order.Swap(id, iderID); err != nil { 90 return nil, err 91 } 92 93 removed := g.items[id] 94 95 delete(g.items, id) 96 g.items[iderID] = m 97 98 return removed, nil 99 } 100 101 // Remove removes the item by id. Returns an error if the item 102 // doesn't exist. 103 func (g *orderedIDs) Remove(id string) (ider, error) { 104 if len(id) == 0 { 105 return nil, fmt.Errorf("remove ID must not be empty") 106 } 107 108 if err := g.order.Remove(id); err != nil { 109 return nil, err 110 } 111 112 removed := g.items[id] 113 delete(g.items, id) 114 return removed, nil 115 } 116 117 func (g *orderedIDs) List() []string { 118 items := g.order.List() 119 order := make([]string, len(items)) 120 copy(order, items) 121 return order 122 } 123 124 // Clear removes all entries and slots. 125 func (g *orderedIDs) Clear() { 126 g.order.Clear() 127 g.items = map[string]ider{} 128 } 129 130 // GetOrder returns the item in the order it should be invoked in. 131 func (g *orderedIDs) GetOrder() []interface{} { 132 order := g.order.List() 133 ordered := make([]interface{}, len(order)) 134 for i := 0; i < len(order); i++ { 135 ordered[i] = g.items[order[i]] 136 } 137 138 return ordered 139 } 140 141 // relativeOrder provides ordering of item 142 type relativeOrder struct { 143 order []string 144 } 145 146 func newRelativeOrder(cap int) *relativeOrder { 147 return &relativeOrder{ 148 order: make([]string, 0, cap), 149 } 150 } 151 152 // Add inserts an item into the order relative to the position provided. 153 func (s *relativeOrder) Add(pos RelativePosition, ids ...string) error { 154 if len(ids) == 0 { 155 return nil 156 } 157 158 for _, id := range ids { 159 if _, ok := s.has(id); ok { 160 return fmt.Errorf("already exists, %v", id) 161 } 162 } 163 164 switch pos { 165 case Before: 166 return s.insert(0, Before, ids...) 167 168 case After: 169 s.order = append(s.order, ids...) 170 171 default: 172 return fmt.Errorf("invalid position, %v", int(pos)) 173 } 174 175 return nil 176 } 177 178 // Insert injects an item before or after the relative item. Returns 179 // an error if the relative item does not exist. 180 func (s *relativeOrder) Insert(relativeTo string, pos RelativePosition, ids ...string) error { 181 if len(ids) == 0 { 182 return nil 183 } 184 185 for _, id := range ids { 186 if _, ok := s.has(id); ok { 187 return fmt.Errorf("already exists, %v", id) 188 } 189 } 190 191 i, ok := s.has(relativeTo) 192 if !ok { 193 return fmt.Errorf("not found, %v", relativeTo) 194 } 195 196 return s.insert(i, pos, ids...) 197 } 198 199 // Swap will replace the item id with the to item. Returns an 200 // error if the original item id does not exist. Allows swapping out an 201 // item for another item with the same id. 202 func (s *relativeOrder) Swap(id, to string) error { 203 i, ok := s.has(id) 204 if !ok { 205 return fmt.Errorf("not found, %v", id) 206 } 207 208 if _, ok = s.has(to); ok && id != to { 209 return fmt.Errorf("already exists, %v", to) 210 } 211 212 s.order[i] = to 213 return nil 214 } 215 216 func (s *relativeOrder) Remove(id string) error { 217 i, ok := s.has(id) 218 if !ok { 219 return fmt.Errorf("not found, %v", id) 220 } 221 222 s.order = append(s.order[:i], s.order[i+1:]...) 223 return nil 224 } 225 226 func (s *relativeOrder) List() []string { 227 return s.order 228 } 229 230 func (s *relativeOrder) Clear() { 231 s.order = s.order[0:0] 232 } 233 234 func (s *relativeOrder) insert(i int, pos RelativePosition, ids ...string) error { 235 switch pos { 236 case Before: 237 n := len(ids) 238 var src []string 239 if n <= cap(s.order)-len(s.order) { 240 s.order = s.order[:len(s.order)+n] 241 src = s.order 242 } else { 243 src = s.order 244 s.order = make([]string, len(s.order)+n) 245 copy(s.order[:i], src[:i]) // only when allocating a new slice do we need to copy the front half 246 } 247 copy(s.order[i+n:], src[i:]) 248 copy(s.order[i:], ids) 249 case After: 250 if i == len(s.order)-1 || len(s.order) == 0 { 251 s.order = append(s.order, ids...) 252 } else { 253 s.order = append(s.order[:i+1], append(ids, s.order[i+1:]...)...) 254 } 255 256 default: 257 return fmt.Errorf("invalid position, %v", int(pos)) 258 } 259 260 return nil 261 } 262 263 func (s *relativeOrder) has(id string) (i int, found bool) { 264 for i := 0; i < len(s.order); i++ { 265 if s.order[i] == id { 266 return i, true 267 } 268 } 269 return 0, false 270 }