Add negatable tag to set a bool to be negatable.

If negatable, add `--[no-]` prefix to help.
This commit is contained in:
Joe Schmitt
2021-02-18 10:55:55 -05:00
committed by Alec Thomas
parent 2a0d7af9c5
commit 2fdddc4f51
7 changed files with 34 additions and 5 deletions
+1
View File
@@ -440,6 +440,7 @@ Tag | Description
`required` | If present, flag/arg is required.
`optional` | If present, flag/arg is optional.
`hidden` | If present, command or flag is hidden.
`negatable` | If present on a `bool` field, supports prefixing a flag with `--no-` to set flag to `false`
`format:"X"` | Format for parsing input, if supported.
`sep:"X"` | Separator for sequences (defaults to ","). May be `none` to disable splitting.
`mapsep:"X"` | Separator for maps (defaults to ";"). May be `none` to disable splitting.
+1 -1
View File
@@ -652,7 +652,7 @@ func (c *Context) parseFlag(flags []*Flag, match string) (err error) {
}
// Found a matching flag.
c.scan.Pop()
if flag.Value.IsBool() && match == neg {
if match == neg && flag.Tag.Negatable {
flag.Negated = true
}
err := flag.Parse(c.scan, c.getValue(flag.Value))
+10 -2
View File
@@ -472,9 +472,17 @@ func formatFlag(haveShort bool, flag *Flag) string {
flagString += fmt.Sprintf("-%c, --%s", flag.Short, name)
} else {
if haveShort {
flagString += fmt.Sprintf(" --%s", name)
if isBool && flag.Tag.Negatable {
flagString = fmt.Sprintf(" --[no-]%s", name)
} else {
flagString += fmt.Sprintf(" --%s", name)
}
} else {
flagString += fmt.Sprintf("--%s", name)
if isBool && flag.Tag.Negatable {
flagString = fmt.Sprintf("--[no-]%s", name)
} else {
flagString += fmt.Sprintf("--%s", name)
}
}
}
if !isBool {
+3
View File
@@ -29,6 +29,7 @@ func TestHelp(t *testing.T) {
Slice []string `help:"A slice of strings." placeholder:"STR"`
Map map[string]int `help:"A map of strings to ints."`
Required bool `required help:"A required flag."`
Sort bool `negatable help:"Is sortable or not."`
One struct {
Flag string `help:"Nested flag."`
@@ -75,6 +76,7 @@ Flags:
--slice=STR,... A slice of strings.
--map=KEY=VALUE;... A map of strings to ints.
--required A required flag.
--[no-]sort Is sortable or not.
Commands:
one --required
@@ -115,6 +117,7 @@ Flags:
--slice=STR,... A slice of strings.
--map=KEY=VALUE;... A map of strings to ints.
--required A required flag.
--[no-]sort Is sortable or not.
--flag=STRING Nested flag under two.
--required-two
+12 -1
View File
@@ -359,7 +359,7 @@ func TestTraceErrorPartiallySucceeds(t *testing.T) {
func TestNegatedBooleanFlag(t *testing.T) {
var cli struct {
Cmd struct {
Flag bool `kong:"default='true'"`
Flag bool `kong:"default='true',negatable"`
} `kong:"cmd"`
}
@@ -369,6 +369,17 @@ func TestNegatedBooleanFlag(t *testing.T) {
require.Equal(t, false, cli.Cmd.Flag)
}
func TestInvalidNegatedNonBool(t *testing.T) {
var cli struct {
Cmd struct {
Flag string `kong:"negatable"`
} `kong:"cmd"`
}
_, err := kong.New(&cli)
require.Error(t, err)
}
type hookContext struct {
cmd bool
values []string
+1 -1
View File
@@ -227,7 +227,6 @@ type Value struct {
Tag *Tag
Target reflect.Value
Required bool
Negated bool
Set bool // Set to true when this value is set through some mechanism.
Format string // Formatting directive, if applicable.
Position int // Position (for positional arguments).
@@ -370,6 +369,7 @@ type Flag struct {
Env string
Short rune
Hidden bool
Negated bool
}
func (f *Flag) String() string {
+6
View File
@@ -33,6 +33,7 @@ type Tag struct {
Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
Embed bool
Aliases []string
Negatable bool
// Storage for all tag keys for arbitrary lookups.
items map[string][]string
@@ -158,6 +159,11 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
t.Xor = t.Get("xor")
t.Prefix = t.Get("prefix")
t.Embed = t.Has("embed")
negatable := t.Has("negatable")
if negatable && ft.Type.Name() != "bool" {
fail("negatable can only be set on booleans")
}
t.Negatable = negatable
splitFn := func(r rune) bool {
return r == ',' || r == ' '
}