diff --git a/README.md b/README.md index e3b9fe9c..5dbed024 100644 --- a/README.md +++ b/README.md @@ -663,8 +663,7 @@ func main() { ## Validation Kong does validation on the structure of a command-line, but also supports -extensible validation. Any node in the tree may implement the following -interface: +extensible validation. Any node in the tree may implement either of the following interfaces: ```go type Validatable interface { @@ -672,6 +671,12 @@ type Validatable interface { } ``` +```go +type Validatable interface { + Validate(kctx *kong.Context) error + } +``` + If one of these nodes is in the active command-line it will be called during normal validation. diff --git a/context.go b/context.go index f174e636..38a2f9b4 100644 --- a/context.go +++ b/context.go @@ -208,7 +208,7 @@ func (c *Context) Validate() error { //nolint: gocyclo desc = node.Path() } if validate := isValidatable(value); validate != nil { - if err := validate.Validate(); err != nil { + if err := validate.Validate(c); err != nil { if desc != "" { return fmt.Errorf("%s: %w", desc, err) } @@ -1094,12 +1094,23 @@ func findPotentialCandidates(needle string, haystack []string, format string, ar } type validatable interface{ Validate() error } +type extendedValidatable interface { + Validate(kctx *Context) error +} + +// Proxy a validatable function to the extendedValidatable interface +type validatableFunc func() error -func isValidatable(v reflect.Value) validatable { +func (f validatableFunc) Validate(kctx *Context) error { return f() } + +func isValidatable(v reflect.Value) extendedValidatable { if !v.IsValid() || (v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice || v.Kind() == reflect.Map) && v.IsNil() { return nil } if validate, ok := v.Interface().(validatable); ok { + return validatableFunc(validate.Validate) + } + if validate, ok := v.Interface().(extendedValidatable); ok { return validate } if v.CanAddr() { diff --git a/kong_test.go b/kong_test.go index 9547dd49..e0efac80 100644 --- a/kong_test.go +++ b/kong_test.go @@ -1466,6 +1466,19 @@ func TestValidateArg(t *testing.T) { assert.EqualError(t, err, ": flag error") } +type extendedValidateFlag string + +func (v *extendedValidateFlag) Validate(kctx *kong.Context) error { return errors.New("flag error") } + +func TestExtendedValidateFlag(t *testing.T) { + cli := struct { + Flag extendedValidateFlag + }{} + p := mustNew(t, &cli) + _, err := p.Parse([]string{"--flag=one"}) + assert.EqualError(t, err, "--flag: flag error") +} + func TestPointers(t *testing.T) { cli := struct { Mapped *mappedValue