diff --git a/README.md b/README.md index 36d3daf..53ef9cb 100644 --- a/README.md +++ b/README.md @@ -690,7 +690,7 @@ The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context. It will then use that file name to initialize the yaml input source for any flags that are defined on that command. As a note the "load" flag used would also have to be defined on the command flags in order -for this code snipped to work. +for this code snippet to work. Currently only YAML, JSON, and TOML files are supported but developers can add support for other input sources by implementing the altsrc.InputSourceContext for their diff --git a/command_test.go b/command_test.go index f43b4ae..89f497c 100644 --- a/command_test.go +++ b/command_test.go @@ -61,8 +61,13 @@ func TestParseAndRunShortOpts(t *testing.T) { {testArgs: args{"foo", "test", "-af"}, expectedErr: nil, expectedArgs: &args{}}, {testArgs: args{"foo", "test", "-cf"}, expectedErr: nil, expectedArgs: &args{}}, {testArgs: args{"foo", "test", "-acf"}, expectedErr: nil, expectedArgs: &args{}}, + {testArgs: args{"foo", "test", "--acf"}, expectedErr: errors.New("flag provided but not defined: -acf"), expectedArgs: nil}, {testArgs: args{"foo", "test", "-invalid"}, expectedErr: errors.New("flag provided but not defined: -invalid"), expectedArgs: nil}, + {testArgs: args{"foo", "test", "-acf", "-invalid"}, expectedErr: errors.New("flag provided but not defined: -invalid"), expectedArgs: nil}, + {testArgs: args{"foo", "test", "--invalid"}, expectedErr: errors.New("flag provided but not defined: -invalid"), expectedArgs: nil}, + {testArgs: args{"foo", "test", "-acf", "--invalid"}, expectedErr: errors.New("flag provided but not defined: -invalid"), expectedArgs: nil}, {testArgs: args{"foo", "test", "-acf", "arg1", "-invalid"}, expectedErr: nil, expectedArgs: &args{"arg1", "-invalid"}}, + {testArgs: args{"foo", "test", "-acf", "arg1", "--invalid"}, expectedErr: nil, expectedArgs: &args{"arg1", "--invalid"}}, {testArgs: args{"foo", "test", "-acfi", "not-arg", "arg1", "-invalid"}, expectedErr: nil, expectedArgs: &args{"arg1", "-invalid"}}, {testArgs: args{"foo", "test", "-i", "ivalue"}, expectedErr: nil, expectedArgs: &args{}}, {testArgs: args{"foo", "test", "-i", "ivalue", "arg1"}, expectedErr: nil, expectedArgs: &args{"arg1"}}, @@ -84,7 +89,7 @@ func TestParseAndRunShortOpts(t *testing.T) { &BoolFlag{Name: "abc", Aliases: []string{"a"}}, &BoolFlag{Name: "cde", Aliases: []string{"c"}}, &BoolFlag{Name: "fgh", Aliases: []string{"f"}}, - &StringFlag{Name: "ijk", Aliases:[]string{"i"}}, + &StringFlag{Name: "ijk", Aliases: []string{"i"}}, }, } diff --git a/parse.go b/parse.go index 2c2005c..660f538 100644 --- a/parse.go +++ b/parse.go @@ -22,28 +22,35 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error { } errStr := err.Error() - trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ") + trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -") if errStr == trimmed { return err } // regenerate the initial args with the split short opts - newArgs := []string{} + argsWereSplit := false for i, arg := range args { - if arg != trimmed { - newArgs = append(newArgs, arg) + // skip args that are not part of the error message + if name := strings.TrimLeft(arg, "-"); name != trimmed { continue } - shortOpts := splitShortOptions(set, trimmed) + // if we can't split, the error was accurate + shortOpts := splitShortOptions(set, arg) if len(shortOpts) == 1 { return err } - // add each short option and all remaining arguments - newArgs = append(newArgs, shortOpts...) - newArgs = append(newArgs, args[i+1:]...) - args = newArgs + // swap current argument with the split version + args = append(args[:i], append(shortOpts, args[i+1:]...)...) + argsWereSplit = true + break + } + + // This should be an impossible to reach code path, but in case the arg + // splitting failed to happen, this will prevent infinite loops + if !argsWereSplit { + return err } // Since custom parsing failed, replace the flag set before retrying