Merge pull request #116 from mpobrien/unknown_handler
Add field for callback to handle unknown/unmapped options during parse
diff --git a/parser.go b/parser.go
index 5a8ec92..a417ba9 100644
--- a/parser.go
+++ b/parser.go
@@ -24,6 +24,13 @@
// NamespaceDelimiter separates group namespaces and option long names
NamespaceDelimiter string
+ // UnknownOptionsHandler is a function which gets called when the parser
+ // encounters an unknown option. The function receives the unknown option
+ // name and the remaining command line arguments.
+ // It should return a new list of remaining arguments to continue parsing,
+ // or an error to indicate a parse failure.
+ UnknownOptionHandler func(option string, args []string) ([]string, error)
+
internalError error
}
@@ -204,13 +211,22 @@
ignoreUnknown := (p.Options & IgnoreUnknown) != None
parseErr := wrapError(err)
- if !(parseErr.Type == ErrUnknownFlag && ignoreUnknown) {
+ if parseErr.Type != ErrUnknownFlag || (!ignoreUnknown && p.UnknownOptionHandler == nil) {
s.err = parseErr
break
}
if ignoreUnknown {
s.addArgs(arg)
+ } else if p.UnknownOptionHandler != nil {
+ modifiedArgs, err := p.UnknownOptionHandler(optname, s.args)
+
+ if err != nil {
+ s.err = err
+ break
+ }
+
+ s.args = modifiedArgs
}
}
}
diff --git a/parser_test.go b/parser_test.go
index 3026b33..bba7576 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -1,6 +1,7 @@
package flags
import (
+ "fmt"
"os"
"reflect"
"strconv"
@@ -356,3 +357,54 @@
}
}
}
+
+func TestUnknownFlagHandler(t *testing.T) {
+
+ var opts struct {
+ Flag1 string `long:"flag1"`
+ Flag2 string `long:"flag2"`
+ }
+
+ p := NewParser(&opts, None)
+
+ var unknownFlag1 string
+ var unknownFlag2 bool
+
+ // Set up a callback to intercept unknown options during parsing
+ p.UnknownOptionHandler = func(option string, args []string) ([]string, error) {
+ if option == "unknownFlag1" {
+ // consume a value from remaining args list
+ unknownFlag1 = args[0]
+ return args[1:], nil
+ } else if option == "unknownFlag2" {
+ // treat this one as a bool switch, don't consume any args
+ unknownFlag2 = true
+ return args, nil
+ }
+
+ return args, fmt.Errorf("Unknown flag: %v", option)
+ }
+
+ // Parse args containing some unknown flags, verify that
+ // our callback can handle all of them
+ _, err := p.ParseArgs([]string{"--flag1=stuff", "--unknownFlag1", "blah", "--unknownFlag2", "--flag2=foo"})
+
+ if err != nil {
+ assertErrorf(t, "Parser returned unexpected error %v", err)
+ }
+
+ assertString(t, opts.Flag1, "stuff")
+ assertString(t, opts.Flag2, "foo")
+ assertString(t, unknownFlag1, "blah")
+
+ if !unknownFlag2 {
+ assertErrorf(t, "Flag should have been set by unknown handler, but had value: %v", unknownFlag2)
+ }
+
+ // Parse args with unknown flags that callback doesn't handle, verify it returns error
+ _, err = p.ParseArgs([]string{"--flag1=stuff", "--unknownFlagX", "blah", "--flag2=foo"})
+
+ if err == nil {
+ assertErrorf(t, "Parser should have returned error, but returned nil")
+ }
+}