aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRajat Jindal <[email protected]>2018-03-30 04:51:55 -0700
committerEric Paris <[email protected]>2018-03-30 07:51:55 -0400
commit1cd4a0c365d95803411bec89fb7b76bade17053b (patch)
tree0418221a2cc2b117d883454319b4eae09781a191
parentad68c28ee799163e627e77fcc6e8ecaa866e3535 (diff)
add ability to ignore unknown flags (#160)
* add ability to ignore unknown flags * add testcases * add 2 more patterns to the testcase * handle --unknownflag=val arg and -u=val arg scenario * add ParseErrorsWhiteList to extend error handling during parsing
-rw-r--r--flag.go63
-rw-r--r--flag_test.go78
2 files changed, 134 insertions, 7 deletions
diff --git a/flag.go b/flag.go
index 7f460b5..f0acbb8 100644
--- a/flag.go
+++ b/flag.go
@@ -123,6 +123,12 @@ const (
PanicOnError
)
+// ParseErrorsWhitelist defines the parsing errors that can be ignored
+type ParseErrorsWhitelist struct {
+ // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
+ UnknownFlags bool
+}
+
// NormalizedName is a flag name that has been normalized according to rules
// for the FlagSet (e.g. making '-' and '_' equivalent).
type NormalizedName string
@@ -138,6 +144,9 @@ type FlagSet struct {
// help/usage messages.
SortFlags bool
+ // ParseErrorsWhitelist is used to configure a whitelist of errors
+ ParseErrorsWhitelist ParseErrorsWhitelist
+
name string
parsed bool
actual map[NormalizedName]*Flag
@@ -899,6 +908,25 @@ func (f *FlagSet) usage() {
}
}
+//--unknown (args will be empty)
+//--unknown --next-flag ... (args will be --next-flag ...)
+//--unknown arg ... (args will be arg ...)
+func stripUnknownFlagValue(args []string) []string {
+ if len(args) == 0 {
+ //--unknown
+ return args
+ }
+
+ first := args[0]
+ if first[0] == '-' {
+ //--unknown --next-flag ...
+ return args
+ }
+
+ //--unknown arg ... (args will be arg ...)
+ return args[1:]
+}
+
func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
a = args
name := s[2:]
@@ -910,13 +938,24 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
split := strings.SplitN(name, "=", 2)
name = split[0]
flag, exists := f.formal[f.normalizeFlagName(name)]
+
if !exists {
- if name == "help" { // special case for nice help message.
+ switch {
+ case name == "help":
f.usage()
return a, ErrHelp
+ case f.ParseErrorsWhitelist.UnknownFlags:
+ // --unknown=unknownval arg ...
+ // we do not want to lose arg in this case
+ if len(split) >= 2 {
+ return a, nil
+ }
+
+ return stripUnknownFlagValue(a), nil
+ default:
+ err = f.failf("unknown flag: --%s", name)
+ return
}
- err = f.failf("unknown flag: --%s", name)
- return
}
var value string
@@ -954,13 +993,25 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
flag, exists := f.shorthands[c]
if !exists {
- if c == 'h' { // special case for nice help message.
+ switch {
+ case c == 'h':
f.usage()
err = ErrHelp
return
+ case f.ParseErrorsWhitelist.UnknownFlags:
+ // '-f=arg arg ...'
+ // we do not want to lose arg in this case
+ if len(shorthands) > 2 && shorthands[1] == '=' {
+ outShorts = ""
+ return
+ }
+
+ outArgs = stripUnknownFlagValue(outArgs)
+ return
+ default:
+ err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
+ return
}
- err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
- return
}
var value string
diff --git a/flag_test.go b/flag_test.go
index d587752..3c13de3 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -389,7 +389,78 @@ func testParseAll(f *FlagSet, t *testing.T) {
}
if !reflect.DeepEqual(got, want) {
t.Errorf("f.ParseAll() fail to restore the args")
- t.Errorf("Got: %v", got)
+ t.Errorf("Got: %v", got)
+ t.Errorf("Want: %v", want)
+ }
+}
+
+func testParseWithUnknownFlags(f *FlagSet, t *testing.T) {
+ if f.Parsed() {
+ t.Error("f.Parse() = true before Parse")
+ }
+ f.ParseErrorsWhitelist.UnknownFlags = true
+
+ f.BoolP("boola", "a", false, "bool value")
+ f.BoolP("boolb", "b", false, "bool2 value")
+ f.BoolP("boolc", "c", false, "bool3 value")
+ f.BoolP("boold", "d", false, "bool4 value")
+ f.BoolP("boole", "e", false, "bool4 value")
+ f.StringP("stringa", "s", "0", "string value")
+ f.StringP("stringz", "z", "0", "string value")
+ f.StringP("stringx", "x", "0", "string value")
+ f.StringP("stringy", "y", "0", "string value")
+ f.StringP("stringo", "o", "0", "string value")
+ f.Lookup("stringx").NoOptDefVal = "1"
+ args := []string{
+ "-ab",
+ "-cs=xx",
+ "--stringz=something",
+ "--unknown1",
+ "unknown1Value",
+ "-d=true",
+ "-x",
+ "--unknown2=unknown2Value",
+ "-u=unknown3Value",
+ "-p",
+ "unknown4Value",
+ "-q", //another unknown with bool value
+ "-y",
+ "ee",
+ "--unknown7=unknown7value",
+ "--stringo=ovalue",
+ "--unknown8=unknown8value",
+ "--boole",
+ "--unknown6",
+ }
+ want := []string{
+ "boola", "true",
+ "boolb", "true",
+ "boolc", "true",
+ "stringa", "xx",
+ "stringz", "something",
+ "boold", "true",
+ "stringx", "1",
+ "stringy", "ee",
+ "stringo", "ovalue",
+ "boole", "true",
+ }
+ got := []string{}
+ store := func(flag *Flag, value string) error {
+ got = append(got, flag.Name)
+ if len(value) > 0 {
+ got = append(got, value)
+ }
+ return nil
+ }
+ if err := f.ParseAll(args, store); err != nil {
+ t.Errorf("expected no error, got %s", err)
+ }
+ if !f.Parsed() {
+ t.Errorf("f.Parse() = false after Parse")
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("f.ParseAll() fail to restore the args")
+ t.Errorf("Got: %v", got)
t.Errorf("Want: %v", want)
}
}
@@ -500,6 +571,11 @@ func TestParseAll(t *testing.T) {
testParseAll(GetCommandLine(), t)
}
+func TestIgnoreUnknownFlags(t *testing.T) {
+ ResetForTesting(func() { t.Error("bad parse") })
+ testParseWithUnknownFlags(GetCommandLine(), t)
+}
+
func TestFlagSetParse(t *testing.T) {
testParse(NewFlagSet("test", ContinueOnError), t)
}