Add negatable tag to set a bool to be negatable.
If negatable, add `--[no-]` prefix to help.
This commit is contained in:
@@ -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
@@ -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))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 == ' '
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user