-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
imagefilter: add
ResultFormatter
type to support flexible output
This commit adds a new `ResultFormatter` interface that supports outputing the results as text or JSON.
- Loading branch information
Showing
2 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package imagefilter | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
) | ||
|
||
// OutputFormat contains the valid output formats for formatting results | ||
type OutputFormat string | ||
|
||
const ( | ||
OutputFormatDefault OutputFormat = "" | ||
OutputFormatText OutputFormat = "text" | ||
OutputFormatJSON OutputFormat = "json" | ||
) | ||
|
||
// ResultFormatter will format the given result list to the given io.Writer | ||
type ResultsFormatter interface { | ||
Output(io.Writer, []Result) error | ||
} | ||
|
||
// NewResultFormatter will create a formatter based on the given format. | ||
func NewResultsFormatter(format OutputFormat) (ResultsFormatter, error) { | ||
switch format { | ||
case OutputFormatDefault, OutputFormatText: | ||
return &textResultsFormatter{}, nil | ||
case OutputFormatJSON: | ||
return &jsonResultsFormatter{}, nil | ||
default: | ||
return nil, fmt.Errorf("unsupported formatter %q", format) | ||
} | ||
} | ||
|
||
type textResultsFormatter struct{} | ||
|
||
func (*textResultsFormatter) Output(w io.Writer, all []Result) error { | ||
var errs []error | ||
|
||
for _, res := range all { | ||
// this should be copy/paste friendly | ||
if _, err := fmt.Fprintf(w, "%s arch:%s type:%s\n", res.Distro.Name(), res.Arch.Name(), res.ImgType.Name()); err != nil { | ||
errs = append(errs, err) | ||
} | ||
} | ||
if len(errs) > 0 { | ||
return errors.Join(errs...) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type jsonResultsFormatter struct{} | ||
|
||
type distroResultJSON struct { | ||
Name string `json:"name"` | ||
} | ||
|
||
type archResultJSON struct { | ||
Name string `json:"name"` | ||
} | ||
|
||
type imgTypeResultJSON struct { | ||
Name string `json:"name"` | ||
} | ||
|
||
type filteredResultJSON struct { | ||
Distro distroResultJSON `json:"distro"` | ||
Arch archResultJSON `json:"arch"` | ||
ImgType imgTypeResultJSON `json:"image_type"` | ||
} | ||
|
||
func (*jsonResultsFormatter) Output(w io.Writer, all []Result) error { | ||
var out []filteredResultJSON | ||
|
||
for _, res := range all { | ||
out = append(out, filteredResultJSON{ | ||
Distro: distroResultJSON{ | ||
Name: res.Distro.Name(), | ||
}, | ||
Arch: archResultJSON{ | ||
Name: res.Arch.Name(), | ||
}, | ||
ImgType: imgTypeResultJSON{ | ||
Name: res.ImgType.Name(), | ||
}, | ||
}) | ||
} | ||
|
||
enc := json.NewEncoder(w) | ||
return enc.Encode(out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package imagefilter_test | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/osbuild/images/pkg/distrofactory" | ||
"github.com/osbuild/images/pkg/imagefilter" | ||
) | ||
|
||
func TestResultsFormatter(t *testing.T) { | ||
fac := distrofactory.NewTestDefault() | ||
|
||
for _, tc := range []struct { | ||
formatter string | ||
distro, arch, imgType string | ||
expectsOutput string | ||
}{ | ||
{ | ||
"", | ||
"test-distro-1", "test_arch3", "qcow2", | ||
`test-distro-1 arch:test_arch3 type:qcow2` + "\n", | ||
}, | ||
{ | ||
"text", | ||
"test-distro-1", "test_arch3", "qcow2", | ||
`test-distro-1 arch:test_arch3 type:qcow2` + "\n", | ||
}, | ||
{ | ||
"json", | ||
"test-distro-1", "test_arch3", "qcow2", | ||
`[{"distro":{"name":"test-distro-1"},"arch":{"name":"test_arch3"},"image_type":{"name":"qcow2"}}]` + "\n", | ||
}, | ||
} { | ||
// XXX: it would be nice if TestDistro would support constructing | ||
// like GetDistro("rhel-8.1:i386,amd64:ami,qcow2") instead of | ||
// the current very static setup | ||
di := fac.GetDistro(tc.distro) | ||
require.NotNil(t, di) | ||
ar, err := di.GetArch(tc.arch) | ||
require.NoError(t, err) | ||
im, err := ar.GetImageType(tc.imgType) | ||
require.NoError(t, err) | ||
|
||
var buf bytes.Buffer | ||
res := []imagefilter.Result{ | ||
{Distro: di, Arch: ar, ImgType: im}, | ||
} | ||
fmter, err := imagefilter.NewResultsFormatter(imagefilter.OutputFormat(tc.formatter)) | ||
require.NoError(t, err) | ||
err = fmter.Output(&buf, res) | ||
assert.NoError(t, err) | ||
assert.Equal(t, tc.expectsOutput, buf.String(), tc) | ||
} | ||
} |