ordered_group.go

  1package middleware
  2
  3import "fmt"
  4
  5// RelativePosition provides specifying the relative position of a middleware
  6// in an ordered group.
  7type RelativePosition int
  8
  9// Relative position for middleware in steps.
 10const (
 11	After RelativePosition = iota
 12	Before
 13)
 14
 15type ider interface {
 16	ID() string
 17}
 18
 19// orderedIDs provides an ordered collection of items with relative ordering
 20// by name.
 21type orderedIDs struct {
 22	order *relativeOrder
 23	items map[string]ider
 24}
 25
 26const baseOrderedItems = 5
 27
 28func 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.
 37func (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.
 53func (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.
 70func (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.
 77func (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.
101func (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
115func (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.
123func (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.
129func (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
140type relativeOrder struct {
141	order []string
142}
143
144func 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.
151func (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.
178func (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.
200func (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
214func (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
224func (s *relativeOrder) List() []string {
225	return s.order
226}
227
228func (s *relativeOrder) Clear() {
229	s.order = s.order[0:0]
230}
231
232func (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
261func (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}