Skip to content

Commit 4a9a7e1

Browse files
authored
feat: add cobra cmd to context (#9)
* feat: add cobra cmd to context * fix test * refactor * use getter method instead for ctx * no lint * fix readme
1 parent 516d4f2 commit 4a9a7e1

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

README.md

+24-8
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func main() {
103103
}
104104
```
105105

106-
### Configuration
106+
### CommandWithConfig
107107

108108
Ecdysis provides an automatic way to parse a configuration file, environment variables, and flags using the [`viper`](https://github.com/spf13/viper) library. To use it, you need to implement the `CommandWithConfig` interface.
109109

@@ -122,7 +122,9 @@ The order of precedence for configuration values is:
122122
123123
```go
124124
var (
125-
_ ecdysis.CommandWithConfiguration = (*RootCommand)(nil)
125+
_ ecdysis.CommandWithFlags = (*RootCommand)(nil)
126+
_ ecdysis.CommandWithExecute = (*RootCommand)(nil)
127+
_ ecdysis.CommandWithConfig = (*RootCommand)(nil)
126128
)
127129

128130
type ConduitConfig struct {
@@ -144,12 +146,12 @@ type RootCommand struct {
144146
cfg ConduitConfig
145147
}
146148

147-
func (c *RootCommand) ParseConfig() ecdysis.Config {
149+
func (c *RootCommand) Config() ecdysis.Config {
148150
return ecdysis.Config{
149-
EnvPrefix: "CONDUIT", // prefix for environment variables
150-
ParsedCfg: &c.cfg, // where configuration will be parsed
151-
ConfigPath: c.flags.ConduitCfgPath, // where to read the configuration file
152-
DefaultCfg: c.cfg, // where to extract default values from
151+
EnvPrefix: "CONDUIT",
152+
Parsed: &c.Cfg,
153+
Path: c.flags.ConduitCfgPath,
154+
DefaultValues: conduit.DefaultConfigWithBasePath(path),
153155
}
154156
}
155157

@@ -167,7 +169,21 @@ func (c *RootCommand) Flags() []ecdysis.Flag {
167169

168170
return flags
169171
}
170-
````
172+
```
173+
174+
### Fetching `cobra.Command` from `CommandWithExecute`
175+
176+
If you need to access the `cobra.Command` instance from a `CommandWithExecute` implementation, you can utilize
177+
the `ecdysis.CobraCmdFromContext` function to fetch it from the context:
178+
179+
```go
180+
func (c *RootCommand) Execute(ctx context.Context) error {
181+
if cmd := ecdysis.CobraCmdFromContext(ctx); cmd != nil {
182+
return cmd.Help()
183+
}
184+
return nil
185+
}
186+
```
171187

172188
## Flags
173189

context.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright © 2024 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+
"context"
19+
20+
"github.com/spf13/cobra"
21+
)
22+
23+
type cobraCmdCtxKey struct{}
24+
25+
// contextWithCobraCommand provides the cobra command to the context.
26+
// This is useful for situations such as wanting to execute cmd.Help() directly from Execute().
27+
func contextWithCobraCommand(ctx context.Context, cmd *cobra.Command) context.Context {
28+
return context.WithValue(ctx, cobraCmdCtxKey{}, cmd)
29+
}
30+
31+
// CobraCmdFromContext fetches the cobra command from the context. If the
32+
// context does not contain a cobra command, it returns nil.
33+
func CobraCmdFromContext(ctx context.Context) *cobra.Command {
34+
if cobraCmd := ctx.Value(cobraCmdCtxKey{}); cobraCmd != nil {
35+
return cobraCmd.(*cobra.Command) //nolint:forcetypeassert // only this package can set the value, it has to be a *cobra.Command
36+
}
37+
return nil
38+
}

decorators.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,9 @@ func (CommandWithExecuteDecorator) Decorate(_ *Ecdysis, cmd *cobra.Command, c Co
636636
return err
637637
}
638638
}
639-
return v.Execute(cmd.Context())
639+
640+
ctx := contextWithCobraCommand(cmd.Context(), cmd)
641+
return v.Execute(ctx)
640642
}
641643

642644
return nil

ecdysis_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ func TestBuildCobraCommand_Behavioral(t *testing.T) {
112112
ctx := context.Background()
113113
ctrl := gomock.NewController(t)
114114
cmd := NewMockBehavioralTestCommand(ctrl)
115+
ctx = context.WithValue(ctx, cobraCmdCtxKey{}, cmd)
115116

116117
wantLogger := slog.New(slog.NewTextHandler(nil, nil))
117118
ecdysis := New(WithDecorators(CommandWithLoggerDecorator{Logger: wantLogger}))
@@ -124,7 +125,7 @@ func TestBuildCobraCommand_Behavioral(t *testing.T) {
124125

125126
// Set up the remaining expectations before executing the command.
126127
call = cmd.EXPECT().Args(gomock.Any()).Return(nil).After(call)
127-
cmd.EXPECT().Execute(ctx).Return(nil).After(call)
128+
cmd.EXPECT().Execute(gomock.AssignableToTypeOf(ctx)).Return(nil).After(call)
128129

129130
err := got.ExecuteContext(ctx)
130131
if err != nil {

0 commit comments

Comments
 (0)