src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }