k9s/internal/render/header.go

187 lines
3.6 KiB
Go

package render
import (
"reflect"
"github.com/rs/zerolog/log"
)
const ageCol = "AGE"
// HeaderColumn represent a table header.
type HeaderColumn struct {
Name string
Align int
Decorator DecoratorFunc
Hide bool
Wide bool
MX bool
Time bool
}
// Clone copies a header.
func (h HeaderColumn) Clone() HeaderColumn {
return h
}
// ----------------------------------------------------------------------------
// Header represents a table header.
type Header []HeaderColumn
// Clone duplicates a header.
func (h Header) Clone() Header {
header := make(Header, len(h))
for i, c := range h {
header[i] = c.Clone()
}
return header
}
// Labelize returns a new Header based on labels.
func (h Header) Labelize(cols []int, labelCol int, rr RowEvents) Header {
header := make(Header, 0, len(cols)+1)
for _, c := range cols {
header = append(header, h[c])
}
cc := rr.ExtractHeaderLabels(labelCol)
for _, c := range cc {
header = append(header, HeaderColumn{Name: c})
}
return header
}
// MapIndices returns a collection of mapped column indices based of the requested columns.
func (h Header) MapIndices(cols []string, wide bool) []int {
ii := make([]int, 0, len(cols))
cc := make(map[int]struct{}, len(cols))
for _, col := range cols {
idx := h.IndexOf(col, true)
if idx < 0 {
log.Warn().Msgf("Column %q not found on resource", col)
}
ii, cc[idx] = append(ii, idx), struct{}{}
}
if !wide {
return ii
}
for i := range h {
if _, ok := cc[i]; ok {
continue
}
ii = append(ii, i)
}
return ii
}
// Customize builds a header from custom col definitions.
func (h Header) Customize(cols []string, wide bool) Header {
if len(cols) == 0 {
return h
}
cc := make(Header, 0, len(h))
xx := make(map[int]struct{}, len(h))
for _, c := range cols {
idx := h.IndexOf(c, true)
if idx == -1 {
log.Warn().Msgf("Column %s is not available on this resource", c)
col := HeaderColumn{
Name: c,
}
cc = append(cc, col)
continue
}
xx[idx] = struct{}{}
col := h[idx].Clone()
col.Wide = false
cc = append(cc, col)
}
if !wide {
return cc
}
for i, c := range h {
if _, ok := xx[i]; ok {
continue
}
col := c.Clone()
col.Wide = true
cc = append(cc, col)
}
return cc
}
// Diff returns true if the header changed.
func (h Header) Diff(header Header) bool {
if len(h) != len(header) {
return true
}
return !reflect.DeepEqual(h, header)
}
// Columns return header as a collection of strings.
func (h Header) Columns(wide bool) []string {
if len(h) == 0 {
return nil
}
cc := make([]string, 0, len(h))
for _, c := range h {
if !wide && c.Wide {
continue
}
cc = append(cc, c.Name)
}
return cc
}
// HasAge returns true if table has an age column.
func (h Header) HasAge() bool {
return h.IndexOf(ageCol, true) != -1
}
// IsMetricsCol checks if given column index represents metrics.
func (h Header) IsMetricsCol(col int) bool {
return h[col].MX
}
// IsTimeCol checks if given column index represents a timestamp.
func (h Header) IsTimeCol(col int) bool {
if col >= len(h) {
return false
}
return h[col].Time
}
// ValidColIndex returns the valid col index or -1 if none.
func (h Header) ValidColIndex() int {
return h.IndexOf("VALID", true)
}
// IndexOf returns the col index or -1 if none.
func (h Header) IndexOf(colName string, includeWide bool) int {
for i, c := range h {
if c.Wide && !includeWide {
continue
}
if c.Name == colName {
return i
}
}
return -1
}
// Dump for debugging.
func (h Header) Dump() {
log.Debug().Msgf("HEADER")
for i, c := range h {
log.Debug().Msgf("%d %q -- %t", i, c.Name, c.Wide)
}
}