Skip to content

Commit c750695

Browse files
authored
feat(cli/cliui): output empty string for empty table (#20967)
This changes makes it so that we output the empty string for Format when there is no data. It turns out there are many places in the code where we have such handling, but in a way that would break the JSON formatter (since we'd output nothing on stdout or text rather than `[]`/`null`).
1 parent 3c05cb6 commit c750695

23 files changed

+116
-49
lines changed

cli/cliui/output.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ var _ OutputFormat = &tableFormat{}
106106
//
107107
// defaultColumns is optional and specifies the default columns to display. If
108108
// not specified, all columns are displayed by default.
109+
//
110+
// If the data is empty, an empty string is returned. Callers should check for
111+
// this and provide an appropriate message to the user.
109112
func TableFormat(out any, defaultColumns []string) OutputFormat {
110113
v := reflect.Indirect(reflect.ValueOf(out))
111114
if v.Kind() != reflect.Slice {

cli/cliui/table.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ func DisplayTable(out any, sort string, filterColumns []string) (string, error)
180180
func renderTable(out any, sort string, headers table.Row, filterColumns []string) (string, error) {
181181
v := reflect.Indirect(reflect.ValueOf(out))
182182

183+
// Return empty string for empty data. Callers should check for this
184+
// and provide an appropriate message to the user.
185+
if v.Kind() == reflect.Slice && v.Len() == 0 {
186+
return "", nil
187+
}
188+
183189
headers = filterHeaders(headers, filterColumns)
184190
columnConfigs := createColumnConfigs(headers, filterColumns)
185191

cli/cliui/table_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,15 @@ alice 1
472472
require.NoError(t, err)
473473
compareTables(t, expected, out)
474474
})
475+
476+
t.Run("Empty", func(t *testing.T) {
477+
t.Parallel()
478+
479+
var in []tableTest4
480+
out, err := cliui.DisplayTable(in, "", nil)
481+
require.NoError(t, err)
482+
require.Empty(t, out)
483+
})
475484
}
476485

477486
// compareTables normalizes the incoming table lines

cli/list.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,19 @@ func (r *RootCmd) list() *serpent.Command {
139139
return err
140140
}
141141

142-
if len(res) == 0 && formatter.FormatID() != cliui.JSONFormat().ID() {
142+
out, err := formatter.Format(inv.Context(), res)
143+
if err != nil {
144+
return err
145+
}
146+
147+
if out == "" {
143148
pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Prompt, "No workspaces found! Create one:\n")
144149
_, _ = fmt.Fprintln(inv.Stderr)
145150
_, _ = fmt.Fprintln(inv.Stderr, " "+pretty.Sprint(cliui.DefaultStyles.Code, "coder create <name>"))
146151
_, _ = fmt.Fprintln(inv.Stderr)
147152
return nil
148153
}
149154

150-
out, err := formatter.Format(inv.Context(), res)
151-
if err != nil {
152-
return err
153-
}
154-
155155
_, err = fmt.Fprintln(inv.Stdout, out)
156156
return err
157157
},

cli/organizationmembers.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ func (r *RootCmd) listOrganizationMembers(orgContext *OrganizationContext) *serp
170170
return err
171171
}
172172

173+
if out == "" {
174+
cliui.Infof(inv.Stderr, "No organization members found.")
175+
return nil
176+
}
177+
173178
_, err = fmt.Fprintln(inv.Stdout, out)
174179
return err
175180
},

cli/organizationroles.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ func (r *RootCmd) showOrganizationRoles(orgContext *OrganizationContext) *serpen
9292
return err
9393
}
9494

95+
if out == "" {
96+
cliui.Infof(inv.Stderr, "No organization roles found.")
97+
return nil
98+
}
99+
95100
_, err = fmt.Fprintln(inv.Stdout, out)
96101
return err
97102
},

cli/provisionerjobs.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
110110
return xerrors.Errorf("display provisioner daemons: %w", err)
111111
}
112112

113+
if out == "" {
114+
cliui.Infof(inv.Stderr, "No provisioner jobs found.")
115+
return nil
116+
}
117+
113118
_, _ = fmt.Fprintln(inv.Stdout, out)
114119

115120
return nil

cli/provisioners.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,6 @@ func (r *RootCmd) provisionerList() *serpent.Command {
7474
return xerrors.Errorf("list provisioner daemons: %w", err)
7575
}
7676

77-
if len(daemons) == 0 {
78-
_, _ = fmt.Fprintln(inv.Stdout, "No provisioner daemons found")
79-
return nil
80-
}
81-
8277
var rows []provisionerDaemonRow
8378
for _, daemon := range daemons {
8479
rows = append(rows, provisionerDaemonRow{
@@ -92,6 +87,11 @@ func (r *RootCmd) provisionerList() *serpent.Command {
9287
return xerrors.Errorf("display provisioner daemons: %w", err)
9388
}
9489

90+
if out == "" {
91+
cliui.Infof(inv.Stderr, "No provisioner daemons found.")
92+
return nil
93+
}
94+
9595
_, _ = fmt.Fprintln(inv.Stdout, out)
9696

9797
return nil

cli/schedule.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ func (r *RootCmd) scheduleShow() *serpent.Command {
129129
return err
130130
}
131131

132+
if out == "" {
133+
cliui.Infof(inv.Stderr, "No schedules found.")
134+
return nil
135+
}
136+
132137
_, err = fmt.Fprintln(inv.Stdout, out)
133138
return err
134139
},

cli/task_list.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,6 @@ func (r *RootCmd) taskList() *serpent.Command {
157157
return nil
158158
}
159159

160-
// If no rows and not JSON, show a friendly message.
161-
if len(tasks) == 0 && formatter.FormatID() != cliui.JSONFormat().ID() {
162-
_, _ = fmt.Fprintln(inv.Stderr, "No tasks found.")
163-
return nil
164-
}
165-
166160
rows := make([]taskListRow, len(tasks))
167161
now := time.Now()
168162
for i := range tasks {
@@ -173,6 +167,10 @@ func (r *RootCmd) taskList() *serpent.Command {
173167
if err != nil {
174168
return xerrors.Errorf("format tasks: %w", err)
175169
}
170+
if out == "" {
171+
cliui.Infof(inv.Stderr, "No tasks found.")
172+
return nil
173+
}
176174
_, _ = fmt.Fprintln(inv.Stdout, out)
177175
return nil
178176
},

0 commit comments

Comments
 (0)