Add ability to ignore fields that match given regex pattern(s) (#199)
This commit is contained in:
@@ -24,6 +24,7 @@ func build(k *Kong, ast interface{}) (app *Application, err error) {
|
|||||||
for _, flag := range extraFlags {
|
for _, flag := range extraFlags {
|
||||||
seenFlags[flag.Name] = true
|
seenFlags[flag.Name] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := buildNode(k, iv, ApplicationNode, seenFlags)
|
node, err := buildNode(k, iv, ApplicationNode, seenFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -112,7 +113,15 @@ func buildNode(k *Kong, v reflect.Value, typ NodeType, seenFlags map[string]bool
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MAIN:
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
|
for _, r := range k.ignoreFieldsRegex {
|
||||||
|
if r.MatchString(v.Type().Name() + "." + field.field.Name) {
|
||||||
|
continue MAIN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ft := field.field
|
ft := field.field
|
||||||
fv := field.value
|
fv := field.value
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,10 +49,11 @@ type Kong struct {
|
|||||||
Stdout io.Writer
|
Stdout io.Writer
|
||||||
Stderr io.Writer
|
Stderr io.Writer
|
||||||
|
|
||||||
bindings bindings
|
bindings bindings
|
||||||
loader ConfigurationLoader
|
loader ConfigurationLoader
|
||||||
resolvers []Resolver
|
resolvers []Resolver
|
||||||
registry *Registry
|
registry *Registry
|
||||||
|
ignoreFieldsRegex []*regexp.Regexp
|
||||||
|
|
||||||
noDefaultHelp bool
|
noDefaultHelp bool
|
||||||
usageOnError usageOnError
|
usageOnError usageOnError
|
||||||
@@ -73,13 +75,14 @@ type Kong struct {
|
|||||||
// See the README (https://github.com/alecthomas/kong) for usage instructions.
|
// See the README (https://github.com/alecthomas/kong) for usage instructions.
|
||||||
func New(grammar interface{}, options ...Option) (*Kong, error) {
|
func New(grammar interface{}, options ...Option) (*Kong, error) {
|
||||||
k := &Kong{
|
k := &Kong{
|
||||||
Exit: os.Exit,
|
Exit: os.Exit,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
Stderr: os.Stderr,
|
Stderr: os.Stderr,
|
||||||
registry: NewRegistry().RegisterDefaults(),
|
registry: NewRegistry().RegisterDefaults(),
|
||||||
vars: Vars{},
|
vars: Vars{},
|
||||||
bindings: bindings{},
|
bindings: bindings{},
|
||||||
helpFormatter: DefaultHelpValueFormatter,
|
helpFormatter: DefaultHelpValueFormatter,
|
||||||
|
ignoreFieldsRegex: make([]*regexp.Regexp, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
options = append(options, Bind(k))
|
options = append(options, Bind(k))
|
||||||
|
|||||||
@@ -1297,3 +1297,65 @@ func TestHydratePointerCommands(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, &cmd{Flag: true}, cli.Cmd)
|
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
@@ -6,6 +6,7 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -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.
|
// ConfigurationLoader is a function that builds a resolver from a file.
|
||||||
type ConfigurationLoader func(r io.Reader) (Resolver, error)
|
type ConfigurationLoader func(r io.Reader) (Resolver, error)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user