k9s/internal/render/event.go

261 lines
5.1 KiB
Go

package render
import (
"fmt"
"sort"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const (
// EventUnchanged notifies listener resource has not changed.
EventUnchanged ResEvent = 1 << iota
// EventAdd notifies listener of a resource was added.
EventAdd
// EventUpdate notifies listener of a resource updated.
EventUpdate
// EventDelete notifies listener of a resource was deleted.
EventDelete
// EventClear the stack was reset.
EventClear
)
// ResEvent represents a resource event.
type ResEvent int
// RowEvent tracks resource instance events.
type RowEvent struct {
Kind ResEvent
Row Row
Deltas DeltaRow
}
// RowEvents a collection of row events.
type RowEvents []RowEvent
// NewRowEvent returns a new row event.
func NewRowEvent(kind ResEvent, row Row) RowEvent {
return RowEvent{
Kind: kind,
Row: row,
}
}
// NewDeltaRowEvent returns a new row event with deltas.
func NewDeltaRowEvent(row Row, delta DeltaRow) RowEvent {
return RowEvent{
Kind: EventUpdate,
Row: row,
Deltas: delta,
}
}
// Clone returns a rowevent deep copy.
func (r RowEvent) Clone() RowEvent {
return RowEvent{
Kind: r.Kind,
Row: r.Row.Clone(),
Deltas: r.Deltas.Clone(),
}
}
// Clone returns a rowevents deep copy.
func (rr RowEvents) Clone() RowEvents {
res := make(RowEvents, len(rr))
for i, r := range rr {
res[i] = r.Clone()
}
return res
}
// Upsert add or update a row if it exists.
func (rr RowEvents) Upsert(e RowEvent) RowEvents {
if idx, ok := rr.FindIndex(e.Row.ID); ok {
rr[idx] = e
} else {
rr = append(rr, e)
}
return rr
}
// Delete removes an element by id.
func (rr RowEvents) Delete(id string) RowEvents {
idx, ok := rr.FindIndex(id)
if !ok {
return rr
}
if idx == 0 {
return rr[1:]
}
if idx == len(rr)-1 {
return rr[:len(rr)-1]
}
return append(rr[:idx], rr[idx+1:]...)
}
// Clear delete all row events
func (rr RowEvents) Clear() RowEvents {
for _, e := range rr {
rr = rr.Delete(e.Row.ID)
}
return rr
}
// FindIndex locates a row index by id. Returns false is not found.
func (rr RowEvents) FindIndex(id string) (int, bool) {
for i, e := range rr {
if e.Row.ID == id {
return i, true
}
}
return 0, false
}
// Sort rows based on column index and order.
func (rr RowEvents) Sort(ns string, col int, asc bool) {
t := RowEventSorter{NS: ns, Events: rr, Index: col, Asc: asc}
sort.Sort(t)
gg, kk := map[string][]string{}, make(StringSet, 0, len(rr))
for _, e := range rr {
g := e.Row.Fields[col]
kk = kk.Add(g)
if ss, ok := gg[g]; ok {
gg[g] = append(ss, e.Row.ID)
} else {
gg[g] = []string{e.Row.ID}
}
}
ids := make([]string, 0, len(rr))
for _, k := range kk {
sort.StringSlice(gg[k]).Sort()
ids = append(ids, gg[k]...)
}
s := IdSorter{Ids: ids, Events: rr}
sort.Sort(s)
}
// ----------------------------------------------------------------------------
// RowEventSorter sorts row events by a given colon.
type RowEventSorter struct {
Events RowEvents
Index int
NS string
Asc bool
}
func (r RowEventSorter) Len() int {
return len(r.Events)
}
func (r RowEventSorter) Swap(i, j int) {
r.Events[i], r.Events[j] = r.Events[j], r.Events[i]
}
func (r RowEventSorter) Less(i, j int) bool {
f1, f2 := r.Events[i].Row.Fields, r.Events[j].Row.Fields
return Less(r.Asc, f1[r.Index], f2[r.Index])
}
// ----------------------------------------------------------------------------
// IdSorter sorts row events by a given id.
type IdSorter struct {
Ids []string
Events RowEvents
}
func (s IdSorter) Len() int {
return len(s.Events)
}
func (s IdSorter) Swap(i, j int) {
s.Events[i], s.Events[j] = s.Events[j], s.Events[i]
}
func (s IdSorter) Less(i, j int) bool {
id1, id2 := s.Events[i].Row.ID, s.Events[j].Row.ID
i1, i2 := findIndex(s.Ids, id1), findIndex(s.Ids, id2)
return i1 < i2
}
func findIndex(ss []string, s string) int {
for i := range ss {
if ss[i] == s {
return i
}
}
log.Error().Err(fmt.Errorf("Doh! index not found for %s", s))
return -1
}
// ----------------------------------------------------------------------------
var (
// ModColor row modified color.
ModColor tcell.Color
// AddColor row added color.
AddColor tcell.Color
// ErrColor row err color.
ErrColor tcell.Color
// StdColor row default color.
StdColor tcell.Color
// HighlightColor row highlight color.
HighlightColor tcell.Color
// KillColor row deleted color.
KillColor tcell.Color
// CompletedColor row completed color.
CompletedColor tcell.Color
)
// ColorerFunc represents a resource row colorer.
type ColorerFunc func(ns string, evt RowEvent) tcell.Color
// DefaultColorer set the default table row colors.
func DefaultColorer(ns string, evt RowEvent) tcell.Color {
var col = StdColor
switch evt.Kind {
case EventAdd:
col = AddColor
case EventUpdate:
col = ModColor
case EventDelete:
col = KillColor
}
return col
}
type StringSet []string
func (ss StringSet) Add(item string) StringSet {
if ss.In(item) {
return ss
}
return append(ss, item)
}
func (ss StringSet) In(item string) bool {
return ss.indexOf(item) >= 0
}
func (ss StringSet) indexOf(item string) int {
for i, s := range ss {
if s == item {
return i
}
}
return -1
}