Skip to content

Commit 6b7b181

Browse files
feat: add formatter output library (#94)
2 parents 35e3599 + 85b13cf commit 6b7b181

File tree

5 files changed

+208
-1
lines changed

5 files changed

+208
-1
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ To check and update all files, run:
4242

4343
```
4444
$ go install github.com/google/addlicense@latest
45-
$ addlicense -l apache -v -ignore '**/*.yaml' -c Humanitec ./loader ./schema ./types ./pkg
45+
$ addlicense -l apache -v -ignore '**/*.yaml' -c Humanitec ./loader ./schema ./types ./formatter
4646
```
4747

4848
## Feature requests

formatter/output.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2025 Humanitec
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package formatter
16+
17+
import (
18+
"encoding/json"
19+
"io"
20+
"os"
21+
22+
"github.com/olekukonko/tablewriter"
23+
"gopkg.in/yaml.v3"
24+
)
25+
26+
type OutputFormatter interface {
27+
Display() error
28+
}
29+
30+
type JSONOutputFormatter[T any] struct {
31+
Data T
32+
Out io.Writer
33+
}
34+
35+
type YAMLOutputFormatter[T any] struct {
36+
Data T
37+
Out io.Writer
38+
}
39+
40+
type TableOutputFormatter struct {
41+
Headers []string
42+
Rows [][]string
43+
Out io.Writer
44+
}
45+
46+
func (t *TableOutputFormatter) Display() error {
47+
// Default to stdout if no output is provided
48+
if t.Out == nil {
49+
t.Out = os.Stdout
50+
}
51+
table := tablewriter.NewWriter(t.Out)
52+
table.SetHeader(t.Headers)
53+
table.AppendBulk(t.Rows)
54+
table.SetAutoWrapText(false)
55+
table.SetRowLine(true)
56+
table.SetCenterSeparator("+")
57+
table.SetColumnSeparator("|")
58+
table.SetRowSeparator("-")
59+
table.Render()
60+
return nil
61+
}
62+
63+
func (j *JSONOutputFormatter[T]) Display() error {
64+
// Default to stdout if no output is provided
65+
if j.Out == nil {
66+
j.Out = os.Stdout
67+
}
68+
encoder := json.NewEncoder(j.Out)
69+
encoder.SetIndent("", " ")
70+
if err := encoder.Encode(j.Data); err != nil {
71+
return err
72+
}
73+
return nil
74+
}
75+
76+
func (y *YAMLOutputFormatter[T]) Display() error {
77+
// Default to stdout if no output is provided
78+
if y.Out == nil {
79+
y.Out = os.Stdout
80+
}
81+
82+
encoder := yaml.NewEncoder(y.Out)
83+
if err := encoder.Encode(y.Data); err != nil {
84+
return err
85+
}
86+
return nil
87+
}

formatter/output_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2024 Humanitec
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package formatter
16+
17+
import (
18+
"bytes"
19+
"testing"
20+
21+
"github.com/stretchr/testify/assert"
22+
)
23+
24+
func TestTableOutputFormatter_Display(t *testing.T) {
25+
tests := []struct {
26+
name string
27+
headers []string
28+
rows [][]string
29+
want string
30+
}{
31+
{
32+
name: "simple table",
33+
headers: []string{"Name", "Value"},
34+
rows: [][]string{{"n1", "v1"}, {"n2", "v2"}},
35+
want: `+------+-------+
36+
| NAME | VALUE |
37+
+------+-------+
38+
| n1 | v1 |
39+
+------+-------+
40+
| n2 | v2 |
41+
+------+-------+
42+
`,
43+
},
44+
}
45+
46+
for _, tt := range tests {
47+
t.Run(tt.name, func(t *testing.T) {
48+
buf := &bytes.Buffer{}
49+
f := &TableOutputFormatter{
50+
Headers: tt.headers,
51+
Rows: tt.rows,
52+
Out: buf,
53+
}
54+
err := f.Display()
55+
assert.NoError(t, err)
56+
assert.Equal(t, tt.want, buf.String())
57+
})
58+
}
59+
}
60+
61+
func TestJSONOutputFormatter_Display(t *testing.T) {
62+
tests := []struct {
63+
name string
64+
data []interface{}
65+
want string
66+
}{
67+
{
68+
name: "simple object",
69+
data: []interface{}{map[string]string{"k1": "v1"}, map[string]string{"k2": "v2"}},
70+
want: "[\n {\n \"k1\": \"v1\"\n },\n {\n \"k2\": \"v2\"\n }\n]\n",
71+
},
72+
}
73+
74+
for _, tt := range tests {
75+
t.Run(tt.name, func(t *testing.T) {
76+
buf := &bytes.Buffer{}
77+
f := &JSONOutputFormatter[interface{}]{
78+
Data: tt.data,
79+
Out: buf,
80+
}
81+
err := f.Display()
82+
assert.NoError(t, err)
83+
assert.Equal(t, tt.want, buf.String())
84+
})
85+
}
86+
}
87+
88+
func TestYAMLOutputFormatter_Display(t *testing.T) {
89+
tests := []struct {
90+
name string
91+
data []interface{}
92+
want string
93+
}{
94+
{
95+
name: "simple object",
96+
data: []interface{}{map[string]string{"k1": "v1"}, map[string]string{"k2": "v2"}},
97+
want: "- k1: v1\n- k2: v2\n",
98+
},
99+
}
100+
101+
for _, tt := range tests {
102+
t.Run(tt.name, func(t *testing.T) {
103+
buf := &bytes.Buffer{}
104+
f := &YAMLOutputFormatter[interface{}]{
105+
Data: tt.data,
106+
Out: buf,
107+
}
108+
err := f.Display()
109+
assert.NoError(t, err)
110+
assert.Equal(t, tt.want, buf.String())
111+
})
112+
}
113+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ require (
1111
oras.land/oras-go/v2 v2.5.0
1212
)
1313

14+
require github.com/mattn/go-runewidth v0.0.9 // indirect
15+
1416
require (
1517
github.com/davecgh/go-spew v1.1.1 // indirect
18+
github.com/olekukonko/tablewriter v0.0.5
1619
github.com/opencontainers/go-digest v1.0.0 // indirect
1720
github.com/pmezard/go-difflib v1.0.0 // indirect
1821
golang.org/x/sync v0.12.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
4+
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
35
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
46
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
7+
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
8+
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
59
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
610
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
711
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=

0 commit comments

Comments
 (0)