From d0c0180cecfd46203f91b16795cd70156a89b4ba Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Sat, 28 Aug 2021 16:54:20 +1000 Subject: [PATCH] Hydrate pointers to embedded structs. --- build.go | 13 +++++-------- kong_test.go | 12 +++++++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/build.go b/build.go index 85e18dc..f7893be 100644 --- a/build.go +++ b/build.go @@ -61,6 +61,11 @@ func flattenedFields(v reflect.Value) (out []flattenedField, err error) { if tag.Ignored { continue } + // Command and embedded structs can be pointers, so we hydrate them now. + if (tag.Cmd || tag.Embed) && ft.Type.Kind() == reflect.Ptr { + fv = reflect.New(ft.Type.Elem()).Elem() + v.FieldByIndex(ft.Index).Set(fv.Addr()) + } if !ft.Anonymous && !tag.Embed { if fv.CanSet() { out = append(out, flattenedField{field: ft, value: fv, tag: tag}) @@ -133,14 +138,6 @@ MAIN: name = tag.Prefix + name } - fieldType := ft.Type - // Hydrate command structs that are pointers. - if tag.Cmd && fieldType.Kind() == reflect.Ptr { - fv = reflect.New(fieldType.Elem()).Elem() - field.value = fv - v.FieldByIndex(field.field.Index).Set(fv.Addr()) - } - // Nested structs are either commands or args, unless they implement the Mapper interface. if field.value.Kind() == reflect.Struct && (tag.Cmd || tag.Arg) && k.registry.ForValue(fv) == nil { typ := CommandNode diff --git a/kong_test.go b/kong_test.go index 7d80d40..6bfd514 100644 --- a/kong_test.go +++ b/kong_test.go @@ -1283,19 +1283,25 @@ func TestDuplicateNestedShortFlags(t *testing.T) { require.EqualError(t, err, ".Flag2: duplicate short flag -t") } -func TestHydratePointerCommands(t *testing.T) { +func TestHydratePointerCommandsAndEmbeds(t *testing.T) { type cmd struct { Flag bool } + type embed struct { + Embed bool + } + var cli struct { - Cmd *cmd `cmd:""` + Cmd *cmd `cmd:""` + Embed *embed `embed:""` } k := mustNew(t, &cli) - _, err := k.Parse([]string{"cmd", "--flag"}) + _, err := k.Parse([]string{"--embed", "cmd", "--flag"}) require.NoError(t, err) require.Equal(t, &cmd{Flag: true}, cli.Cmd) + require.Equal(t, &embed{Embed: true}, cli.Embed) } // nolint