Skip to content

Commit 964e246

Browse files
authored
feat: add display decorator (#11)
* add display decorator * rename decorator name * add header * update type * add tests and uses cobra stdout and err * add header * update description
1 parent 2097505 commit 964e246

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

decorators.go

+32
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030

3131
var DefaultDecorators = []Decorator{
3232
CommandWithLoggerDecorator{},
33+
CommandWithOutputDecorator{},
34+
3335
CommandWithAliasesDecorator{},
3436
CommandWithFlagsDecorator{},
3537

@@ -81,6 +83,36 @@ func (d CommandWithLoggerDecorator) Decorate(_ *Ecdysis, _ *cobra.Command, c Com
8183
return nil
8284
}
8385

86+
// -- OUTPUT -------------------------------------------------------------------
87+
88+
// CommandWithOutput can be implemented by a command to provide its own stdout and stderr.
89+
type CommandWithOutput interface {
90+
Command
91+
Output(output Output)
92+
}
93+
94+
// CommandWithOutputDecorator is a decorator that provides a Stdout to the command.
95+
// If the Stdout field is not set, the default stdout will be provided.
96+
type CommandWithOutputDecorator struct {
97+
Output Output
98+
}
99+
100+
// Decorate provides the logger to the command.
101+
func (d CommandWithOutputDecorator) Decorate(_ *Ecdysis, cmd *cobra.Command, c Command) error {
102+
v, ok := c.(CommandWithOutput)
103+
if !ok {
104+
return nil
105+
}
106+
107+
if d.Output == nil {
108+
v.Output(NewDefaultOutput(cmd))
109+
} else {
110+
v.Output(d.Output)
111+
}
112+
113+
return nil
114+
}
115+
84116
// -- ALIASES ------------------------------------------------------------------
85117

86118
// CommandWithAliases can be implemented by a command to provide aliases.

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.23.0
44

55
require (
66
github.com/google/go-cmp v0.6.0
7+
github.com/matryer/is v1.4.1
78
github.com/spf13/cobra v1.8.1
89
github.com/spf13/pflag v1.0.5
910
github.com/spf13/viper v1.19.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1919
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
2020
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
2121
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
22+
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
23+
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
2224
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
2325
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
2426
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=

output.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright © 2025 Meroxa, Inc.
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 ecdysis
16+
17+
import (
18+
"fmt"
19+
"io"
20+
21+
"github.com/spf13/cobra"
22+
)
23+
24+
type Output interface {
25+
Stdout(any)
26+
Stderr(any)
27+
}
28+
29+
type DefaultOutput struct {
30+
stdout io.Writer
31+
stderr io.Writer
32+
}
33+
34+
func NewDefaultOutput(cmd *cobra.Command) *DefaultOutput {
35+
return &DefaultOutput{
36+
stdout: cmd.OutOrStdout(),
37+
stderr: cmd.OutOrStderr(),
38+
}
39+
}
40+
41+
// Stdout writes a message to the configured standard output.
42+
func (d *DefaultOutput) Stdout(msg any) {
43+
fmt.Fprint(d.stdout, msg)
44+
}
45+
46+
// Stderr writes a message to the configured standard error.
47+
func (d *DefaultOutput) Stderr(msg any) {
48+
fmt.Fprint(d.stderr, msg)
49+
}
50+
51+
// Output allows overwriting the stdout and/or stderr for specific use cases (like testing).
52+
func (d *DefaultOutput) Output(stdout, stderr io.Writer) {
53+
if stdout != nil {
54+
d.stdout = stdout
55+
}
56+
if stderr != nil {
57+
d.stderr = stderr
58+
}
59+
}

output_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright © 2025 Meroxa, Inc.
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 ecdysis
16+
17+
import (
18+
"bytes"
19+
"testing"
20+
21+
"github.com/matryer/is"
22+
"github.com/spf13/cobra"
23+
)
24+
25+
func TestNewDefaultOutput(t *testing.T) {
26+
is := is.New(t)
27+
cmd := &cobra.Command{}
28+
out := NewDefaultOutput(cmd)
29+
30+
is.Equal(out.stdout, cmd.OutOrStdout())
31+
is.Equal(out.stderr, cmd.OutOrStderr())
32+
}
33+
34+
func TestStdout(t *testing.T) {
35+
is := is.New(t)
36+
37+
var buf bytes.Buffer
38+
out := &DefaultOutput{stdout: &buf}
39+
40+
message := "hello stdout"
41+
out.Stdout(message)
42+
43+
is.Equal(buf.String(), message)
44+
}
45+
46+
func TestStderr(t *testing.T) {
47+
is := is.New(t)
48+
49+
var buf bytes.Buffer
50+
out := &DefaultOutput{stderr: &buf}
51+
52+
message := "hello stderr"
53+
out.Stderr(message)
54+
55+
is.Equal(buf.String(), message)
56+
}
57+
58+
func TestOutput(t *testing.T) {
59+
is := is.New(t)
60+
61+
var stdoutBuf, stderrBuf bytes.Buffer
62+
out := &DefaultOutput{}
63+
64+
out.Output(&stdoutBuf, &stderrBuf)
65+
66+
stdoutMsg := "stdout test"
67+
stderrMsg := "stderr test"
68+
out.Stdout(stdoutMsg)
69+
out.Stderr(stderrMsg)
70+
71+
is.Equal(stdoutMsg, stdoutBuf.String())
72+
is.Equal(stderrMsg, stderrBuf.String())
73+
}

0 commit comments

Comments
 (0)