270 lines
5.3 KiB
Go
270 lines
5.3 KiB
Go
package render
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"k8s.io/apimachinery/pkg/util/duration"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
// 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(),
|
|
}
|
|
}
|
|
|
|
// Diff returns true if the row changed.
|
|
func (r RowEvent) Diff(re RowEvent) bool {
|
|
if r.Kind != re.Kind {
|
|
return true
|
|
}
|
|
if !reflect.DeepEqual(r.Deltas, re.Deltas) {
|
|
return true
|
|
}
|
|
|
|
// BOZO!! Canned?? Skip age colum
|
|
return !reflect.DeepEqual(r.Row.Fields[:len(r.Row.Fields)-1], re.Row.Fields[:len(re.Row.Fields)-1])
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// RowEvents a collection of row events.
|
|
type RowEvents []RowEvent
|
|
|
|
// Diff returns true if the event changed.
|
|
func (rr RowEvents) Diff(r RowEvents) bool {
|
|
if len(rr) != len(r) {
|
|
return true
|
|
}
|
|
|
|
for i := range rr {
|
|
if rr[i].Diff(r[i]) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// 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 {
|
|
victim, ok := rr.FindIndex(id)
|
|
if !ok {
|
|
return rr
|
|
}
|
|
return append(rr[0:victim], rr[victim+1:]...)
|
|
}
|
|
|
|
// Clear delete all row events
|
|
func (rr RowEvents) Clear() RowEvents {
|
|
return RowEvents{}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
func (rr RowEvents) isAgeCol(col int) bool {
|
|
var age bool
|
|
if len(rr) == 0 {
|
|
return age
|
|
}
|
|
return col == len(rr[0].Row.Fields)-1
|
|
}
|
|
|
|
// 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)
|
|
|
|
ageCol := rr.isAgeCol(col)
|
|
gg, kk := map[string][]string{}, make(StringSet, 0, len(rr))
|
|
for _, r := range rr {
|
|
g := r.Row.Fields[col]
|
|
if ageCol {
|
|
g = toAgeDuration(g)
|
|
}
|
|
kk = kk.Add(g)
|
|
if ss, ok := gg[g]; ok {
|
|
gg[g] = append(ss, r.Row.ID)
|
|
} else {
|
|
gg[g] = []string{r.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)
|
|
}
|
|
|
|
func toAgeDuration(dur string) string {
|
|
d, err := time.ParseDuration(dur)
|
|
if err != nil {
|
|
return dur
|
|
}
|
|
return duration.HumanDuration(d)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// 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
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// StringSet represents a collection of unique strings.
|
|
type StringSet []string
|
|
|
|
// Add adds a new item in the set.
|
|
func (ss StringSet) Add(item string) StringSet {
|
|
if ss.In(item) {
|
|
return ss
|
|
}
|
|
return append(ss, item)
|
|
}
|
|
|
|
// In checks if a string is in the set.
|
|
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
|
|
}
|