123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- package middleware
- import "fmt"
- // RelativePosition provides specifying the relative position of a middleware
- // in an ordered group.
- type RelativePosition int
- // Relative position for middleware in steps.
- const (
- After RelativePosition = iota
- Before
- )
- type ider interface {
- ID() string
- }
- // orderedIDs provides an ordered collection of items with relative ordering
- // by name.
- type orderedIDs struct {
- order *relativeOrder
- items map[string]ider
- }
- const baseOrderedItems = 5
- func newOrderedIDs() *orderedIDs {
- return &orderedIDs{
- order: newRelativeOrder(),
- items: make(map[string]ider, baseOrderedItems),
- }
- }
- // Add injects the item to the relative position of the item group. Returns an
- // error if the item already exists.
- func (g *orderedIDs) Add(m ider, pos RelativePosition) error {
- id := m.ID()
- if len(id) == 0 {
- return fmt.Errorf("empty ID, ID must not be empty")
- }
- if err := g.order.Add(pos, id); err != nil {
- return err
- }
- g.items[id] = m
- return nil
- }
- // Insert injects the item relative to an existing item id. Returns an error if
- // the original item does not exist, or the item being added already exists.
- func (g *orderedIDs) Insert(m ider, relativeTo string, pos RelativePosition) error {
- if len(m.ID()) == 0 {
- return fmt.Errorf("insert ID must not be empty")
- }
- if len(relativeTo) == 0 {
- return fmt.Errorf("relative to ID must not be empty")
- }
- if err := g.order.Insert(relativeTo, pos, m.ID()); err != nil {
- return err
- }
- g.items[m.ID()] = m
- return nil
- }
- // Get returns the ider identified by id. If ider is not present, returns false.
- func (g *orderedIDs) Get(id string) (ider, bool) {
- v, ok := g.items[id]
- return v, ok
- }
- // Swap removes the item by id, replacing it with the new item. Returns an error
- // if the original item doesn't exist.
- func (g *orderedIDs) Swap(id string, m ider) (ider, error) {
- if len(id) == 0 {
- return nil, fmt.Errorf("swap from ID must not be empty")
- }
- iderID := m.ID()
- if len(iderID) == 0 {
- return nil, fmt.Errorf("swap to ID must not be empty")
- }
- if err := g.order.Swap(id, iderID); err != nil {
- return nil, err
- }
- removed := g.items[id]
- delete(g.items, id)
- g.items[iderID] = m
- return removed, nil
- }
- // Remove removes the item by id. Returns an error if the item
- // doesn't exist.
- func (g *orderedIDs) Remove(id string) (ider, error) {
- if len(id) == 0 {
- return nil, fmt.Errorf("remove ID must not be empty")
- }
- if err := g.order.Remove(id); err != nil {
- return nil, err
- }
- removed := g.items[id]
- delete(g.items, id)
- return removed, nil
- }
- func (g *orderedIDs) List() []string {
- items := g.order.List()
- order := make([]string, len(items))
- copy(order, items)
- return order
- }
- // Clear removes all entries and slots.
- func (g *orderedIDs) Clear() {
- g.order.Clear()
- g.items = map[string]ider{}
- }
- // GetOrder returns the item in the order it should be invoked in.
- func (g *orderedIDs) GetOrder() []interface{} {
- order := g.order.List()
- ordered := make([]interface{}, len(order))
- for i := 0; i < len(order); i++ {
- ordered[i] = g.items[order[i]]
- }
- return ordered
- }
- // relativeOrder provides ordering of item
- type relativeOrder struct {
- order []string
- }
- func newRelativeOrder() *relativeOrder {
- return &relativeOrder{
- order: make([]string, 0, baseOrderedItems),
- }
- }
- // Add inserts an item into the order relative to the position provided.
- func (s *relativeOrder) Add(pos RelativePosition, ids ...string) error {
- if len(ids) == 0 {
- return nil
- }
- for _, id := range ids {
- if _, ok := s.has(id); ok {
- return fmt.Errorf("already exists, %v", id)
- }
- }
- switch pos {
- case Before:
- return s.insert(0, Before, ids...)
- case After:
- s.order = append(s.order, ids...)
- default:
- return fmt.Errorf("invalid position, %v", int(pos))
- }
- return nil
- }
- // Insert injects an item before or after the relative item. Returns
- // an error if the relative item does not exist.
- func (s *relativeOrder) Insert(relativeTo string, pos RelativePosition, ids ...string) error {
- if len(ids) == 0 {
- return nil
- }
- for _, id := range ids {
- if _, ok := s.has(id); ok {
- return fmt.Errorf("already exists, %v", id)
- }
- }
- i, ok := s.has(relativeTo)
- if !ok {
- return fmt.Errorf("not found, %v", relativeTo)
- }
- return s.insert(i, pos, ids...)
- }
- // Swap will replace the item id with the to item. Returns an
- // error if the original item id does not exist. Allows swapping out an
- // item for another item with the same id.
- func (s *relativeOrder) Swap(id, to string) error {
- i, ok := s.has(id)
- if !ok {
- return fmt.Errorf("not found, %v", id)
- }
- if _, ok = s.has(to); ok && id != to {
- return fmt.Errorf("already exists, %v", to)
- }
- s.order[i] = to
- return nil
- }
- func (s *relativeOrder) Remove(id string) error {
- i, ok := s.has(id)
- if !ok {
- return fmt.Errorf("not found, %v", id)
- }
- s.order = append(s.order[:i], s.order[i+1:]...)
- return nil
- }
- func (s *relativeOrder) List() []string {
- return s.order
- }
- func (s *relativeOrder) Clear() {
- s.order = s.order[0:0]
- }
- func (s *relativeOrder) insert(i int, pos RelativePosition, ids ...string) error {
- switch pos {
- case Before:
- n := len(ids)
- var src []string
- if n <= cap(s.order)-len(s.order) {
- s.order = s.order[:len(s.order)+n]
- src = s.order
- } else {
- src = s.order
- s.order = make([]string, len(s.order)+n)
- copy(s.order[:i], src[:i]) // only when allocating a new slice do we need to copy the front half
- }
- copy(s.order[i+n:], src[i:])
- copy(s.order[i:], ids)
- case After:
- if i == len(s.order)-1 || len(s.order) == 0 {
- s.order = append(s.order, ids...)
- } else {
- s.order = append(s.order[:i+1], append(ids, s.order[i+1:]...)...)
- }
- default:
- return fmt.Errorf("invalid position, %v", int(pos))
- }
- return nil
- }
- func (s *relativeOrder) has(id string) (i int, found bool) {
- for i := 0; i < len(s.order); i++ {
- if s.order[i] == id {
- return i, true
- }
- }
- return 0, false
- }
|