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.
|
`required` | If present, flag/arg is required.
|
||||||
`optional` | If present, flag/arg is optional.
|
`optional` | If present, flag/arg is optional.
|
||||||
`hidden` | If present, command or flag is hidden.
|
`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.
|
`format:"X"` | Format for parsing input, if supported.
|
||||||
`sep:"X"` | Separator for sequences (defaults to ","). May be `none` to disable splitting.
|
`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.
|
`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.
|
// Found a matching flag.
|
||||||
c.scan.Pop()
|
c.scan.Pop()
|
||||||
if flag.Value.IsBool() && match == neg {
|
if match == neg && flag.Tag.Negatable {
|
||||||
flag.Negated = true
|
flag.Negated = true
|
||||||
}
|
}
|
||||||
err := flag.Parse(c.scan, c.getValue(flag.Value))
|
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)
|
flagString += fmt.Sprintf("-%c, --%s", flag.Short, name)
|
||||||
} else {
|
} else {
|
||||||
if haveShort {
|
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 {
|
} 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 {
|
if !isBool {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func TestHelp(t *testing.T) {
|
|||||||
Slice []string `help:"A slice of strings." placeholder:"STR"`
|
Slice []string `help:"A slice of strings." placeholder:"STR"`
|
||||||
Map map[string]int `help:"A map of strings to ints."`
|
Map map[string]int `help:"A map of strings to ints."`
|
||||||
Required bool `required help:"A required flag."`
|
Required bool `required help:"A required flag."`
|
||||||
|
Sort bool `negatable help:"Is sortable or not."`
|
||||||
|
|
||||||
One struct {
|
One struct {
|
||||||
Flag string `help:"Nested flag."`
|
Flag string `help:"Nested flag."`
|
||||||
@@ -75,6 +76,7 @@ Flags:
|
|||||||
--slice=STR,... A slice of strings.
|
--slice=STR,... A slice of strings.
|
||||||
--map=KEY=VALUE;... A map of strings to ints.
|
--map=KEY=VALUE;... A map of strings to ints.
|
||||||
--required A required flag.
|
--required A required flag.
|
||||||
|
--[no-]sort Is sortable or not.
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
one --required
|
one --required
|
||||||
@@ -115,6 +117,7 @@ Flags:
|
|||||||
--slice=STR,... A slice of strings.
|
--slice=STR,... A slice of strings.
|
||||||
--map=KEY=VALUE;... A map of strings to ints.
|
--map=KEY=VALUE;... A map of strings to ints.
|
||||||
--required A required flag.
|
--required A required flag.
|
||||||
|
--[no-]sort Is sortable or not.
|
||||||
|
|
||||||
--flag=STRING Nested flag under two.
|
--flag=STRING Nested flag under two.
|
||||||
--required-two
|
--required-two
|
||||||
|
|||||||
+12
-1
@@ -359,7 +359,7 @@ func TestTraceErrorPartiallySucceeds(t *testing.T) {
|
|||||||
func TestNegatedBooleanFlag(t *testing.T) {
|
func TestNegatedBooleanFlag(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Cmd struct {
|
Cmd struct {
|
||||||
Flag bool `kong:"default='true'"`
|
Flag bool `kong:"default='true',negatable"`
|
||||||
} `kong:"cmd"`
|
} `kong:"cmd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +369,17 @@ func TestNegatedBooleanFlag(t *testing.T) {
|
|||||||
require.Equal(t, false, cli.Cmd.Flag)
|
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 {
|
type hookContext struct {
|
||||||
cmd bool
|
cmd bool
|
||||||
values []string
|
values []string
|
||||||
|
|||||||
@@ -227,7 +227,6 @@ type Value struct {
|
|||||||
Tag *Tag
|
Tag *Tag
|
||||||
Target reflect.Value
|
Target reflect.Value
|
||||||
Required bool
|
Required bool
|
||||||
Negated bool
|
|
||||||
Set bool // Set to true when this value is set through some mechanism.
|
Set bool // Set to true when this value is set through some mechanism.
|
||||||
Format string // Formatting directive, if applicable.
|
Format string // Formatting directive, if applicable.
|
||||||
Position int // Position (for positional arguments).
|
Position int // Position (for positional arguments).
|
||||||
@@ -370,6 +369,7 @@ type Flag struct {
|
|||||||
Env string
|
Env string
|
||||||
Short rune
|
Short rune
|
||||||
Hidden bool
|
Hidden bool
|
||||||
|
Negated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Flag) String() string {
|
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.
|
Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
|
||||||
Embed bool
|
Embed bool
|
||||||
Aliases []string
|
Aliases []string
|
||||||
|
Negatable bool
|
||||||
|
|
||||||
// Storage for all tag keys for arbitrary lookups.
|
// Storage for all tag keys for arbitrary lookups.
|
||||||
items map[string][]string
|
items map[string][]string
|
||||||
@@ -158,6 +159,11 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
|
|||||||
t.Xor = t.Get("xor")
|
t.Xor = t.Get("xor")
|
||||||
t.Prefix = t.Get("prefix")
|
t.Prefix = t.Get("prefix")
|
||||||
t.Embed = t.Has("embed")
|
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 {
|
splitFn := func(r rune) bool {
|
||||||
return r == ',' || r == ' '
|
return r == ',' || r == ' '
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user