ordered_group.go raw
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 }
271