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