196 lines
3.6 KiB
Go
196 lines
3.6 KiB
Go
package render
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"time"
|
|
|
|
"vbom.ml/util/sortorder"
|
|
)
|
|
|
|
// Fields represents a collection of row fields.
|
|
type Fields []string
|
|
|
|
// Customize returns a subset of fields.
|
|
func (f Fields) Customize(cols []int, out Fields) {
|
|
for i, c := range cols {
|
|
if c < 0 {
|
|
out[i] = NAValue
|
|
continue
|
|
}
|
|
if c < len(f) {
|
|
out[i] = f[c]
|
|
}
|
|
}
|
|
}
|
|
|
|
// Diff returns true if fields differ or false otherwise.
|
|
func (f Fields) Diff(ff Fields, ageCol int) bool {
|
|
if ageCol < 0 {
|
|
return !reflect.DeepEqual(f[:len(f)-1], ff[:len(ff)-1])
|
|
}
|
|
if !reflect.DeepEqual(f[:ageCol], ff[:ageCol]) {
|
|
return true
|
|
}
|
|
return !reflect.DeepEqual(f[ageCol+1:], ff[ageCol+1:])
|
|
}
|
|
|
|
// Clone returns a copy of the fields.
|
|
func (f Fields) Clone() Fields {
|
|
cp := make(Fields, len(f))
|
|
copy(cp, f)
|
|
|
|
return cp
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Row represents a colllection of columns.
|
|
type Row struct {
|
|
ID string
|
|
Fields Fields
|
|
}
|
|
|
|
// NewRow returns a new row with initialized fields.
|
|
func NewRow(size int) Row {
|
|
return Row{Fields: make([]string, size)}
|
|
}
|
|
|
|
// Customize returns a row subset based on given col indices.
|
|
func (r Row) Customize(cols []int) Row {
|
|
out := NewRow(len(cols))
|
|
r.Fields.Customize(cols, out.Fields)
|
|
out.ID = r.ID
|
|
|
|
return out
|
|
}
|
|
|
|
// Diff returns true if row differ or false otherwise.
|
|
func (r Row) Diff(ro Row, ageCol int) bool {
|
|
if r.ID != ro.ID {
|
|
return true
|
|
}
|
|
return r.Fields.Diff(ro.Fields, ageCol)
|
|
}
|
|
|
|
// Clone copies a row.
|
|
func (r Row) Clone() Row {
|
|
return Row{
|
|
ID: r.ID,
|
|
Fields: r.Fields.Clone(),
|
|
}
|
|
}
|
|
|
|
// Len returns the length of the row.
|
|
func (r Row) Len() int {
|
|
return len(r.Fields)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Rows represents a collection of rows.
|
|
type Rows []Row
|
|
|
|
// Delete removes an element by id.
|
|
func (rr Rows) Delete(id string) Rows {
|
|
idx, ok := rr.Find(id)
|
|
if !ok {
|
|
return rr
|
|
}
|
|
|
|
if idx == 0 {
|
|
return rr[1:]
|
|
}
|
|
if idx+1 == len(rr) {
|
|
return rr[:len(rr)-1]
|
|
}
|
|
|
|
return append(rr[:idx], rr[idx+1:]...)
|
|
}
|
|
|
|
// Upsert adds a new item.
|
|
func (rr Rows) Upsert(r Row) Rows {
|
|
idx, ok := rr.Find(r.ID)
|
|
if !ok {
|
|
return append(rr, r)
|
|
}
|
|
rr[idx] = r
|
|
|
|
return rr
|
|
}
|
|
|
|
// Find locates a row by id. Retturns false is not found.
|
|
func (rr Rows) Find(id string) (int, bool) {
|
|
for i, r := range rr {
|
|
if r.ID == id {
|
|
return i, true
|
|
}
|
|
}
|
|
|
|
return 0, false
|
|
}
|
|
|
|
// Sort rows based on column index and order.
|
|
func (rr Rows) Sort(col int, asc bool) {
|
|
t := RowSorter{Rows: rr, Index: col, Asc: asc}
|
|
sort.Sort(t)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// RowSorter sorts rows.
|
|
type RowSorter struct {
|
|
Rows Rows
|
|
Index int
|
|
Asc bool
|
|
}
|
|
|
|
func (s RowSorter) Len() int {
|
|
return len(s.Rows)
|
|
}
|
|
|
|
func (s RowSorter) Swap(i, j int) {
|
|
s.Rows[i], s.Rows[j] = s.Rows[j], s.Rows[i]
|
|
}
|
|
|
|
func (s RowSorter) Less(i, j int) bool {
|
|
return Less(s.Asc, s.Rows[i].Fields[s.Index], s.Rows[j].Fields[s.Index])
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Helpers...
|
|
|
|
// Less return true if c1 < c2.
|
|
func Less(asc bool, c1, c2 string) bool {
|
|
if o, ok := isDurationSort(asc, c1, c2); ok {
|
|
return o
|
|
}
|
|
|
|
b := sortorder.NaturalLess(c1, c2)
|
|
if asc {
|
|
return b
|
|
}
|
|
return !b
|
|
}
|
|
|
|
func isDurationSort(asc bool, s1, s2 string) (bool, bool) {
|
|
d1, ok1 := isDuration(s1)
|
|
d2, ok2 := isDuration(s2)
|
|
if !ok1 || !ok2 {
|
|
return false, false
|
|
}
|
|
|
|
if asc {
|
|
return d1 <= d2, true
|
|
}
|
|
return d1 >= d2, true
|
|
}
|
|
|
|
func isDuration(s string) (time.Duration, bool) {
|
|
d, err := time.ParseDuration(s)
|
|
if err != nil {
|
|
return d, false
|
|
}
|
|
return d, true
|
|
}
|