diff --git a/pkg/es/api.go b/pkg/es/api.go
index 63a6346..9f11735 100644
--- a/pkg/es/api.go
+++ b/pkg/es/api.go
@@ -328,8 +328,8 @@ func ApiList(conf *cfg.Config, pattern string) error {
filter := regexp.MustCompile(pattern)
- table := printer.NewTable(conf, 4, 0)
- table.Addheaders("path", "http verb", "tag", "description")
+ table := printer.NewTable(conf).WithSize(4, 0).
+ WithHeaders("path", "http verb", "tag", "description")
for path, item := range assets.OpenAPI.Spec().Paths.Paths {
if pattern != "" {
diff --git a/pkg/es/ccr.go b/pkg/es/ccr.go
index 671f724..cc42992 100644
--- a/pkg/es/ccr.go
+++ b/pkg/es/ccr.go
@@ -111,8 +111,8 @@ func CcrRemoteInfo(conf *cfg.Config, index string) error {
mode = "leader"
}
- table := printer.NewTable(conf, 2, 5)
- table.Addheaders("ccr remote property", "value")
+ table := printer.NewTable(conf).WithSize(2, 5).
+ WithHeaders("ccr remote property", "value")
table.Entries = [][]any{
{"Remote Cluster", remote},
diff --git a/pkg/es/ccr_follower.go b/pkg/es/ccr_follower.go
index feb8f9e..44901c5 100644
--- a/pkg/es/ccr_follower.go
+++ b/pkg/es/ccr_follower.go
@@ -164,8 +164,8 @@ func CcrFollowerShow(conf *cfg.Config, index string) error {
follower := res.Indices[0].Shards[0]
- table := printer.NewTable(conf, 2, 9)
- table.Addheaders("ccr follower property", "value")
+ table := printer.NewTable(conf).WithSize(2, 9).
+ WithHeaders("ccr follower property", "value")
table.Entries = [][]any{
{"name", index},
diff --git a/pkg/es/cluster.go b/pkg/es/cluster.go
index a7af1d6..f0d2e56 100644
--- a/pkg/es/cluster.go
+++ b/pkg/es/cluster.go
@@ -58,8 +58,8 @@ func ClusterList(conf *cfg.Config) error {
wg.Wait()
- table := printer.NewTable(conf, 5, len(conf.Clusters))
- table.Addheaders("cluster", "uri", "reachable", "current", "error")
+ table := printer.NewTable(conf).WithSize(5, len(conf.Clusters)).
+ WithHeaders("cluster", "uri", "reachable", "current", "error")
idx := 0
@@ -212,8 +212,8 @@ func ClusterStatus(conf *cfg.Config) error {
}
}
- table := printer.NewTable(conf, 2, 7)
- table.Addheaders(conf.DefaultCluster.Name, "status")
+ table := printer.NewTable(conf).WithSize(2, 7).
+ WithHeaders(conf.DefaultCluster.Name, "status")
table.Entries = [][]any{
{"Cluster Name", res.health.ClusterName},
@@ -291,7 +291,7 @@ func gatherClusterStats(clusterstats *clusterstats.Response, table *printer.Tabl
{"indices", clusterstats.Indices.Count},
{"Docs", clusterstats.Indices.Docs.Count},
{"Total Size", printer.Bytes(clusterstats.Indices.Docs.TotalSizeInBytes)},
- {"Total Queries", "%d", querycount},
+ {"Total Queries", querycount},
{"Shards Primaries", clusterstats.Indices.Shards.Primaries},
{"Shards Total", clusterstats.Indices.Shards.Total},
{"Storage", fmt.Sprintf(
diff --git a/pkg/es/cluster_settings.go b/pkg/es/cluster_settings.go
index a4e85d4..c82a495 100644
--- a/pkg/es/cluster_settings.go
+++ b/pkg/es/cluster_settings.go
@@ -38,8 +38,8 @@ func ClusterSettingsList(conf *cfg.Config) error {
return fmt.Errorf("failed to get cluster settings: %w", esErrorString(err))
}
- table := printer.NewTable(conf, 2, 0)
- table.Addheaders("setting", "value")
+ table := printer.NewTable(conf).WithSize(2, 0).
+ WithHeaders("setting", "value")
entries := [][]any{}
diff --git a/pkg/es/cluster_util.go b/pkg/es/cluster_util.go
index 67fabcb..6fe7013 100644
--- a/pkg/es/cluster_util.go
+++ b/pkg/es/cluster_util.go
@@ -71,9 +71,9 @@ func checkClusterStatus(conf *cfg.Config, leader, follower string) bool {
status[cluster] = clusterHealth
}
- table := printer.NewTable(conf, 3, 6)
+ table := printer.NewTable(conf).WithSize(3, 6).
- table.Addheaders("setting", "leader:"+leader, "follower:"+follower)
+ WithHeaders("setting", "leader:"+leader, "follower:"+follower)
table.Entries = [][]any{
{"Cluster Name", status[leader].ClusterName, status[follower].ClusterName},
@@ -151,8 +151,8 @@ func findIlmErrors(conf *cfg.Config, leader, follower string) bool {
if len(failed[cluster]) > 0 {
idx := 0
- table := printer.NewTable(conf, 2, len(failed[cluster]))
- table.Addheaders("ilm errors on "+which, "errors")
+ table := printer.NewTable(conf).WithSize(2, len(failed[cluster])).
+ WithHeaders("ilm errors on "+which, "errors")
for name, count := range failed[cluster] {
table.Entries[idx] = []any{name, count}
@@ -225,8 +225,8 @@ func findIndicesOnlyOnLeader(conf *cfg.Config, indices ClusterIndices, leader, f
}
idx := 0
- table := printer.NewTable(conf, 3, len(indexOnlyOnLeader))
- table.Addheaders("index only on leader", "size", "docscount")
+ table := printer.NewTable(conf).WithSize(3, len(indexOnlyOnLeader)).
+ WithHeaders("index only on leader", "size", "docscount")
for name, index := range indexOnlyOnLeader {
name := printer.Colorize(conf, "red", name)
@@ -275,8 +275,8 @@ func findOrphanedIndices(conf *cfg.Config, indices ClusterIndices, leader, follo
}
idx := 0
- table := printer.NewTable(conf, 3, len(orphaned))
- table.Addheaders("orphaned index on follower", "size", "docscount")
+ table := printer.NewTable(conf).WithSize(3, len(orphaned)).
+ WithHeaders("orphaned index on follower", "size", "docscount")
for name, index := range orphaned {
name := printer.Colorize(conf, "red", name)
@@ -311,8 +311,8 @@ func findFailedFollowerIndices(conf *cfg.Config, indices ClusterIndices, followe
}
idx := 0
- table := printer.NewTable(conf, 3, len(red))
- table.Addheaders("red index on follower", "size", "docscount")
+ table := printer.NewTable(conf).WithSize(3, len(red)).
+ WithHeaders("red index on follower", "size", "docscount")
for name, index := range red {
name := printer.Colorize(conf, "red", name)
diff --git a/pkg/es/datastream.go b/pkg/es/datastream.go
index 42f8339..bd88f0c 100644
--- a/pkg/es/datastream.go
+++ b/pkg/es/datastream.go
@@ -62,8 +62,8 @@ func DatastreamList(conf *cfg.Config) error {
}
}
- table := printer.NewTable(conf, 7, size)
- table.Addheaders("name", "ilm policy", "hidden", "system", "replicated", "generation", "timestamp field")
+ table := printer.NewTable(conf).WithSize(7, size).
+ WithHeaders("name", "ilm policy", "hidden", "system", "replicated", "generation", "timestamp field")
for idx, datastream := range list {
name := printer.Colorize(conf, datastream.Status.String(), datastream.Name)
@@ -146,8 +146,8 @@ func DatastreamShow(conf *cfg.Config, dsname string) error {
return fmt.Errorf("failed to get data stream stats: %w", esErrorString(err))
}
- table := printer.NewTable(conf, 11, 2)
- table.Addheaders("data stream property", "value")
+ table := printer.NewTable(conf).WithSize(11, 2).
+ WithHeaders("data stream property", "value")
datastream := res.DataStreams[0]
@@ -176,8 +176,8 @@ func DatastreamShow(conf *cfg.Config, dsname string) error {
return err
}
- table = printer.NewTable(conf, 5, len(datastream.Indices))
- table.Addheaders("backing index name", "uuid", "prefer ilm", "ilm policy", "managed by")
+ table = printer.NewTable(conf).WithSize(5, len(datastream.Indices)).
+ WithHeaders("backing index name", "uuid", "prefer ilm", "ilm policy", "managed by")
for idx, index := range datastream.Indices {
policy := ""
@@ -233,8 +233,8 @@ func DatastreamRollover(conf *cfg.Config, ds string) error {
return fmt.Errorf("failed to rollover data stream: %w", esErrorString(err))
}
- table := printer.NewTable(conf, 2, 5)
- table.Addheaders("rollover response", "value")
+ table := printer.NewTable(conf).WithSize(2, 5).
+ WithHeaders("rollover response", "value")
table.Entries = [][]any{
{"acknowledged", res.Acknowledged},
{"rolled over", res.RolledOver},
diff --git a/pkg/es/ilm.go b/pkg/es/ilm.go
index b87597e..f374a66 100644
--- a/pkg/es/ilm.go
+++ b/pkg/es/ilm.go
@@ -85,8 +85,8 @@ func IlmList(conf *cfg.Config, pattern string) error {
repr.Println(res)
}
- table := printer.NewTable(conf, 5, 0)
- table.Addheaders("ilm policy", "hot", "warm", "frozen", "delete")
+ table := printer.NewTable(conf).WithSize(5, 0).
+ WithHeaders("ilm policy", "hot", "warm", "frozen", "delete")
for name, ilm := range res {
table.AddRow(name,
@@ -123,8 +123,8 @@ func IlmShow(conf *cfg.Config, policy string) error {
return IlmShowTree(conf, ilm.Policy)
}
- table := printer.NewTable(conf, 2, 5)
- table.Addheaders("ilm policy setting", "value")
+ table := printer.NewTable(conf).WithSize(2, 5).
+ WithHeaders("ilm policy setting", "value")
table.Entries = [][]any{
{"policy", policy},
@@ -142,9 +142,6 @@ func IlmShow(conf *cfg.Config, policy string) error {
func IlmShowTree(conf *cfg.Config, ilm types.IlmPolicy) error {
indent := ""
- table := printer.NewTable(conf, 4, 0)
- table.Addheaders("phase", "min age", "min size", "snapshot repo")
-
for _, phase := range IlmPhaseOrder {
if phase == "hot" {
fmt.Printf("%s%s phase:\n%s rollover after %s\n",
@@ -280,8 +277,8 @@ func IlmExplain(conf *cfg.Config, index string) error {
ilm := explain.(*types.LifecycleExplainManaged)
- table := printer.NewTable(conf, 2, 0)
- table.Addheaders("ilm status field", "value")
+ table := printer.NewTable(conf).WithSize(2, 0).
+ WithHeaders("ilm status field", "value")
info := ""
diff --git a/pkg/es/ilm_forecast.go b/pkg/es/ilm_forecast.go
index be7c19f..0b79a61 100644
--- a/pkg/es/ilm_forecast.go
+++ b/pkg/es/ilm_forecast.go
@@ -105,8 +105,8 @@ func IlmForecastList(conf *cfg.Config, filter string) error {
headers = append(headers, "ilm policy")
}
- table := printer.NewTable(conf, len(headers), 0)
- table.Addheaders(headers...)
+ table := printer.NewTable(conf).WithSize(len(headers), 0).
+ WithHeaders(headers...)
for _, phase := range phaseData {
if filter != "" && !flt.MatchString(phase.index) {
diff --git a/pkg/es/index.go b/pkg/es/index.go
index 6aca80c..456e4c7 100644
--- a/pkg/es/index.go
+++ b/pkg/es/index.go
@@ -104,8 +104,8 @@ func IndexList(conf *cfg.Config) error {
}
}
- table := printer.NewTable(conf, 3, size)
- table.Addheaders("name", "size", "docscount")
+ table := printer.NewTable(conf).WithSize(3, size).
+ WithHeaders("name", "size", "docscount")
for idx, index := range list {
name := printer.Colorize(conf, *index.Health, *index.Index)
@@ -140,8 +140,8 @@ func IndexShow(conf *cfg.Config, indexpattern string) error {
idx++
}
- table := printer.NewTable(conf, 2, 7)
- table.Addheaders("index property", "value")
+ table := printer.NewTable(conf).WithSize(2, 7).
+ WithHeaders("index property", "value")
ts, err := strconv.ParseInt(index.Settings.Index.CreationDate.(string), 10, 64)
if err != nil {
@@ -263,8 +263,8 @@ func IndexFields(conf *cfg.Config, index string) error {
return fmt.Errorf("failed to retrieve field capabilties: %w", esErrorString(err))
}
- table := printer.NewTable(conf, 5, 0)
- table.Addheaders("field", "type", "searchable", "aggretable", "metadata")
+ table := printer.NewTable(conf).WithSize(5, 0).
+ WithHeaders("field", "type", "searchable", "aggretable", "metadata")
idx := 0
@@ -336,7 +336,7 @@ func IndexDiskusage(conf *cfg.Config, index string) error {
return errors.New("no disk usage reported for index")
}
- table := printer.NewTableEmpty(conf).
+ table := printer.NewTable(conf).
WithHeaders("field", "bloom filter", "norms", "points", "term vectors", "knn vectors", "total")
for name, field := range diskusage.Fields {
diff --git a/pkg/es/index_alias.go b/pkg/es/index_alias.go
index 1b5a8cb..8864f60 100644
--- a/pkg/es/index_alias.go
+++ b/pkg/es/index_alias.go
@@ -72,8 +72,8 @@ func IndexAliasList(conf *cfg.Config) error {
}
}
- table := printer.NewTable(conf, 2, len(aliaslist))
- table.Addheaders("index", "alias")
+ table := printer.NewTable(conf).WithSize(2, len(aliaslist)).
+ WithHeaders("index", "alias")
idx := 0
for index, aliases := range aliaslist {
@@ -113,8 +113,8 @@ func IndexAliasRollover(conf *cfg.Config, alias string) error {
return fmt.Errorf("failed to rollover index alias: %w", esErrorString(err))
}
- table := printer.NewTable(conf, 2, 5)
- table.Addheaders("rollover response", "value")
+ table := printer.NewTable(conf).WithSize(2, 5).
+ WithHeaders("rollover response", "value")
table.Entries = [][]any{
{"acknowledged", res.Acknowledged},
{"rolled over", res.RolledOver},
diff --git a/pkg/es/index_copy.go b/pkg/es/index_copy.go
index 63c5db6..36b396f 100644
--- a/pkg/es/index_copy.go
+++ b/pkg/es/index_copy.go
@@ -62,7 +62,7 @@ func IndexCopy(conf *cfg.Config) error {
return fmt.Errorf("failed to copy indices: %w", esErrorString(err))
}
- table := printer.NewTable(conf, 2, 0).
+ table := printer.NewTable(conf).WithSize(2, 0).
WithHeaders("Reindex metrtic", "value")
table.Entries = [][]any{
diff --git a/pkg/es/index_template.go b/pkg/es/index_template.go
index 49f6252..3e5142e 100644
--- a/pkg/es/index_template.go
+++ b/pkg/es/index_template.go
@@ -59,8 +59,8 @@ func IndexTemplateList(conf *cfg.Config, filter string) error {
slog.Debug("res", "index templates", res)
- table := printer.NewTable(conf, 5, 0)
- table.Addheaders("name", "description", "index patterns", "priority")
+ table := printer.NewTable(conf).WithSize(5, 0).
+ WithHeaders("name", "description", "index patterns", "priority")
tplList := filterIndexTemplates(conf, filter, res.IndexTemplates)
@@ -120,8 +120,8 @@ func IndexTemplateShow(conf *cfg.Config, tplname string) error {
tpl := res.IndexTemplates[0]
- table := printer.NewTable(conf, 2, 6)
- table.Addheaders("index template property", "value")
+ table := printer.NewTable(conf).WithSize(2, 6).
+ WithHeaders("index template property", "value")
desc, err := json.Marshal(tpl.IndexTemplate.Meta_["description"])
if err != nil {
@@ -148,8 +148,8 @@ func IndexTemplateShow(conf *cfg.Config, tplname string) error {
return err
}
- table = printer.NewTable(conf, 2, 0)
- table.Addheaders("index setting property", "value")
+ table = printer.NewTable(conf).WithSize(2, 0).
+ WithHeaders("index setting property", "value")
err = getIndexTemplateSettings(conf, tplname, table)
if err != nil {
@@ -162,8 +162,8 @@ func IndexTemplateShow(conf *cfg.Config, tplname string) error {
return err
}
- table = printer.NewTable(conf, 2, 0)
- table.Addheaders("index field mapping", "type")
+ table = printer.NewTable(conf).WithSize(2, 0).
+ WithHeaders("index field mapping", "type")
if tpl.IndexTemplate.Template.Mappings != nil {
for name, field := range tpl.IndexTemplate.Template.Mappings.Properties {
@@ -441,8 +441,8 @@ func rolloverAliasIndexTemplate(conf *cfg.Config, name string) error {
return nil
}
- table := printer.NewTable(conf, 4, 0)
- table.Addheaders("rollover alias", "status", "ack", "new index")
+ table := printer.NewTable(conf).WithSize(4, 0).
+ WithHeaders("rollover alias", "status", "ack", "new index")
// apply rollover to all matching aliases, if any
for alias := range aliases {
diff --git a/pkg/es/license.go b/pkg/es/license.go
index 6f5b6c4..15a9c3e 100644
--- a/pkg/es/license.go
+++ b/pkg/es/license.go
@@ -35,7 +35,7 @@ func LicenseShow(conf *cfg.Config) error {
slog.Debug("license show", "license", res)
- table := printer.NewTableEmpty(conf).WithHeaders("license setting", "value")
+ table := printer.NewTable(conf).WithHeaders("license setting", "value")
lic := res.License
diff --git a/pkg/es/node.go b/pkg/es/node.go
index a0c6e8c..74c35ee 100644
--- a/pkg/es/node.go
+++ b/pkg/es/node.go
@@ -37,7 +37,7 @@ func NodeList(conf *cfg.Config) error {
slog.Debug("ES result", "nodes", nodes)
- table := printer.NewTableEmpty(conf).WithHeaders(
+ table := printer.NewTable(conf).WithHeaders(
"name", "ip", "load1m", "load5m", "load15m", "ram %", "heap %")
for _, node := range nodes {
@@ -104,8 +104,8 @@ func NodeShow(conf *cfg.Config, nodename string) error {
for id, info := range res.Nodes {
stat := stats.Nodes[id]
- table := printer.NewTable(conf, 2, 0)
- table.Addheaders(nodename+" property", "value")
+ table := printer.NewTable(conf).WithSize(2, 0).
+ WithHeaders(nodename+" property", "value")
roles := make([]string, len(info.Roles))
for idx, role := range info.Roles {
@@ -197,8 +197,8 @@ func NodeClients(conf *cfg.Config, nodename string) error {
slog.Debug("ES result", "stat", stats)
- table := printer.NewTable(conf, 5, 0)
- table.Addheaders("agent", "id", "when", "from host", "url")
+ table := printer.NewTable(conf).WithSize(5, 0).
+ WithHeaders("agent", "id", "when", "from host", "url")
for _, stat := range stats.Nodes {
for _, client := range stat.Http.Clients {
@@ -250,7 +250,7 @@ func NodeUsage(conf *cfg.Config, nodeid string) error {
slog.Debug("ES result", "usage", stats)
- table := printer.NewTableEmpty(conf).WithHeaders(
+ table := printer.NewTable(conf).WithHeaders(
"node",
"bulk",
"doc get",
diff --git a/pkg/es/role.go b/pkg/es/role.go
index 3665940..89cc5bc 100644
--- a/pkg/es/role.go
+++ b/pkg/es/role.go
@@ -47,8 +47,8 @@ func RoleList(conf *cfg.Config) error {
slog.Debug("ES result", "roles", res)
- table := printer.NewTable(conf, 3, len(res))
- table.Addheaders("role", "index roles", "cluster roles")
+ table := printer.NewTable(conf).WithSize(3, len(res)).
+ WithHeaders("role", "index roles", "cluster roles")
idx := 0
for name, role := range res {
@@ -126,8 +126,8 @@ func roleRemoteClusters(conf *cfg.Config, role types.Role) error {
return nil
}
- table := printer.NewTable(conf, 2, len(role.RemoteCluster))
- table.Addheaders("remote cluster", "privilege")
+ table := printer.NewTable(conf).WithSize(2, len(role.RemoteCluster)).
+ WithHeaders("remote cluster", "privilege")
idx := 0
@@ -152,8 +152,8 @@ func roleClusters(conf *cfg.Config, role types.Role) error {
return nil
}
- table := printer.NewTable(conf, 1, len(role.Cluster))
- table.Addheaders("cluster rights")
+ table := printer.NewTable(conf).WithSize(1, len(role.Cluster)).
+ WithHeaders("cluster rights")
idx := 0
for _, cluster := range role.Cluster {
@@ -171,8 +171,8 @@ func roleRemoteIndices(conf *cfg.Config, role types.Role) error {
return nil
}
- table := printer.NewTable(conf, 3, len(role.RemoteIndices))
- table.Addheaders("remote index names", "index permissions", "allow restricted")
+ table := printer.NewTable(conf).WithSize(3, len(role.RemoteIndices)).
+ WithHeaders("remote index names", "index permissions", "allow restricted")
idx := 0
@@ -197,8 +197,8 @@ func roleIndices(conf *cfg.Config, role types.Role) error {
return nil
}
- table := printer.NewTable(conf, 3, len(role.Indices))
- table.Addheaders("index names", "index permissions", "allow restricted")
+ table := printer.NewTable(conf).WithSize(3, len(role.Indices)).
+ WithHeaders("index names", "index permissions", "allow restricted")
idx := 0
@@ -223,8 +223,8 @@ func roleApplications(conf *cfg.Config, role types.Role) error {
return nil
}
- table := printer.NewTable(conf, 3, len(role.Applications))
- table.Addheaders("application", "privileges", "resources")
+ table := printer.NewTable(conf).WithSize(3, len(role.Applications)).
+ WithHeaders("application", "privileges", "resources")
idx := 0
for _, priv := range role.Applications {
diff --git a/pkg/es/role_diff.go b/pkg/es/role_diff.go
index 1f16588..77edd75 100644
--- a/pkg/es/role_diff.go
+++ b/pkg/es/role_diff.go
@@ -216,8 +216,8 @@ func RoleDiff(conf *cfg.Config, csvfile, role string) error {
rows := diffRoles(conf, records, res)
- table := printer.NewTable(conf, 3, len(rows))
- table.Addheaders("role", "is deployed", "is defined")
+ table := printer.NewTable(conf).WithSize(3, len(rows)).
+ WithHeaders("role", "is deployed", "is defined")
for idx, row := range rows {
deployed := printer.Colorize(conf, "green", "deployed")
diff --git a/pkg/es/searchql.go b/pkg/es/searchql.go
index d667558..552ce5a 100644
--- a/pkg/es/searchql.go
+++ b/pkg/es/searchql.go
@@ -64,14 +64,13 @@ func SearchQL(conf *cfg.Config, querystring string) error {
return nil
}
- table := printer.NewTable(conf, len(qlResult.Columns), len(qlResult.Values))
-
headers := make([]string, len(qlResult.Columns))
for idx, col := range qlResult.Columns {
headers[idx] = col.Name
}
- table.Addheaders(headers...)
+ table := printer.NewTable(conf).WithSize(len(qlResult.Columns), len(qlResult.Values)).
+ WithHeaders(headers...)
table.Entries = qlResult.Values
diff --git a/pkg/es/shard.go b/pkg/es/shard.go
index 8052add..4087333 100644
--- a/pkg/es/shard.go
+++ b/pkg/es/shard.go
@@ -100,8 +100,8 @@ func printShards(conf *cfg.Config, shardlist shards.Response) error {
headers = append(headers, "node", "ip")
}
- table := printer.NewTable(conf, len(headers), len(shardlist))
- table.Addheaders(headers...)
+ table := printer.NewTable(conf).WithSize(len(headers), len(shardlist)).
+ WithHeaders(headers...)
for idx, shard := range shardlist {
name := colorzizeShard(conf, *shard.State, *shard.Index)
@@ -163,8 +163,8 @@ func ShardAllocation(conf *cfg.Config, index string) error {
slog.Debug("ES result", "explain", res)
- table := printer.NewTable(conf, 2, 10)
- table.Addheaders("shard allocation setting", "value")
+ table := printer.NewTable(conf).WithSize(2, 10).
+ WithHeaders("shard allocation setting", "value")
table.Entries = [][]any{
{"Index", index},
diff --git a/pkg/es/snapshot.go b/pkg/es/snapshot.go
index 4f13e49..3dab5bb 100644
--- a/pkg/es/snapshot.go
+++ b/pkg/es/snapshot.go
@@ -84,8 +84,8 @@ func SnapshotList(conf *cfg.Config) error {
}
}
- table := printer.NewTable(conf, 5, len(snapshots))
- table.Addheaders("name", "index", "start", "orphaned", "status")
+ table := printer.NewTable(conf).WithSize(5, len(snapshots)).
+ WithHeaders("name", "index", "start", "orphaned", "status")
for idx, snap := range snapshots {
table.Entries[idx] = []any{
@@ -118,8 +118,8 @@ func SnapshotShow(conf *cfg.Config, snapshot string) error {
return errors.New("no snapshot retrieved")
}
- table := printer.NewTable(conf, 2, 17)
- table.Addheaders("snapshot property", "value")
+ table := printer.NewTable(conf).WithSize(2, 17).
+ WithHeaders("snapshot property", "value")
snap := res.Snapshots[0]
diff --git a/pkg/es/task.go b/pkg/es/task.go
index 83297c7..f92c9e6 100644
--- a/pkg/es/task.go
+++ b/pkg/es/task.go
@@ -36,8 +36,8 @@ func TaskList(conf *cfg.Config) error {
slog.Debug("res", "tasks", res)
- table := printer.NewTable(conf, 6, len(res))
- table.Addheaders("task id", "action", "start time", "run time", "node", "type")
+ table := printer.NewTable(conf).WithSize(6, len(res)).
+ WithHeaders("task id", "action", "start time", "run time", "node", "type")
for idx, task := range res {
var ts time.Time
diff --git a/pkg/printer/builder.go b/pkg/printer/builder.go
index 10197d4..0ea2725 100644
--- a/pkg/printer/builder.go
+++ b/pkg/printer/builder.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,15 +16,15 @@ 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 "strings"
-// a wrapper around strings.Builder for convenience
+// Builder is a wrapper around strings.Builder for convenience
type Builder struct {
strings.Builder
}
func (builder *Builder) WriteStringLine(in string) {
- builder.WriteString(in + "\n")
+ builder.WriteString(in)
+ builder.WriteString("\n")
}
diff --git a/pkg/printer/bytes.go b/pkg/printer/bytes.go
index ecbeaa7..c15be53 100644
--- a/pkg/printer/bytes.go
+++ b/pkg/printer/bytes.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,10 +16,11 @@ 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"
+// ByteSize converts uint64 bytes into human readable size
+// e.g. 1500 => 1.5kb
type ByteSize struct {
size uint64
}
diff --git a/pkg/printer/cast.go b/pkg/printer/cast.go
index 3f7c077..339d14d 100644
--- a/pkg/printer/cast.go
+++ b/pkg/printer/cast.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,7 +16,6 @@ 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"
@@ -25,9 +26,11 @@ import (
"github.com/elastic/go-elasticsearch/v9/typedapi/types"
)
-func any2string(in any) string {
+// stringer returns the string representation of different types of
+// values
+func stringer(cell any) string {
//nolint:gocritic
- switch val := in.(type) {
+ switch val := cell.(type) {
case string:
return val
case bool:
@@ -54,52 +57,27 @@ func any2string(in any) string {
return string(val)
case nil:
return "null"
- case types.Percentage, types.DateTime:
- return val.(string)
- }
+ case *types.Float64:
+ // ignore err here, because types.Float64.MarshalJSON() never returns one
+ f, _ := val.MarshalJSON()
- return ""
+ return string(f)
+ default:
+ // Caution: this may cause a panic if the [unknown] type does
+ // not implement fmt.Stringer. In this case add another case
+ // to the type switch for it above.
+ return cell.(fmt.Stringer).String()
+ }
}
-func (data *Table) preprocessRows() {
- if data.processed {
- // only do it once
- return
+// visibleLen returns the length of a string but only visible chars,
+// w/o ansi color escapes
+func visibleLen(word string) int {
+ if !strings.Contains(word, "\x1b") {
+ // no ansi escape in there, use faster method
+ return len(word)
}
- // 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
+ // contains escapes, need to clean up before counting
+ return len(ansiCtrlSeq.ReplaceAllLiteralString(word, ""))
}
diff --git a/pkg/printer/color.go b/pkg/printer/color.go
index 573d6cf..1fdac91 100644
--- a/pkg/printer/color.go
+++ b/pkg/printer/color.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -15,8 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package printer
-
import (
"os"
diff --git a/pkg/printer/doc.go b/pkg/printer/doc.go
index b970dd9..a1ebc1d 100644
--- a/pkg/printer/doc.go
+++ b/pkg/printer/doc.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,7 +16,6 @@ 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"
diff --git a/pkg/printer/metrics.go b/pkg/printer/metrics.go
index caf5ed5..b3539e2 100644
--- a/pkg/printer/metrics.go
+++ b/pkg/printer/metrics.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,7 +16,6 @@ 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"
diff --git a/pkg/printer/pager.go b/pkg/printer/pager.go
index 5cfb023..82e226e 100644
--- a/pkg/printer/pager.go
+++ b/pkg/printer/pager.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,7 +16,6 @@ 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
// pager setup using bubbletea
// file shamlelessly copied from:
diff --git a/pkg/printer/table.go b/pkg/printer/table.go
index eb43416..4a40bf0 100644
--- a/pkg/printer/table.go
+++ b/pkg/printer/table.go
@@ -1,3 +1,5 @@
+package printer
+
/*
Copyright © 2026 Thomas von Dein
@@ -14,7 +16,6 @@ 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 (
"encoding/json"
@@ -28,13 +29,20 @@ import (
"gopkg.in/yaml.v3"
)
-type Table struct {
- Mode string // tsv, json, yaml
- Headers []string
- RawHeaders []string
- Entries [][]any
+var (
+ // ansiCtrlSeq is being used to remove ANSI control sequences so
+ // that we can properly determine string lenght's
+ ansiCtrlSeq = regexp.MustCompile(`\033.[0-9;]+m`)
+)
- rows [][]string // representation used for printing
+// Table stores tabular data for printing
+type Table struct {
+ Mode string // tsv, json, yaml
+ Headers []string // colored headers
+ RawHeaders []string // plain string headers
+ Entries [][]any // rows of cells as fed in by pkg/es
+
+ rows [][]string // representation used for printing, cells are stringified
processed bool
lenHeaders []int
alignInts bool
@@ -42,73 +50,65 @@ type Table struct {
debugGoRoutines bool
}
-func NewTable(conf *cfg.Config, columns, rows int) *Table {
- table := new(Table{
+// NewTable returns a new empty table object with unaligned storage
+func NewTable(conf *cfg.Config) *Table {
+ return new(Table{
Mode: conf.Output,
maxwidth: cfg.GetTermWidth(),
debugGoRoutines: conf.DebugGoRoutines,
+ Headers: []string{},
+ RawHeaders: []string{},
+ lenHeaders: []int{},
+ alignInts: conf.AlignInts,
})
-
- table.Headers = make([]string, columns)
- table.RawHeaders = make([]string, columns)
- table.Entries = make([][]any, rows)
- table.lenHeaders = make([]int, columns)
- table.alignInts = conf.AlignInts
-
- return table
}
-func NewTableEmpty(conf *cfg.Config) *Table {
- table := new(Table{
- Mode: conf.Output,
- maxwidth: cfg.GetTermWidth(),
- debugGoRoutines: conf.DebugGoRoutines,
- })
- table.alignInts = conf.AlignInts
-
- return table
-}
-
-func (table *Table) WithHeaders(headers ...string) *Table {
- count := len(headers)
-
- table.Entries = [][]any{}
- table.lenHeaders = make([]int, count)
- table.Headers = make([]string, count)
- table.RawHeaders = make([]string, count)
-
- table.Addheaders(headers...)
-
- return table
-}
-
-func (table *Table) Print() error {
- var err error
-
- switch table.Mode {
- case "json":
- err = table.PrintJSON()
- case "yaml":
- err = table.PrintYAML()
- case "csv":
- err = table.PrintCSV()
- default:
- err = table.PrintTSV()
+// WithSize configures the dimensions of the table, allocs aligned storage
+func (t *Table) WithSize(columns, rows int) *Table {
+ if columns > 0 {
+ t.Headers = make([]string, columns)
+ t.RawHeaders = make([]string, columns)
+ t.lenHeaders = make([]int, columns)
}
- if table.debugGoRoutines {
+ if rows > 0 {
+ t.Entries = make([][]any, rows)
+ }
+
+ return t
+}
+
+// WithHeaders sets table headers
+func (t *Table) WithHeaders(headers ...string) *Table {
+ count := len(headers)
+
+ return t.WithSize(count, 0).formatHeaders(headers...)
+}
+
+// Print outputs the tabular data according to Table.Mode
+func (t *Table) Print() error {
+ var err error
+
+ switch t.Mode {
+ case "json":
+ err = t.PrintJSON()
+ case "yaml":
+ err = t.PrintYAML()
+ case "csv":
+ err = t.PrintCSV()
+ default:
+ err = t.PrintTSV()
+ }
+
+ if t.debugGoRoutines {
printGoRoutineMetrics()
}
return err
}
-var (
- ansiCtrlSeq = regexp.MustCompile(`\033.[0-9;]+m`)
-)
-
-func (table *Table) PrintYAML() error {
- raw := table.toMap()
+func (t *Table) PrintYAML() error {
+ raw := t.toMap()
body, err := yaml.Marshal(raw)
if err != nil {
@@ -120,8 +120,8 @@ func (table *Table) PrintYAML() error {
return nil
}
-func (table *Table) PrintJSON() error {
- raw := table.toMap()
+func (t *Table) PrintJSON() error {
+ raw := t.toMap()
body, err := json.MarshalIndent(raw, "", " ")
if err != nil {
@@ -133,56 +133,101 @@ func (table *Table) PrintJSON() error {
return nil
}
-func (table *Table) PrintTSV() error {
- // length's, convert cell types
- table.preprocessRows()
+func (t *Table) PrintCSV() error {
+ t.preprocessRows()
- // output headers
- for idx, header := range table.Headers {
- if idx+1 != len(table.Headers) {
- fmt.Print(header, strings.Repeat(" ", table.lenHeaders[idx]-visibleLen(header)))
- } else {
- // no padding for last header
- fmt.Print(header)
+ fmt.Println(strings.Join(t.RawHeaders, ","))
+
+ for _, entries := range t.rows {
+ row := make([]string, len(entries))
+
+ for idx, entry := range entries {
+ if strings.Contains(entry, " ") || strings.Contains(entry, ",") {
+ row[idx] = `"` + entry + `"`
+ } else {
+ row[idx] = entry
+ }
}
- if idx < len(table.Headers)-1 {
- fmt.Print(" ")
- }
+ fmt.Println(strings.Join(row, ","))
}
- fmt.Println()
+ return nil
+}
- for _, entries := range table.rows {
+// Sort tabular data by first column
+func (t *Table) Sort() {
+ // sanity checks
+ if len(t.Entries) == 0 {
+ return
+ }
+
+ t.preprocessRows()
+
+ sort.Slice(t.rows, func(i, j int) bool {
+ return t.rows[i][0] < t.rows[j][0]
+ })
+}
+
+func (t *Table) AddRow(fields ...any) {
+ t.Entries = append(t.Entries, fields)
+}
+
+func (t *Table) AddRowLate(fields ...any) {
+ t.AddRow(fields)
+
+ if !t.processed {
+ return
+ }
+
+ row := make([]string, len(fields))
+
+ for idx, field := range fields {
+ row[idx] = stringer(field)
+ }
+
+ t.rows = append(t.rows, row)
+}
+
+// PrintTSV is the default printer, it outputs in tab-separated-value format
+func (t *Table) PrintTSV() error {
+ t.preprocessRows()
+ t.printTsvHeaders()
+
+ return t.printTsvRows()
+}
+
+func (t *Table) printTsvRows() error {
+ for _, entries := range t.rows {
currentWidth := 0
columns := len(entries)
for idx, entry := range entries {
length := visibleLen(entry)
- if length+currentWidth > table.maxwidth &&
- table.maxwidth-currentWidth > 1 &&
+ if length+currentWidth > t.maxwidth &&
+ t.maxwidth-currentWidth > 1 &&
idx == columns-1 {
// text is too wide to be put into one line, and
// it's the last cell, so wrap it
- entry = wrap(table.maxwidth-currentWidth, currentWidth+2, entry)
+ entry = wrap(t.maxwidth-currentWidth, currentWidth+2, entry)
}
- currentWidth += table.lenHeaders[idx]
+ currentWidth += t.lenHeaders[idx]
switch {
- case isInt(entry) && table.alignInts:
+ case isInt(entry) && t.alignInts:
// align right
- fmt.Print(strings.Repeat(" ", table.lenHeaders[idx]-length), entry)
- case length < table.lenHeaders[idx] && idx+1 != len(entries):
+ fmt.Print(strings.Repeat(" ", t.lenHeaders[idx]-length), entry)
+ case length < t.lenHeaders[idx] && idx+1 != len(entries):
// pad right, if required
- fmt.Print(entry, strings.Repeat(" ", table.lenHeaders[idx]-length))
+ fmt.Print(entry, strings.Repeat(" ", t.lenHeaders[idx]-length))
default:
// no padding for last entry
fmt.Print(entry)
}
- if idx < len(table.Headers)-1 {
+ if idx < len(t.Headers)-1 {
fmt.Print(" ")
}
}
@@ -193,6 +238,40 @@ func (table *Table) PrintTSV() error {
return nil
}
+// printTsvHeaders outputs TSV headers
+func (t *Table) printTsvHeaders() {
+ for idx, header := range t.Headers {
+ if idx+1 != len(t.Headers) {
+ fmt.Print(header, strings.Repeat(" ", t.lenHeaders[idx]-visibleLen(header)))
+ } else {
+ // no padding for last header
+ fmt.Print(header)
+ }
+
+ if idx < len(t.Headers)-1 {
+ fmt.Print(" ")
+ }
+ }
+
+ fmt.Println()
+}
+
+// formatHeaders formats header fields according to output mode
+func (t *Table) formatHeaders(headers ...string) *Table {
+ for idx, header := range headers {
+ switch t.Mode {
+ case "json", "yaml":
+ t.Headers[idx] = strings.ReplaceAll(strings.ToLower(header), " ", "_")
+ default:
+ t.Headers[idx] = Bold(strings.ReplaceAll(strings.ToUpper(header), " ", "-"))
+ }
+
+ t.RawHeaders[idx] = header
+ }
+
+ return t
+}
+
// Wrap a text into multiple lines, first line is not indented, all
// further lines will be indented. Used within Print() to print large
// cell text.
@@ -235,91 +314,61 @@ func wrap(width, indent int, text string) string {
return wrapped
}
-func (table *Table) PrintCSV() error {
- table.preprocessRows()
-
- fmt.Println(strings.Join(table.RawHeaders, ","))
-
- for _, entries := range table.rows {
- row := make([]string, len(entries))
-
- for idx, entry := range entries {
- if strings.Contains(entry, " ") || strings.Contains(entry, ",") {
- row[idx] = `"` + entry + `"`
- } else {
- row[idx] = entry
- }
- }
-
- fmt.Println(strings.Join(row, ","))
- }
-
- return nil
-}
-
-func (table *Table) Sort() {
- // sanity checks
- if len(table.Entries) == 0 {
- return
- }
-
- table.preprocessRows()
-
- sort.Slice(table.rows, func(i, j int) bool {
- return table.rows[i][0] < table.rows[j][0]
- })
-}
-
-func (table *Table) Addheaders(headers ...string) {
- for idx, header := range headers {
- switch table.Mode {
- case "json", "yaml":
- table.Headers[idx] = strings.ReplaceAll(strings.ToLower(header), " ", "_")
- default:
- table.Headers[idx] = Bold(strings.ReplaceAll(strings.ToUpper(header), " ", "-"))
- }
-
- table.RawHeaders[idx] = header
- }
-}
-
-func (table *Table) AddRow(fields ...any) {
- table.Entries = append(table.Entries, fields)
-}
-
-func (table *Table) AddRowLate(fields ...any) {
- table.AddRow(fields)
-
- if !table.processed {
- return
- }
-
- row := make([]string, len(fields))
-
- for idx, field := range fields {
- row[idx] = any2string(field)
- }
-
- table.rows = append(table.rows, row)
-}
-
// needed for json and yaml output
-func (table *Table) toMap() []map[string]any {
- raw := make([]map[string]any, len(table.Entries))
+func (t *Table) toMap() []map[string]any {
+ raw := make([]map[string]any, len(t.Entries))
- for idx, entries := range table.Entries {
- raw[idx] = make(map[string]any, len(table.Headers))
+ for idx, entries := range t.Entries {
+ raw[idx] = make(map[string]any, len(t.Headers))
for eidx, entry := range entries {
- raw[idx][table.Headers[eidx]] = entry
+ raw[idx][t.Headers[eidx]] = entry
}
}
return raw
}
-// return the length of a string but only visible chars, w/o ansi color escapes
-func visibleLen(word string) int {
- return len(ansiCtrlSeq.ReplaceAllLiteralString(word, ""))
+func (t *Table) preprocessRows() {
+ if t.processed {
+ // only do it once
+ return
+ }
+
+ // convert entries to strings
+ t.rows = make([][]string, len(t.Entries))
+ for rowidx, entries := range t.Entries {
+ t.rows[rowidx] = make([]string, len(entries))
+
+ for colidx, entry := range t.Entries[rowidx] {
+ t.rows[rowidx][colidx] = stringer(entry)
+ }
+ }
+
+ // determine header lenght's
+ for idx, head := range t.Headers {
+ t.lenHeaders[idx] = visibleLen(head)
+ }
+
+ // determine max width per column
+ for _, entries := range t.rows {
+ currentWidth := 0
+
+ for idx, entry := range entries {
+ length := visibleLen(entry)
+
+ if t.lenHeaders[idx] < length {
+ if length > currentWidth+t.maxwidth {
+ t.lenHeaders[idx] = t.maxwidth - currentWidth
+ } else {
+ t.lenHeaders[idx] = length
+ }
+ }
+
+ currentWidth += t.lenHeaders[idx]
+ }
+ }
+
+ t.processed = true
}
func isInt(num string) bool {
diff --git a/t/Makefile b/t/Makefile
index 7f90f67..040c959 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -1,4 +1,4 @@
-.PHONY: test clean cluster docs search up down delete
+.PHONY: test clean cluster docs search up down delete runenv
# docker stuff
up:
@@ -20,6 +20,8 @@ delete:
clean-docker: down delete
+runenv: up waitup cluster docs wait
+
# mosscap esctl stuff
test: up waitup cluster docs wait search