Support for flag prefixing.

This commit is contained in:
Alec Thomas
2018-09-12 12:17:57 +10:00
parent 844494faff
commit fd197e5081
5 changed files with 40 additions and 6 deletions
+3 -1
View File
@@ -395,7 +395,9 @@ Both can coexist with standard Tag parsing.
| `hidden` | If present, command or flag is hidden. | | `hidden` | If present, command or flag is hidden. |
| `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. |
| `enum:"X,Y,..."` | | `enum:"X,Y,..."` | Set of valid values allowed for this flag. |
| `group:"X"` | Logical group for a flag or command. |
| `prefix:"X"` | Prefix for all sub-flags. |
## Variable interpolation ## Variable interpolation
+18 -5
View File
@@ -38,20 +38,30 @@ func dashedString(s string) string {
type flattenedField struct { type flattenedField struct {
field reflect.StructField field reflect.StructField
value reflect.Value value reflect.Value
tag *Tag
} }
func flattenedFields(v reflect.Value) (out []flattenedField) { func flattenedFields(v reflect.Value) (out []flattenedField) {
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
ft := v.Type().Field(i) ft := v.Type().Field(i)
fv := v.Field(i) fv := v.Field(i)
tag := parseTag(fv, ft)
if ft.Anonymous { if ft.Anonymous {
out = append(out, flattenedFields(fv)...) sub := flattenedFields(fv)
// Assign parent group to children, if they're not otherwise set.
for _, subf := range sub {
if subf.tag.Group == "" {
subf.tag.Group = tag.Group
}
subf.tag.Prefix = tag.Prefix + subf.tag.Prefix
}
out = append(out, sub...)
continue continue
} }
if !fv.CanSet() { if !fv.CanSet() {
continue continue
} }
out = append(out, flattenedField{field: ft, value: fv}) out = append(out, flattenedField{field: ft, value: fv, tag: tag})
} }
return return
} }
@@ -65,11 +75,12 @@ func buildNode(k *Kong, v reflect.Value, typ NodeType, seenFlags map[string]bool
ft := field.field ft := field.field
fv := field.value fv := field.value
tag := parseTag(fv, ft) tag := field.tag
name := tag.Name name := tag.Name
if name == "" { if name == "" {
name = strings.ToLower(dashedString(ft.Name)) name = tag.Prefix + strings.ToLower(dashedString(ft.Name))
} else {
name = tag.Prefix + name
} }
// Nested structs are either commands or args, unless they implement the Mapper interface. // Nested structs are either commands or args, unless they implement the Mapper interface.
@@ -108,6 +119,7 @@ func buildChild(k *Kong, node *Node, typ NodeType, v reflect.Value, ft reflect.S
child.Parent = node child.Parent = node
child.Help = tag.Help child.Help = tag.Help
child.Hidden = tag.Hidden child.Hidden = tag.Hidden
child.Group = tag.Group
if fv.Type().Implements(helpProviderType) { if fv.Type().Implements(helpProviderType) {
child.Detail = fv.Interface().(HelpProvider).Help() child.Detail = fv.Interface().(HelpProvider).Help()
@@ -176,6 +188,7 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
Short: tag.Short, Short: tag.Short,
PlaceHolder: tag.PlaceHolder, PlaceHolder: tag.PlaceHolder,
Env: tag.Env, Env: tag.Env,
Group: tag.Group,
} }
value.Flag = flag value.Flag = flag
node.Flags = append(node.Flags, flag) node.Flags = append(node.Flags, flag)
+13
View File
@@ -577,3 +577,16 @@ func TestBoolOverride(t *testing.T) {
_, err = p.Parse([]string{"--flag", "false"}) _, err = p.Parse([]string{"--flag", "false"})
require.Error(t, err) require.Error(t, err)
} }
func TestAnonymousPrefix(t *testing.T) {
type Anonymous struct {
Flag string
}
var cli struct {
Anonymous `prefix:"anon-"`
}
p := mustNew(t, &cli)
_, err := p.Parse([]string{"--anon-flag=moo"})
require.NoError(t, err)
require.Equal(t, "moo", cli.Flag)
}
+2
View File
@@ -37,6 +37,7 @@ type Node struct {
Name string Name string
Help string // Short help displayed in summaries. Help string // Short help displayed in summaries.
Detail string // Detailed help displayed when describing command/arg alone. Detail string // Detailed help displayed when describing command/arg alone.
Group string
Hidden bool Hidden bool
Flags []*Flag Flags []*Flag
Positional []*Positional Positional []*Positional
@@ -284,6 +285,7 @@ type Positional = Value
// A Flag represents a command-line flag. // A Flag represents a command-line flag.
type Flag struct { type Flag struct {
*Value *Value
Group string // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
PlaceHolder string PlaceHolder string
Env string Env string
Short rune Short rune
+4
View File
@@ -25,6 +25,8 @@ type Tag struct {
Hidden bool Hidden bool
Sep rune Sep rune
Enum string Enum string
Group string
Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
// Storage for all tag keys for arbitrary lookups. // Storage for all tag keys for arbitrary lookups.
items map[string]string items map[string]string
@@ -137,6 +139,8 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
t.Hidden = t.Has("hidden") t.Hidden = t.Has("hidden")
t.Format = t.Get("format") t.Format = t.Get("format")
t.Sep, _ = t.GetRune("sep") t.Sep, _ = t.GetRune("sep")
t.Group = t.Get("group")
t.Prefix = t.Get("prefix")
if t.Sep == 0 { if t.Sep == 0 {
if t.Get("sep") == "none" { if t.Get("sep") == "none" {
t.Sep = -1 t.Sep = -1