Much more thorough checking of enum values.

This commit is contained in:
Alec Thomas
2019-10-11 16:55:10 +11:00
parent 9a1f499155
commit 77a613fb8b
2 changed files with 41 additions and 6 deletions
+31 -6
View File
@@ -135,13 +135,10 @@ func (c *Context) Empty() bool {
func (c *Context) Validate() error {
err := Visit(c.Model, func(node Visitable, next Next) error {
if value, ok := node.(*Value); ok {
if value.Enum != "" && !value.EnumMap()[fmt.Sprintf("%v", value.Target.Interface())] {
enums := []string{}
for enum := range value.EnumMap() {
enums = append(enums, fmt.Sprintf("%q", enum))
if value.Enum != "" {
if err := checkEnum(value, value.Target); err != nil {
return err
}
sort.Strings(enums)
return fmt.Errorf("%s must be one of %s but got %q", value.ShortSummary(), strings.Join(enums, ","), value.Target.Interface())
}
}
return next(nil)
@@ -646,6 +643,34 @@ func checkMissingPositionals(positional int, values []*Value) error {
return fmt.Errorf("missing positional arguments %s", strings.Join(missing, " "))
}
func checkEnum(value *Value, target reflect.Value) error {
switch target.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < target.Len(); i++ {
if err := checkEnum(value, target.Index(i)); err != nil {
return err
}
}
return nil
case reflect.Map, reflect.Struct:
return errors.Errorf("enum can only be applied to a slice or value")
default:
enumMap := value.EnumMap()
v := fmt.Sprintf("%v", target)
if enumMap[v] {
return nil
}
enums := []string{}
for enum := range enumMap {
enums = append(enums, fmt.Sprintf("%q", enum))
}
sort.Strings(enums)
return fmt.Errorf("%s must be one of %s but got %q", value.ShortSummary(), strings.Join(enums, ","), target.Interface())
}
}
func checkXorDuplicates(paths []*Path) error {
for _, path := range paths {
seen := map[string]*Flag{}
+10
View File
@@ -781,3 +781,13 @@ func TestXorChild(t *testing.T) {
_, err = p.Parse([]string{"--two=hi", "cmd", "--three"})
require.Error(t, err, "--two and --three can't be used together")
}
func TestEnumSequence(t *testing.T) {
var cli struct {
State []string `enum:"a,b,c" default:"a"`
}
p := mustNew(t, &cli)
_, err := p.Parse(nil)
require.NoError(t, err)
require.Equal(t, []string{"a"}, cli.State)
}