Skip to content

Commit 9c08a58

Browse files
authored
Support hooks on embed:"" fields (#493)
Relates to 840220c (#90) This change adds support for hooks to be called on fields that are tagged with `embed:""`. ### Use case If a command has several subcommands, many (but not all) of which need the same external resource, this allows defining the flag-level inputs for that resource centrally, and then using `embed:""` in any command that needs that resource. For example, imagine: ```go type githubClientProvider struct { Token string `name:"github-token" env:"GITHUB_TOKEN"` URL string `name:"github-url" env:"GITHUB_URL"` } func (g *githubClientProvider) BeforeApply(kctx *kong.Context) error { return kctx.BindToProvider(func() (*github.Client, error) { return github.NewClient(...), nil }) } ``` Then, any command that needs GitHub client will add this field, any other resource providers it needs, and add parameters to its `Run` method to accept those resources: ```go type listUsersCmd struct { GitHub githubClientProvider `embed:""` S3 s3ClientProvider `embed:""` } func (l *listUsersCmd) Run(gh *github.Client, s3 *s3.Client) error { ... } ``` ### Alternatives It is possible to do the same today if the `*Provider` struct above is actually a Go embed instead of a Kong embed, *and* it is exported. ``` type GitHubClientProvider struct{ ... } type listUsersCmd struct { GithubClientProvider S3ClientProvider } ``` The difference is whether the struct defining the flags is required to be exported or not.
1 parent 042a325 commit 9c08a58

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

callbacks.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,19 @@ func getMethods(value reflect.Value, name string) []reflect.Value {
8080
for i := 0; i < value.NumField(); i++ {
8181
field := value.Field(i)
8282
fieldType := t.Field(i)
83-
if fieldType.IsExported() && fieldType.Anonymous {
83+
if !fieldType.IsExported() {
84+
continue
85+
}
86+
87+
// Hooks on exported embedded fields should be called.
88+
if fieldType.Anonymous {
89+
receivers = append(receivers, field)
90+
continue
91+
}
92+
93+
// Hooks on exported fields that are not exported,
94+
// but are tagged with `embed:""` should be called.
95+
if _, ok := fieldType.Tag.Lookup("embed"); ok {
8496
receivers = append(receivers, field)
8597
}
8698
}

kong_test.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -2413,9 +2413,19 @@ func (e *EmbeddedCallback) AfterApply() error {
24132413
return nil
24142414
}
24152415

2416+
type taggedEmbeddedCallback struct {
2417+
Tagged bool
2418+
}
2419+
2420+
func (e *taggedEmbeddedCallback) AfterApply() error {
2421+
e.Tagged = true
2422+
return nil
2423+
}
2424+
24162425
type EmbeddedRoot struct {
24172426
EmbeddedCallback
2418-
Root bool
2427+
Tagged taggedEmbeddedCallback `embed:""`
2428+
Root bool
24192429
}
24202430

24212431
func (e *EmbeddedRoot) AfterApply() error {
@@ -2432,6 +2442,9 @@ func TestEmbeddedCallbacks(t *testing.T) {
24322442
EmbeddedCallback: EmbeddedCallback{
24332443
Embedded: true,
24342444
},
2445+
Tagged: taggedEmbeddedCallback{
2446+
Tagged: true,
2447+
},
24352448
Root: true,
24362449
}
24372450
assert.Equal(t, expected, actual)

0 commit comments

Comments
 (0)