196 lines
3.7 KiB
Go
196 lines
3.7 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Authors of K9s
|
|
|
|
package model
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"sync"
|
|
|
|
"github.com/derailed/k9s/internal/slogs"
|
|
)
|
|
|
|
const (
|
|
// StackPush denotes an add on the stack.
|
|
StackPush StackAction = 1 << iota
|
|
|
|
// StackPop denotes a delete on the stack.
|
|
StackPop
|
|
)
|
|
|
|
// StackAction represents an action on the stack.
|
|
type StackAction int
|
|
|
|
// StackEvent represents an operation on a view stack.
|
|
type StackEvent struct {
|
|
// Kind represents the event condition.
|
|
Action StackAction
|
|
|
|
// Item represents the targeted item.
|
|
Component Component
|
|
}
|
|
|
|
// StackListener represents a stack listener.
|
|
type StackListener interface {
|
|
// StackPushed indicates a new item was added.
|
|
StackPushed(Component)
|
|
|
|
// StackPopped indicates an item was deleted
|
|
StackPopped(old, new Component)
|
|
|
|
// StackTop indicates the top of the stack
|
|
StackTop(Component)
|
|
}
|
|
|
|
// Stack represents a stacks of components.
|
|
type Stack struct {
|
|
components []Component
|
|
listeners []StackListener
|
|
mx sync.RWMutex
|
|
}
|
|
|
|
// NewStack returns a new initialized stack.
|
|
func NewStack() *Stack {
|
|
return &Stack{}
|
|
}
|
|
|
|
// Flatten returns a string representation of the component stack.
|
|
func (s *Stack) Flatten() []string {
|
|
s.mx.RLock()
|
|
defer s.mx.RUnlock()
|
|
|
|
ss := make([]string, len(s.components))
|
|
for i, c := range s.components {
|
|
ss[i] = c.Name()
|
|
}
|
|
return ss
|
|
}
|
|
|
|
// RemoveListener removes a listener.
|
|
func (s *Stack) RemoveListener(l StackListener) {
|
|
victim := -1
|
|
for i, lis := range s.listeners {
|
|
if lis == l {
|
|
victim = i
|
|
break
|
|
}
|
|
}
|
|
if victim == -1 {
|
|
return
|
|
}
|
|
s.listeners = append(s.listeners[:victim], s.listeners[victim+1:]...)
|
|
}
|
|
|
|
// AddListener registers a stack listener.
|
|
func (s *Stack) AddListener(l StackListener) {
|
|
s.listeners = append(s.listeners, l)
|
|
if !s.Empty() {
|
|
l.StackTop(s.Top())
|
|
}
|
|
}
|
|
|
|
// Push adds a new item.
|
|
func (s *Stack) Push(c Component) {
|
|
if top := s.Top(); top != nil {
|
|
top.Stop()
|
|
}
|
|
|
|
s.mx.Lock()
|
|
s.components = append(s.components, c)
|
|
s.mx.Unlock()
|
|
s.notify(StackPush, c)
|
|
}
|
|
|
|
// Pop removed the top item and returns it.
|
|
func (s *Stack) Pop() (Component, bool) {
|
|
if s.Empty() {
|
|
return nil, false
|
|
}
|
|
|
|
var c Component
|
|
s.mx.Lock()
|
|
c = s.components[len(s.components)-1]
|
|
c.Stop()
|
|
s.components = s.components[:len(s.components)-1]
|
|
s.mx.Unlock()
|
|
|
|
s.notify(StackPop, c)
|
|
|
|
return c, true
|
|
}
|
|
|
|
// Peek returns stack state.
|
|
func (s *Stack) Peek() []Component {
|
|
s.mx.RLock()
|
|
defer s.mx.RUnlock()
|
|
|
|
return s.components
|
|
}
|
|
|
|
// Clear clear out the stack using pops.
|
|
func (s *Stack) Clear() {
|
|
for range s.components {
|
|
s.Pop()
|
|
}
|
|
}
|
|
|
|
// Empty returns true if the stack is empty.
|
|
func (s *Stack) Empty() bool {
|
|
s.mx.RLock()
|
|
defer s.mx.RUnlock()
|
|
|
|
return len(s.components) == 0
|
|
}
|
|
|
|
// IsLast indicates if stack only has one item left.
|
|
func (s *Stack) IsLast() bool {
|
|
return len(s.components) == 1
|
|
}
|
|
|
|
// Previous returns the previous component if any.
|
|
func (s *Stack) Previous() Component {
|
|
if s.Empty() {
|
|
return nil
|
|
}
|
|
if s.IsLast() {
|
|
return s.Top()
|
|
}
|
|
|
|
return s.components[len(s.components)-2]
|
|
}
|
|
|
|
// Top returns the top most item or nil if the stack is empty.
|
|
func (s *Stack) Top() Component {
|
|
if s.Empty() {
|
|
return nil
|
|
}
|
|
|
|
s.mx.RLock()
|
|
defer s.mx.RUnlock()
|
|
return s.components[len(s.components)-1]
|
|
}
|
|
|
|
func (s *Stack) notify(a StackAction, c Component) {
|
|
for _, l := range s.listeners {
|
|
switch a {
|
|
case StackPush:
|
|
l.StackPushed(c)
|
|
case StackPop:
|
|
l.StackPopped(c, s.Top())
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Helpers...
|
|
|
|
// Dump prints out the stack.
|
|
func (s *Stack) Dump() {
|
|
slog.Debug("Stack Dump", slogs.Stack, fmt.Sprintf("%p", s))
|
|
for i, c := range s.components {
|
|
slog.Debug(fmt.Sprintf("%d -- %s -- %#v", i, c.Name(), c))
|
|
}
|
|
slog.Debug("------------------")
|
|
}
|