Skip to content

Commit

Permalink
Add ability to ignore fields that match given regex pattern(s) (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
dselans authored Aug 28, 2021
1 parent dd904ce commit 07faa2e
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 11 deletions.
9 changes: 9 additions & 0 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func build(k *Kong, ast interface{}) (app *Application, err error) {
for _, flag := range extraFlags {
seenFlags[flag.Name] = true
}

node, err := buildNode(k, iv, ApplicationNode, seenFlags)
if err != nil {
return nil, err
Expand Down Expand Up @@ -112,7 +113,15 @@ func buildNode(k *Kong, v reflect.Value, typ NodeType, seenFlags map[string]bool
if err != nil {
return nil, err
}

MAIN:
for _, field := range fields {
for _, r := range k.ignoreFieldsRegex {
if r.MatchString(v.Type().Name() + "." + field.field.Name) {
continue MAIN
}
}

ft := field.field
fv := field.value

Expand Down
25 changes: 14 additions & 11 deletions kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
)

Expand Down Expand Up @@ -48,10 +49,11 @@ type Kong struct {
Stdout io.Writer
Stderr io.Writer

bindings bindings
loader ConfigurationLoader
resolvers []Resolver
registry *Registry
bindings bindings
loader ConfigurationLoader
resolvers []Resolver
registry *Registry
ignoreFieldsRegex []*regexp.Regexp

noDefaultHelp bool
usageOnError usageOnError
Expand All @@ -73,13 +75,14 @@ type Kong struct {
// See the README (https://github.com/alecthomas/kong) for usage instructions.
func New(grammar interface{}, options ...Option) (*Kong, error) {
k := &Kong{
Exit: os.Exit,
Stdout: os.Stdout,
Stderr: os.Stderr,
registry: NewRegistry().RegisterDefaults(),
vars: Vars{},
bindings: bindings{},
helpFormatter: DefaultHelpValueFormatter,
Exit: os.Exit,
Stdout: os.Stdout,
Stderr: os.Stderr,
registry: NewRegistry().RegisterDefaults(),
vars: Vars{},
bindings: bindings{},
helpFormatter: DefaultHelpValueFormatter,
ignoreFieldsRegex: make([]*regexp.Regexp, 0),
}

options = append(options, Bind(k))
Expand Down
62 changes: 62 additions & 0 deletions kong_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1297,3 +1297,65 @@ func TestHydratePointerCommands(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &cmd{Flag: true}, cli.Cmd)
}

// nolint
type testIgnoreFields struct {
Foo struct {
Bar bool
Sub struct {
SubFlag1 bool `kong:"name=subflag1"`
XXX_SubFlag2 bool `kong:"name=subflag2"`
} `kong:"cmd"`
} `kong:"cmd"`
XXX_Baz struct {
Boo bool
} `kong:"cmd,name=baz"`
}

func TestIgnoreRegex(t *testing.T) {
cli := testIgnoreFields{}

k, err := kong.New(&cli, kong.IgnoreFieldsRegex(`.*\.XXX_.+`))
require.NoError(t, err)

_, err = k.Parse([]string{"foo", "sub"})
require.NoError(t, err)

_, err = k.Parse([]string{"foo", "sub", "--subflag1"})
require.NoError(t, err)

_, err = k.Parse([]string{"foo", "sub", "--subflag2"})
require.Error(t, err)
require.Contains(t, err.Error(), "unknown flag --subflag2")

_, err = k.Parse([]string{"baz"})
require.Error(t, err)
require.Contains(t, err.Error(), "unexpected argument baz")
}

// Verify that passing a nil regex will work
func TestIgnoreRegexEmpty(t *testing.T) {
cli := testIgnoreFields{}

_, err := kong.New(&cli, kong.IgnoreFieldsRegex(""))
require.Error(t, err)
require.Contains(t, "regex input cannot be empty", err.Error())
}

type optionWithErr struct{}

func (o *optionWithErr) Apply(k *kong.Kong) error {
return errors.New("option returned err")
}

func TestOptionReturnsErr(t *testing.T) {
cli := struct {
Test bool
}{}

optWithError := &optionWithErr{}

_, err := kong.New(cli, optWithError)
require.Error(t, err)
require.Equal(t, "option returned err", err.Error())
}
26 changes: 26 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os/user"
"path/filepath"
"reflect"
"regexp"
"strings"

"github.com/pkg/errors"
Expand Down Expand Up @@ -319,6 +320,31 @@ func Resolvers(resolvers ...Resolver) Option {
})
}

// IgnoreFieldsRegex will cause kong.New() to skip field names that match any
// of the provided regex patterns. This is useful if you are not able to add a
// kong="-" struct tag to a struct/element before the call to New.
//
// Example: When referencing protoc generated structs, you will likely want to
// ignore/skip XXX_* fields.
func IgnoreFieldsRegex(regexes ...string) Option {
return OptionFunc(func(k *Kong) error {
for _, r := range regexes {
if r == "" {
return errors.New("regex input cannot be empty")
}

re, err := regexp.Compile(r)
if err != nil {
return errors.Wrap(err, "unable to compile regex")
}

k.ignoreFieldsRegex = append(k.ignoreFieldsRegex, re)
}

return nil
})
}

// ConfigurationLoader is a function that builds a resolver from a file.
type ConfigurationLoader func(r io.Reader) (Resolver, error)

Expand Down

0 comments on commit 07faa2e

Please sign in to comment.