diff --git a/pkg/printer/bytes.go b/pkg/printer/bytes.go new file mode 100644 index 0000000..cae3c58 --- /dev/null +++ b/pkg/printer/bytes.go @@ -0,0 +1,31 @@ +/* +Copyright © 2026 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package printer + +import "github.com/dustin/go-humanize" + +type ByteSize struct { + size uint64 +} + +func Bytes(size int64) ByteSize { + return ByteSize{size: uint64(size)} +} + +func (b *ByteSize) String() string { + return humanize.Bytes(b.size) +} diff --git a/pkg/printer/cast.go b/pkg/printer/cast.go new file mode 100644 index 0000000..153916e --- /dev/null +++ b/pkg/printer/cast.go @@ -0,0 +1,99 @@ +/* +Copyright © 2026 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package printer + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/elastic/go-elasticsearch/v9/typedapi/types" +) + +func any2string(in any) string { + switch val := in.(type) { + case string: + return val + case bool: + return strconv.FormatBool(val) + case int64: + return strconv.FormatInt(val, 10) + case int: + return strconv.Itoa(val) + case float64: + return fmt.Sprintf("%.2f", val) + case []string: + return strings.Join(val, ",") + case ByteSize: + return val.String() + case time.Time: + return val.Format("2006-01-02 15:04:05") + case []byte: + return string(val) + case types.Percentage: + return val.(string) + case types.DateTime: + return val.(string) + case nil: + return "null" + } + + return "" +} + +func (data *Table) preprocessRows() { + if data.processed { + // only do it once + return + } + + // convert entries to strings + data.rows = make([][]string, len(data.Entries)) + for rowidx, entries := range data.Entries { + data.rows[rowidx] = make([]string, len(entries)) + + for colidx, entry := range data.Entries[rowidx] { + data.rows[rowidx][colidx] = any2string(entry) + } + } + + // determine header lenght's + for idx, head := range data.Headers { + data.lenHeaders[idx] = visibleLen(head) + } + + // determine max width per column + for _, entries := range data.rows { + currentWidth := 0 + for idx, entry := range entries { + length := visibleLen(entry) + + if data.lenHeaders[idx] < length { + if length > currentWidth+data.maxwidth { + data.lenHeaders[idx] = data.maxwidth - currentWidth + } else { + data.lenHeaders[idx] = length + } + } + + currentWidth += data.lenHeaders[idx] + } + } + + data.processed = true +} diff --git a/pkg/printer/table.go b/pkg/printer/table.go index 8a48e6d..26020a2 100644 --- a/pkg/printer/table.go +++ b/pkg/printer/table.go @@ -32,8 +32,10 @@ import ( type Table struct { Mode string // tsv, json, yaml Headers []string - Entries [][]string + Entries [][]any + rows [][]string // representation used for printing + processed bool lenHeaders []int alignInts bool maxwidth int @@ -43,7 +45,7 @@ func NewTable(conf *cfg.Config, columns, rows int) *Table { table := Table{Mode: conf.Output, maxwidth: cfg.GetTermWidth()} table.Headers = make([]string, columns) - table.Entries = make([][]string, rows) + table.Entries = make([][]any, rows) table.lenHeaders = make([]int, columns) table.alignInts = conf.AlignInts @@ -59,7 +61,7 @@ func NewTableEmpty(conf *cfg.Config) *Table { func (table *Table) WithHeaders(headers ...string) *Table { count := len(headers) - table.Entries = [][]string{} + table.Entries = [][]any{} table.lenHeaders = make([]int, count) table.Headers = make([]string, count) @@ -84,11 +86,11 @@ var ( ) // needed for json and yaml output -func (data *Table) toMap() []map[string]string { - raw := make([]map[string]string, len(data.Entries)) +func (data *Table) toMap() []map[string]any { + raw := make([]map[string]any, len(data.Entries)) for idx, entries := range data.Entries { - raw[idx] = make(map[string]string, len(data.Headers)) + raw[idx] = make(map[string]any, len(data.Headers)) for eidx, entry := range entries { raw[idx][data.Headers[eidx]] = entry } @@ -124,27 +126,8 @@ func (data *Table) PrintJSON() error { } func (data *Table) PrintTSV() error { - // determine lenght's - for idx, head := range data.Headers { - data.lenHeaders[idx] = visibleLen(head) - } - - for _, entries := range data.Entries { - currentWidth := 0 - for idx, entry := range entries { - length := visibleLen(entry) - - if data.lenHeaders[idx] < length { - if length > currentWidth+data.maxwidth { - data.lenHeaders[idx] = data.maxwidth - currentWidth - } else { - data.lenHeaders[idx] = length - } - } - - currentWidth += data.lenHeaders[idx] - } - } + // length's, convert cell types + data.preprocessRows() // output headers for idx, header := range data.Headers { @@ -161,7 +144,7 @@ func (data *Table) PrintTSV() error { } fmt.Println() - for _, entries := range data.Entries { + for _, entries := range data.rows { currentWidth := 0 for idx, entry := range entries { @@ -211,8 +194,10 @@ func (data *Table) Sort() { return } - sort.Slice(data.Entries, func(i, j int) bool { - return data.Entries[i][0] < data.Entries[j][0] + data.preprocessRows() + + sort.Slice(data.rows, func(i, j int) bool { + return data.rows[i][0] < data.rows[j][0] }) } @@ -227,7 +212,7 @@ func (data *Table) Addheaders(headers ...string) { } } -func (data *Table) AddRow(fields ...string) { +func (data *Table) AddRow(fields ...any) { data.Entries = append(data.Entries, fields) }