1package utils 2 3import ( 4 "fmt" 5 "sync" 6) 7 8// ErrEmptyStack is used when an action that requires some 9// content is invoked and the stack is empty 10type ErrEmptyStack struct { 11 action string 12} 13 14func (err ErrEmptyStack) Error() string { 15 return fmt.Sprintf("attempted to %s with empty stack", err.action) 16} 17 18// ErrBadTypeCast is used by PopX functions when the item 19// cannot be typed to X 20type ErrBadTypeCast struct{} 21 22func (err ErrBadTypeCast) Error() string { 23 return "attempted to do a typed pop and item was not of type" 24} 25 26// Stack is a simple type agnostic stack implementation 27type Stack struct { 28 s []interface{} 29 l sync.Mutex 30} 31 32// NewStack create a new stack 33func NewStack() *Stack { 34 s := &Stack{ 35 s: make([]interface{}, 0), 36 } 37 return s 38} 39 40// Push adds an item to the top of the stack. 41func (s *Stack) Push(item interface{}) { 42 s.l.Lock() 43 defer s.l.Unlock() 44 s.s = append(s.s, item) 45} 46 47// Pop removes and returns the top item on the stack, or returns 48// ErrEmptyStack if the stack has no content 49func (s *Stack) Pop() (interface{}, error) { 50 s.l.Lock() 51 defer s.l.Unlock() 52 l := len(s.s) 53 if l > 0 { 54 item := s.s[l-1] 55 s.s = s.s[:l-1] 56 return item, nil 57 } 58 return nil, ErrEmptyStack{action: "Pop"} 59} 60 61// PopString attempts to cast the top item on the stack to the string type. 62// If this succeeds, it removes and returns the top item. If the item 63// is not of the string type, ErrBadTypeCast is returned. If the stack 64// is empty, ErrEmptyStack is returned 65func (s *Stack) PopString() (string, error) { 66 s.l.Lock() 67 defer s.l.Unlock() 68 l := len(s.s) 69 if l > 0 { 70 item := s.s[l-1] 71 if item, ok := item.(string); ok { 72 s.s = s.s[:l-1] 73 return item, nil 74 } 75 return "", ErrBadTypeCast{} 76 } 77 return "", ErrEmptyStack{action: "PopString"} 78} 79 80// Empty returns true if the stack is empty 81func (s *Stack) Empty() bool { 82 s.l.Lock() 83 defer s.l.Unlock() 84 return len(s.s) == 0 85} 86