Skip to content

Commit 3610402

Browse files
authored
Use new table formatter everywhere (#3544)
1 parent c432979 commit 3610402

File tree

13 files changed

+198
-197
lines changed

13 files changed

+198
-197
lines changed

cli/cliui/table.go

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func DisplayTable(out any, sort string, filterColumns []string) (string, error)
9090
sort = strings.ToLower(strings.ReplaceAll(sort, "_", " "))
9191
h, ok := headersMap[sort]
9292
if !ok {
93-
return "", xerrors.Errorf("specified sort column %q not found in table headers, available columns are %q", sort, strings.Join(headersRaw, `", "`))
93+
return "", xerrors.Errorf(`specified sort column %q not found in table headers, available columns are "%v"`, sort, strings.Join(headersRaw, `", "`))
9494
}
9595

9696
// Autocorrect
@@ -101,7 +101,7 @@ func DisplayTable(out any, sort string, filterColumns []string) (string, error)
101101
column := strings.ToLower(strings.ReplaceAll(column, "_", " "))
102102
h, ok := headersMap[column]
103103
if !ok {
104-
return "", xerrors.Errorf("specified filter column %q not found in table headers, available columns are %q", sort, strings.Join(headersRaw, `", "`))
104+
return "", xerrors.Errorf(`specified filter column %q not found in table headers, available columns are "%v"`, sort, strings.Join(headersRaw, `", "`))
105105
}
106106

107107
// Autocorrect
@@ -158,6 +158,10 @@ func DisplayTable(out any, sort string, filterColumns []string) (string, error)
158158
if val != nil {
159159
v = val.Format(time.Stamp)
160160
}
161+
case fmt.Stringer:
162+
if val != nil {
163+
v = val.String()
164+
}
161165
}
162166

163167
rowSlice[i] = v
@@ -301,19 +305,3 @@ func valueToTableMap(val reflect.Value) (map[string]any, error) {
301305

302306
return row, nil
303307
}
304-
305-
func ValidateColumns(all, given []string) error {
306-
for _, col := range given {
307-
found := false
308-
for _, c := range all {
309-
if strings.EqualFold(strings.ReplaceAll(col, "_", " "), c) {
310-
found = true
311-
break
312-
}
313-
}
314-
if !found {
315-
return fmt.Errorf("unknown column: %s", col)
316-
}
317-
}
318-
return nil
319-
}

cli/cliui/table_test.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cliui_test
22

33
import (
4+
"fmt"
45
"log"
56
"strings"
67
"testing"
@@ -12,6 +13,16 @@ import (
1213
"github.com/coder/coder/cli/cliui"
1314
)
1415

16+
type stringWrapper struct {
17+
str string
18+
}
19+
20+
var _ fmt.Stringer = stringWrapper{}
21+
22+
func (s stringWrapper) String() string {
23+
return s.str
24+
}
25+
1526
type tableTest1 struct {
1627
Name string `table:"name"`
1728
NotIncluded string // no table tag
@@ -28,9 +39,9 @@ type tableTest1 struct {
2839
}
2940

3041
type tableTest2 struct {
31-
Name string `table:"name"`
32-
Age int `table:"age"`
33-
NotIncluded string `table:"-"`
42+
Name stringWrapper `table:"name"`
43+
Age int `table:"age"`
44+
NotIncluded string `table:"-"`
3445
}
3546

3647
type tableTest3 struct {
@@ -48,21 +59,21 @@ func Test_DisplayTable(t *testing.T) {
4859
Age: 10,
4960
Roles: []string{"a", "b", "c"},
5061
Sub1: tableTest2{
51-
Name: "foo1",
62+
Name: stringWrapper{str: "foo1"},
5263
Age: 11,
5364
},
5465
Sub2: &tableTest2{
55-
Name: "foo2",
66+
Name: stringWrapper{str: "foo2"},
5667
Age: 12,
5768
},
5869
Sub3: tableTest3{
5970
Sub: tableTest2{
60-
Name: "foo3",
71+
Name: stringWrapper{str: "foo3"},
6172
Age: 13,
6273
},
6374
},
6475
Sub4: tableTest2{
65-
Name: "foo4",
76+
Name: stringWrapper{str: "foo4"},
6677
Age: 14,
6778
},
6879
Time: someTime,
@@ -73,18 +84,18 @@ func Test_DisplayTable(t *testing.T) {
7384
Age: 20,
7485
Roles: []string{"a"},
7586
Sub1: tableTest2{
76-
Name: "bar1",
87+
Name: stringWrapper{str: "bar1"},
7788
Age: 21,
7889
},
7990
Sub2: nil,
8091
Sub3: tableTest3{
8192
Sub: tableTest2{
82-
Name: "bar3",
93+
Name: stringWrapper{str: "bar3"},
8394
Age: 23,
8495
},
8596
},
8697
Sub4: tableTest2{
87-
Name: "bar4",
98+
Name: stringWrapper{str: "bar4"},
8899
Age: 24,
89100
},
90101
Time: someTime,
@@ -95,18 +106,18 @@ func Test_DisplayTable(t *testing.T) {
95106
Age: 30,
96107
Roles: nil,
97108
Sub1: tableTest2{
98-
Name: "baz1",
109+
Name: stringWrapper{str: "baz1"},
99110
Age: 31,
100111
},
101112
Sub2: nil,
102113
Sub3: tableTest3{
103114
Sub: tableTest2{
104-
Name: "baz3",
115+
Name: stringWrapper{str: "baz3"},
105116
Age: 33,
106117
},
107118
},
108119
Sub4: tableTest2{
109-
Name: "baz4",
120+
Name: stringWrapper{str: "baz4"},
110121
Age: 34,
111122
},
112123
Time: someTime,

cli/features.go

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import (
55
"fmt"
66
"strings"
77

8-
"github.com/coder/coder/cli/cliui"
9-
"github.com/jedib0t/go-pretty/v6/table"
10-
118
"github.com/spf13/cobra"
129
"golang.org/x/xerrors"
1310

11+
"github.com/coder/coder/cli/cliui"
1412
"github.com/coder/coder/codersdk"
1513
)
1614

@@ -38,10 +36,6 @@ func featuresList() *cobra.Command {
3836
Use: "list",
3937
Aliases: []string{"ls"},
4038
RunE: func(cmd *cobra.Command, args []string) error {
41-
err := cliui.ValidateColumns(featureColumns, columns)
42-
if err != nil {
43-
return err
44-
}
4539
client, err := createClient(cmd)
4640
if err != nil {
4741
return err
@@ -54,11 +48,14 @@ func featuresList() *cobra.Command {
5448
out := ""
5549
switch outputFormat {
5650
case "table", "":
57-
out = displayFeatures(columns, entitlements.Features)
51+
out, err = displayFeatures(columns, entitlements.Features)
52+
if err != nil {
53+
return xerrors.Errorf("render table: %w", err)
54+
}
5855
case "json":
5956
outBytes, err := json.Marshal(entitlements)
6057
if err != nil {
61-
return xerrors.Errorf("marshal users to JSON: %w", err)
58+
return xerrors.Errorf("marshal features to JSON: %w", err)
6259
}
6360

6461
out = string(outBytes)
@@ -78,35 +75,28 @@ func featuresList() *cobra.Command {
7875
return cmd
7976
}
8077

78+
type featureRow struct {
79+
Name string `table:"name"`
80+
Entitlement string `table:"entitlement"`
81+
Enabled bool `table:"enabled"`
82+
Limit *int64 `table:"limit"`
83+
Actual *int64 `table:"actual"`
84+
}
85+
8186
// displayFeatures will return a table displaying all features passed in.
8287
// filterColumns must be a subset of the feature fields and will determine which
8388
// columns to display
84-
func displayFeatures(filterColumns []string, features map[string]codersdk.Feature) string {
85-
tableWriter := cliui.Table()
86-
header := table.Row{}
87-
for _, h := range featureColumns {
88-
header = append(header, h)
89-
}
90-
tableWriter.AppendHeader(header)
91-
tableWriter.SetColumnConfigs(cliui.FilterTableColumns(header, filterColumns))
92-
tableWriter.SortBy([]table.SortBy{{
93-
Name: "username",
94-
}})
89+
func displayFeatures(filterColumns []string, features map[string]codersdk.Feature) (string, error) {
90+
rows := make([]featureRow, 0, len(features))
9591
for name, feat := range features {
96-
tableWriter.AppendRow(table.Row{
97-
name,
98-
feat.Entitlement,
99-
feat.Enabled,
100-
intOrNil(feat.Limit),
101-
intOrNil(feat.Actual),
92+
rows = append(rows, featureRow{
93+
Name: name,
94+
Entitlement: string(feat.Entitlement),
95+
Enabled: feat.Enabled,
96+
Limit: feat.Limit,
97+
Actual: feat.Actual,
10298
})
10399
}
104-
return tableWriter.Render()
105-
}
106100

107-
func intOrNil(i *int64) string {
108-
if i == nil {
109-
return ""
110-
}
111-
return fmt.Sprintf("%d", *i)
101+
return cliui.DisplayTable(rows, "name", filterColumns)
112102
}

cli/list.go

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"time"
66

77
"github.com/google/uuid"
8-
"github.com/jedib0t/go-pretty/v6/table"
98
"github.com/spf13/cobra"
109

1110
"github.com/coder/coder/cli/cliui"
@@ -14,6 +13,49 @@ import (
1413
"github.com/coder/coder/codersdk"
1514
)
1615

16+
type workspaceListRow struct {
17+
Workspace string `table:"workspace"`
18+
Template string `table:"template"`
19+
Status string `table:"status"`
20+
LastBuilt string `table:"last built"`
21+
Outdated bool `table:"outdated"`
22+
StartsAt string `table:"starts at"`
23+
StopsAfter string `table:"stops after"`
24+
}
25+
26+
func workspaceListRowFromWorkspace(now time.Time, usersByID map[uuid.UUID]codersdk.User, workspace codersdk.Workspace) workspaceListRow {
27+
status := codersdk.WorkspaceDisplayStatus(workspace.LatestBuild.Job.Status, workspace.LatestBuild.Transition)
28+
29+
lastBuilt := now.UTC().Sub(workspace.LatestBuild.Job.CreatedAt).Truncate(time.Second)
30+
autostartDisplay := "-"
31+
if !ptr.NilOrEmpty(workspace.AutostartSchedule) {
32+
if sched, err := schedule.Weekly(*workspace.AutostartSchedule); err == nil {
33+
autostartDisplay = fmt.Sprintf("%s %s (%s)", sched.Time(), sched.DaysOfWeek(), sched.Location())
34+
}
35+
}
36+
37+
autostopDisplay := "-"
38+
if !ptr.NilOrZero(workspace.TTLMillis) {
39+
dur := time.Duration(*workspace.TTLMillis) * time.Millisecond
40+
autostopDisplay = durationDisplay(dur)
41+
if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.After(now) && status == "Running" {
42+
remaining := time.Until(workspace.LatestBuild.Deadline)
43+
autostopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining))
44+
}
45+
}
46+
47+
user := usersByID[workspace.OwnerID]
48+
return workspaceListRow{
49+
Workspace: user.Username + "/" + workspace.Name,
50+
Template: workspace.TemplateName,
51+
Status: status,
52+
LastBuilt: durationDisplay(lastBuilt),
53+
Outdated: workspace.Outdated,
54+
StartsAt: autostartDisplay,
55+
StopsAfter: autostopDisplay,
56+
}
57+
}
58+
1759
func list() *cobra.Command {
1860
var columns []string
1961
cmd := &cobra.Command{
@@ -32,10 +74,10 @@ func list() *cobra.Command {
3274
return err
3375
}
3476
if len(workspaces) == 0 {
35-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Prompt.String()+"No workspaces found! Create one:")
36-
_, _ = fmt.Fprintln(cmd.OutOrStdout())
37-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+cliui.Styles.Code.Render("coder create <name>"))
38-
_, _ = fmt.Fprintln(cmd.OutOrStdout())
77+
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), cliui.Styles.Prompt.String()+"No workspaces found! Create one:")
78+
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
79+
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), " "+cliui.Styles.Code.Render("coder create <name>"))
80+
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
3981
return nil
4082
}
4183
users, err := client.Users(cmd.Context(), codersdk.UsersRequest{})
@@ -47,48 +89,18 @@ func list() *cobra.Command {
4789
usersByID[user.ID] = user
4890
}
4991

50-
tableWriter := cliui.Table()
51-
header := table.Row{"workspace", "template", "status", "last built", "outdated", "starts at", "stops after"}
52-
tableWriter.AppendHeader(header)
53-
tableWriter.SortBy([]table.SortBy{{
54-
Name: "workspace",
55-
}})
56-
tableWriter.SetColumnConfigs(cliui.FilterTableColumns(header, columns))
57-
5892
now := time.Now()
59-
for _, workspace := range workspaces {
60-
status := codersdk.WorkspaceDisplayStatus(workspace.LatestBuild.Job.Status, workspace.LatestBuild.Transition)
61-
62-
lastBuilt := time.Now().UTC().Sub(workspace.LatestBuild.Job.CreatedAt).Truncate(time.Second)
63-
autostartDisplay := "-"
64-
if !ptr.NilOrEmpty(workspace.AutostartSchedule) {
65-
if sched, err := schedule.Weekly(*workspace.AutostartSchedule); err == nil {
66-
autostartDisplay = fmt.Sprintf("%s %s (%s)", sched.Time(), sched.DaysOfWeek(), sched.Location())
67-
}
68-
}
69-
70-
autostopDisplay := "-"
71-
if !ptr.NilOrZero(workspace.TTLMillis) {
72-
dur := time.Duration(*workspace.TTLMillis) * time.Millisecond
73-
autostopDisplay = durationDisplay(dur)
74-
if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.After(now) && status == "Running" {
75-
remaining := time.Until(workspace.LatestBuild.Deadline)
76-
autostopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining))
77-
}
78-
}
93+
displayWorkspaces := make([]workspaceListRow, len(workspaces))
94+
for i, workspace := range workspaces {
95+
displayWorkspaces[i] = workspaceListRowFromWorkspace(now, usersByID, workspace)
96+
}
7997

80-
user := usersByID[workspace.OwnerID]
81-
tableWriter.AppendRow(table.Row{
82-
user.Username + "/" + workspace.Name,
83-
workspace.TemplateName,
84-
status,
85-
durationDisplay(lastBuilt),
86-
workspace.Outdated,
87-
autostartDisplay,
88-
autostopDisplay,
89-
})
98+
out, err := cliui.DisplayTable(displayWorkspaces, "workspace", columns)
99+
if err != nil {
100+
return err
90101
}
91-
_, err = fmt.Fprintln(cmd.OutOrStdout(), tableWriter.Render())
102+
103+
_, err = fmt.Fprintln(cmd.OutOrStdout(), out)
92104
return err
93105
},
94106
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy