diff --git a/README.md b/README.md index ba8275b..22e00f0 100644 --- a/README.md +++ b/README.md @@ -566,7 +566,7 @@ Both can coexist with standard Tag parsing. | `default:"1"` | On a command, make it the default. | | `default:"withargs"` | On a command, make it the default and allow args/flags from that command | | `short:"X"` | Short name, if flag. | -| `aliases:"X,Y"` | One or more aliases (for cmd). | +| `aliases:"X,Y"` | One or more aliases (for cmd or flag). | | `required:""` | If present, flag/arg is required. | | `optional:""` | If present, flag/arg is optional. | | `hidden:""` | If present, command or flag is hidden. | diff --git a/build.go b/build.go index e23c115..a52d90f 100644 --- a/build.go +++ b/build.go @@ -296,6 +296,13 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv return failField(v, ft, "duplicate flag --%s", value.Name) } seenFlags["--"+value.Name] = true + for _, alias := range tag.Aliases { + aliasFlag := "--" + alias + if seenFlags[aliasFlag] { + return failField(v, ft, "duplicate flag %s", aliasFlag) + } + seenFlags[aliasFlag] = true + } if tag.Short != 0 { if seenFlags["-"+string(tag.Short)] { return failField(v, ft, "duplicate short flag -%c", tag.Short) @@ -304,6 +311,7 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv } flag := &Flag{ Value: value, + Aliases: tag.Aliases, Short: tag.Short, PlaceHolder: tag.PlaceHolder, Envs: tag.Envs, diff --git a/context.go b/context.go index 4350608..b2bfea6 100644 --- a/context.go +++ b/context.go @@ -684,15 +684,24 @@ func flipBoolValue(value reflect.Value) error { func (c *Context) parseFlag(flags []*Flag, match string) (err error) { candidates := []string{} + for _, flag := range flags { long := "--" + flag.Name - short := "-" + string(flag.Short) - neg := "--no-" + flag.Name + matched := long == match candidates = append(candidates, long) if flag.Short != 0 { + short := "-" + string(flag.Short) + matched = matched || (short == match) candidates = append(candidates, short) } - if short != match && long != match && !(match == neg && flag.Tag.Negatable) { + for _, alias := range flag.Aliases { + alias = "--" + alias + matched = matched || (alias == match) + candidates = append(candidates, alias) + } + + neg := "--no-" + flag.Name + if !matched && !(match == neg && flag.Tag.Negatable) { continue } // Found a matching flag. diff --git a/kong_test.go b/kong_test.go index 650cf2a..3e71979 100644 --- a/kong_test.go +++ b/kong_test.go @@ -518,6 +518,16 @@ func TestShort(t *testing.T) { assert.Equal(t, "hello", cli.String) } +func TestAlias(t *testing.T) { + var cli struct { + String string `aliases:"str"` + } + app := mustNew(t, &cli) + _, err := app.Parse([]string{"--str", "hello"}) + assert.NoError(t, err) + assert.Equal(t, "hello", cli.String) +} + func TestDuplicateFlagChoosesLast(t *testing.T) { var cli struct { Flag int @@ -1321,6 +1331,24 @@ func TestDuplicateShortflags(t *testing.T) { assert.EqualError(t, err, ".Flag2: duplicate short flag -t") } +func TestDuplicateAliases(t *testing.T) { + cli1 := struct { + Flag1 string `aliases:"flag"` + Flag2 string `aliases:"flag"` + }{} + _, err := kong.New(&cli1) + assert.EqualError(t, err, ".Flag2: duplicate flag --flag") +} + +func TestDuplicateAliasLong(t *testing.T) { + cli2 := struct { + Flag string `` + Flag2 string `aliases:"flag"` // duplicates Flag + }{} + _, err := kong.New(&cli2) + assert.EqualError(t, err, ".Flag2: duplicate flag --flag") +} + func TestDuplicateNestedShortFlags(t *testing.T) { cli := struct { Flag1 bool `short:"t"` diff --git a/model.go b/model.go index eeb547c..4e59fea 100644 --- a/model.go +++ b/model.go @@ -397,6 +397,7 @@ type Flag struct { Xor []string PlaceHolder string Envs []string + Aliases []string Short rune Hidden bool Negated bool