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