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