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)
}