Skip to content

Commit abd2e21

Browse files
committed
cli/command/formatter: add Format.templateString, remove Context.preFormat
The `Context.preFormat` method normalizes the Format as given by the user, and handles (e.g.) stripping the "table" prefix and replacing the "json" format for the actual format (`{{json .}}`). The method used a `finalFormat` field on the Context as intermediate, and was required to be called before executing the format. This patch adds a `Format.templateString()` method that returns the parsed format instead of storing it on the Context. It is currently not exported, but something we could consider in future. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent cb615a9 commit abd2e21

3 files changed

Lines changed: 94 additions & 35 deletions

File tree

cli/command/formatter/disk_usage.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ func (ctx *DiskUsageContext) startSubsection(format Format) (*template.Template,
4646
ctx.buffer = &bytes.Buffer{}
4747
ctx.header = ""
4848
ctx.Format = format
49-
ctx.preFormat()
5049

5150
return ctx.parseFormat()
5251
}
@@ -88,7 +87,6 @@ func (ctx *DiskUsageContext) Write() (err error) {
8887
return ctx.verboseWrite()
8988
}
9089
ctx.buffer = &bytes.Buffer{}
91-
ctx.preFormat()
9290

9391
tmpl, err := ctx.parseFormat()
9492
if err != nil {
@@ -213,7 +211,6 @@ func (ctx *DiskUsageContext) verboseWrite() error {
213211
return ctx.verboseWriteTable(duc)
214212
}
215213

216-
ctx.preFormat()
217214
tmpl, err := ctx.parseFormat()
218215
if err != nil {
219216
return err

cli/command/formatter/formatter.go

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func (f Format) IsTable() bool {
3333
return strings.HasPrefix(string(f), TableFormatKey)
3434
}
3535

36-
// IsJSON returns true if the format is the json format
36+
// IsJSON returns true if the format is the JSON format
3737
func (f Format) IsJSON() bool {
3838
return string(f) == JSONFormatKey
3939
}
@@ -43,6 +43,31 @@ func (f Format) Contains(sub string) bool {
4343
return strings.Contains(string(f), sub)
4444
}
4545

46+
// templateString pre-processes the format and returns it as a string
47+
// for templating.
48+
func (f Format) templateString() string {
49+
out := string(f)
50+
switch out {
51+
case TableFormatKey:
52+
// A bare "--format table" should already be handled before we
53+
// hit this; a literal "table" here means a custom "table" format
54+
// without template.
55+
return ""
56+
case JSONFormatKey:
57+
// "--format json" only; not JSON formats ("--format '{{json .Field}}'").
58+
return JSONFormat
59+
}
60+
61+
// "--format 'table {{.Field}}\t{{.Field}}'" -> "{{.Field}}\t{{.Field}}"
62+
if after, isTable := strings.CutPrefix(out, TableFormatKey); isTable {
63+
out = after
64+
}
65+
66+
out = strings.Trim(out, " ") // trim spaces, but preserve other whitespace.
67+
out = strings.NewReplacer(`\t`, "\t", `\n`, "\n").Replace(out)
68+
return out
69+
}
70+
4671
// Context contains information required by the formatter to print the output as desired.
4772
type Context struct {
4873
// Output is the output stream to which the formatted string is written.
@@ -53,28 +78,12 @@ type Context struct {
5378
Trunc bool
5479

5580
// internal element
56-
finalFormat string
57-
header any
58-
buffer *bytes.Buffer
59-
}
60-
61-
func (c *Context) preFormat() {
62-
c.finalFormat = string(c.Format)
63-
// TODO: handle this in the Format type
64-
switch {
65-
case c.Format.IsTable():
66-
c.finalFormat = c.finalFormat[len(TableFormatKey):]
67-
case c.Format.IsJSON():
68-
c.finalFormat = JSONFormat
69-
}
70-
71-
c.finalFormat = strings.Trim(c.finalFormat, " ")
72-
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
73-
c.finalFormat = r.Replace(c.finalFormat)
81+
header any
82+
buffer *bytes.Buffer
7483
}
7584

7685
func (c *Context) parseFormat() (*template.Template, error) {
77-
tmpl, err := templates.Parse(c.finalFormat)
86+
tmpl, err := templates.Parse(c.Format.templateString())
7887
if err != nil {
7988
return nil, fmt.Errorf("template parsing error: %w", err)
8089
}
@@ -116,8 +125,6 @@ type SubFormat func(func(SubContext) error) error
116125
// Write the template to the buffer using this Context
117126
func (c *Context) Write(sub SubContext, f SubFormat) error {
118127
c.buffer = &bytes.Buffer{}
119-
c.preFormat()
120-
121128
tmpl, err := c.parseFormat()
122129
if err != nil {
123130
return err

cli/command/formatter/formatter_test.go

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,75 @@ import (
88
"testing"
99

1010
"gotest.tools/v3/assert"
11+
is "gotest.tools/v3/assert/cmp"
1112
)
1213

1314
func TestFormat(t *testing.T) {
14-
f := Format("json")
15-
assert.Assert(t, f.IsJSON())
16-
assert.Assert(t, !f.IsTable())
17-
18-
f = Format("table")
19-
assert.Assert(t, !f.IsJSON())
20-
assert.Assert(t, f.IsTable())
15+
tests := []struct {
16+
doc string
17+
f Format
18+
isJSON bool
19+
isTable bool
20+
template string
21+
}{
22+
{
23+
doc: "json format",
24+
f: "json",
25+
isJSON: true,
26+
isTable: false,
27+
template: JSONFormat,
28+
},
29+
{
30+
doc: "empty table format (no template)",
31+
f: "table",
32+
isJSON: false,
33+
isTable: true,
34+
template: "",
35+
},
36+
{
37+
doc: "table with escaped tabs",
38+
f: "table {{.Field}}\\t{{.Field2}}",
39+
isJSON: false,
40+
isTable: true,
41+
template: "{{.Field}}\t{{.Field2}}",
42+
},
43+
{
44+
doc: "table with raw string",
45+
f: `table {{.Field}}\t{{.Field2}}`,
46+
isJSON: false,
47+
isTable: true,
48+
template: "{{.Field}}\t{{.Field2}}",
49+
},
50+
{
51+
doc: "other format",
52+
f: "other",
53+
isJSON: false,
54+
isTable: false,
55+
template: "other",
56+
},
57+
{
58+
doc: "other with spaces",
59+
f: " other ",
60+
isJSON: false,
61+
isTable: false,
62+
template: "other",
63+
},
64+
{
65+
doc: "other with newline preserved",
66+
f: " other\n ",
67+
isJSON: false,
68+
isTable: false,
69+
template: "other\n",
70+
},
71+
}
2172

22-
f = Format("other")
23-
assert.Assert(t, !f.IsJSON())
24-
assert.Assert(t, !f.IsTable())
73+
for _, tc := range tests {
74+
t.Run(tc.doc, func(t *testing.T) {
75+
assert.Check(t, is.Equal(tc.f.IsJSON(), tc.isJSON))
76+
assert.Check(t, is.Equal(tc.f.IsTable(), tc.isTable))
77+
assert.Check(t, is.Equal(tc.f.templateString(), tc.template))
78+
})
79+
}
2580
}
2681

2782
type fakeSubContext struct {

0 commit comments

Comments
 (0)