code.dwrz.net

Go monorepo.
Log | Files | Refs

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 }