feat: support optionally passing kong.Context to Validate()

Fixes #340
This commit is contained in:
Alec Thomas
2024-11-03 14:14:55 +11:00
parent 64229c9fe7
commit 1b9d57eec1
3 changed files with 33 additions and 4 deletions
+7 -2
View File
@@ -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.
+13 -2
View File
@@ -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
}
func isValidatable(v reflect.Value) validatable {
// Proxy a validatable function to the extendedValidatable interface
type validatableFunc func() error
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() {
+13
View File
@@ -1466,6 +1466,19 @@ func TestValidateArg(t *testing.T) {
assert.EqualError(t, err, "<arg>: 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