feat: Allow ignoring fields from embedded structs (#499)

If a field in a struct is ignored with `kong:"-"`, any embedded fields
with the same name are also ignored. This allows an outer struct to
remove flags from an embedded struct by redefining it and adding a kong
ignore tag.
This commit is contained in:
Cam Hutchison
2025-02-17 20:06:02 +11:00
committed by GitHub
parent 3b9af5bdce
commit 300cba8c27
2 changed files with 58 additions and 1 deletions
+21 -1
View File
@@ -54,6 +54,7 @@ func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err erro
if v.Kind() != reflect.Struct {
return out, nil
}
ignored := map[string]bool{}
for i := 0; i < v.NumField(); i++ {
ft := v.Type().Field(i)
fv := v.Field(i)
@@ -61,7 +62,8 @@ func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err erro
if err != nil {
return nil, err
}
if tag.Ignored {
if tag.Ignored || ignored[ft.Name] {
ignored[ft.Name] = true
continue
}
// Assign group if it's not already set.
@@ -106,9 +108,27 @@ func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err erro
}
out = append(out, sub...)
}
out = removeIgnored(out, ignored)
return out, nil
}
func removeIgnored(fields []flattenedField, ignored map[string]bool) []flattenedField {
j := 0
for i := 0; i < len(fields); i++ {
if ignored[fields[i].field.Name] {
continue
}
if i != j {
fields[j] = fields[i]
}
j++
}
if j != len(fields) {
fields = fields[:j]
}
return fields
}
// Build a Node in the Kong data model.
//
// "v" is the value to create the node from, "typ" is the output Node type.
+37
View File
@@ -856,6 +856,43 @@ func TestExcludedField(t *testing.T) {
assert.Error(t, err)
}
func TestExcludeEmbeddedField(t *testing.T) {
type Embedded struct {
Flag string
Excluded string
}
type Embedded2 struct {
Flag2 string
Excluded string
}
var cli struct {
Embedded
Excluded string `kong:"-"`
Embedded2
}
var cli2 struct {
Embedded Embedded `kong:"embed"`
Excluded string `kong:"-"`
Embedded2 Embedded2 `kong:"embed"`
}
p := mustNew(t, &cli)
_, err := p.Parse([]string{"--flag=foo"})
assert.NoError(t, err)
_, err = p.Parse([]string{"--flag-2=foo"})
assert.NoError(t, err)
_, err = p.Parse([]string{"--excluded=foo"})
assert.Error(t, err)
p = mustNew(t, &cli2)
_, err = p.Parse([]string{"--flag=foo"})
assert.NoError(t, err)
_, err = p.Parse([]string{"--flag-2=foo"})
assert.NoError(t, err)
_, err = p.Parse([]string{"--excluded=foo"})
assert.Error(t, err)
}
func TestUnnamedFieldEmbeds(t *testing.T) {
type Embed struct {
Flag string