Created
March 18, 2011 22:53
-
-
Save op/876997 to your computer and use it in GitHub Desktop.
go flag parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -r f487d74ff495 src/pkg/flag/export_test.go | |
--- a/src/pkg/flag/export_test.go Thu Feb 10 23:01:45 2011 +0800 | |
+++ b/src/pkg/flag/export_test.go Fri Feb 18 15:24:20 2011 +0800 | |
@@ -12,7 +12,7 @@ | |
// After calling ResetForTesting, parse errors in flag handling will panic rather | |
// than exit the program. | |
func ResetForTesting(usage func()) { | |
- flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} | |
+ flags = NewParser() | |
Usage = usage | |
panicOnError = true | |
} | |
diff -r f487d74ff495 src/pkg/flag/flag.go | |
--- a/src/pkg/flag/flag.go Thu Feb 10 23:01:45 2011 +0800 | |
+++ b/src/pkg/flag/flag.go Fri Feb 18 15:24:20 2011 +0800 | |
@@ -62,6 +62,14 @@ | |
} | |
os.Args = flag.Args() | |
flag.Parse() | |
+ | |
+ A flag parser can be created separatley from the global flags and can | |
+ be fed with arguments other than os.Args and will not exit the program in | |
+ case of errors when parsing the flags. | |
+ | |
+ var parser = flag.NewParser() | |
+ var ip *int = parser.Int("flagname", 1234, "help message for flagname") | |
+ parser.Parse() | |
*/ | |
package flag | |
@@ -71,6 +79,42 @@ | |
"strconv" | |
) | |
+// Errors introduced by this package. | |
+ | |
+var ( | |
+ errIllegalSyntax = os.NewError("flag: illegal flag syntax") | |
+ errInvalidBooleanValue = os.NewError("flag: invalid boolean value") | |
+ errInvalidValue = os.NewError("flag: invalid value") | |
+ errMissingArgument = os.NewError("flag: requires an argument") | |
+ errUndefinedFlag = os.NewError("flag: undefined flag") | |
+) | |
+ | |
+type parseError struct { | |
+ os.Error | |
+ name string | |
+ value string | |
+} | |
+ | |
+func newParseError(error os.Error, name string, value string) os.Error { | |
+ return &parseError{error, name, value} | |
+} | |
+ | |
+func (err *parseError) String() string { | |
+ switch err.Error.String() { | |
+ case errIllegalSyntax.String(): | |
+ return "bad flag syntax: " + err.name | |
+ case errInvalidBooleanValue.String(): | |
+ return fmt.Sprintf("invalid boolean value %q for flag: -%s", err.value, err.name) | |
+ case errInvalidValue.String(): | |
+ return fmt.Sprintf("invalid value %q for flag: -%s", err.value, err.name) | |
+ case errMissingArgument.String(): | |
+ return "flag needs an argument: -" + err.name | |
+ case errUndefinedFlag.String(): | |
+ return "flag provided but not defined: -" + err.name | |
+ } | |
+ panic("unknown flag parse error") | |
+} | |
+ | |
// -- Bool Value | |
type boolValue bool | |
@@ -197,16 +241,22 @@ | |
DefValue string // default value (as text); for usage message | |
} | |
-type allFlags struct { | |
+ | |
+// Parser represents a flag parser. | |
+type Parser struct { | |
actual map[string]*Flag | |
formal map[string]*Flag | |
args []string // arguments after flags | |
} | |
-var flags *allFlags | |
+var flags *Parser | |
// VisitAll visits the flags, calling fn for each. It visits all flags, even those not set. | |
func VisitAll(fn func(*Flag)) { | |
+ flags.VisitAll(fn) | |
+} | |
+ | |
+func (flags *Parser) VisitAll(fn func(*Flag)) { | |
for _, f := range flags.formal { | |
fn(f) | |
} | |
@@ -214,6 +264,10 @@ | |
// Visit visits the flags, calling fn for each. It visits only those flags that have been set. | |
func Visit(fn func(*Flag)) { | |
+ flags.Visit(fn) | |
+} | |
+ | |
+func (flags *Parser) Visit(fn func(*Flag)) { | |
for _, f := range flags.actual { | |
fn(f) | |
} | |
@@ -221,12 +275,20 @@ | |
// Lookup returns the Flag structure of the named flag, returning nil if none exists. | |
func Lookup(name string) *Flag { | |
+ return flags.Lookup(name) | |
+} | |
+ | |
+func (flags *Parser) Lookup(name string) *Flag { | |
return flags.formal[name] | |
} | |
// Set sets the value of the named flag. It returns true if the set succeeded; false if | |
// there is no such flag defined. | |
func Set(name, value string) bool { | |
+ return flags.Set(name, value) | |
+} | |
+ | |
+func (flags *Parser) Set(name, value string) bool { | |
f, ok := flags.formal[name] | |
if !ok { | |
return false | |
@@ -241,7 +303,11 @@ | |
// PrintDefaults prints to standard error the default values of all defined flags. | |
func PrintDefaults() { | |
- VisitAll(func(f *Flag) { | |
+ flags.PrintDefaults() | |
+} | |
+ | |
+func (flags *Parser) PrintDefaults() { | |
+ flags.VisitAll(func(f *Flag) { | |
format := " -%s=%s: %s\n" | |
if _, ok := f.Value.(*stringValue); ok { | |
// put quotes on the value | |
@@ -268,11 +334,14 @@ | |
os.Exit(2) | |
} | |
-func NFlag() int { return len(flags.actual) } | |
+func NFlag() int { return flags.NFlag() } | |
+func (flags *Parser) NFlag() int { return len(flags.actual) } | |
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument | |
// after flags have been processed. | |
-func Arg(i int) string { | |
+func Arg(i int) string { return flags.Arg(i) } | |
+ | |
+func (flags *Parser) Arg(i int) string { | |
if i < 0 || i >= len(flags.args) { | |
return "" | |
} | |
@@ -280,62 +349,96 @@ | |
} | |
// NArg is the number of arguments remaining after flags have been processed. | |
-func NArg() int { return len(flags.args) } | |
+func NArg() int { return flags.NArg() } | |
+func (flags *Parser) NArg() int { return len(flags.args) } | |
// Args returns the non-flag command-line arguments. | |
-func Args() []string { return flags.args } | |
+func Args() []string { return flags.Args() } | |
+func (flags *Parser) Args() []string { return flags.args } | |
// BoolVar defines a bool flag with specified name, default value, and usage string. | |
// The argument p points to a bool variable in which to store the value of the flag. | |
func BoolVar(p *bool, name string, value bool, usage string) { | |
- Var(newBoolValue(value, p), name, usage) | |
+ flags.BoolVar(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) BoolVar(p *bool, name string, value bool, usage string) { | |
+ flags.Var(newBoolValue(value, p), name, usage) | |
} | |
// Bool defines a bool flag with specified name, default value, and usage string. | |
// The return value is the address of a bool variable that stores the value of the flag. | |
func Bool(name string, value bool, usage string) *bool { | |
+ return flags.Bool(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Bool(name string, value bool, usage string) *bool { | |
p := new(bool) | |
- BoolVar(p, name, value, usage) | |
+ flags.BoolVar(p, name, value, usage) | |
return p | |
} | |
// IntVar defines an int flag with specified name, default value, and usage string. | |
// The argument p points to an int variable in which to store the value of the flag. | |
func IntVar(p *int, name string, value int, usage string) { | |
- Var(newIntValue(value, p), name, usage) | |
+ flags.IntVar(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) IntVar(p *int, name string, value int, usage string) { | |
+ flags.Var(newIntValue(value, p), name, usage) | |
} | |
// Int defines an int flag with specified name, default value, and usage string. | |
// The return value is the address of an int variable that stores the value of the flag. | |
func Int(name string, value int, usage string) *int { | |
+ return flags.Int(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Int(name string, value int, usage string) *int { | |
p := new(int) | |
- IntVar(p, name, value, usage) | |
+ flags.IntVar(p, name, value, usage) | |
return p | |
} | |
// Int64Var defines an int64 flag with specified name, default value, and usage string. | |
// The argument p points to an int64 variable in which to store the value of the flag. | |
func Int64Var(p *int64, name string, value int64, usage string) { | |
- Var(newInt64Value(value, p), name, usage) | |
+ flags.Int64Var(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Int64Var(p *int64, name string, value int64, usage string) { | |
+ flags.Var(newInt64Value(value, p), name, usage) | |
} | |
// Int64 defines an int64 flag with specified name, default value, and usage string. | |
// The return value is the address of an int64 variable that stores the value of the flag. | |
func Int64(name string, value int64, usage string) *int64 { | |
+ return flags.Int64(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Int64(name string, value int64, usage string) *int64 { | |
p := new(int64) | |
- Int64Var(p, name, value, usage) | |
+ flags.Int64Var(p, name, value, usage) | |
return p | |
} | |
// UintVar defines a uint flag with specified name, default value, and usage string. | |
// The argument p points to a uint variable in which to store the value of the flag. | |
func UintVar(p *uint, name string, value uint, usage string) { | |
- Var(newUintValue(value, p), name, usage) | |
+ flags.UintVar(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) UintVar(p *uint, name string, value uint, usage string) { | |
+ flags.Var(newUintValue(value, p), name, usage) | |
} | |
// Uint defines a uint flag with specified name, default value, and usage string. | |
// The return value is the address of a uint variable that stores the value of the flag. | |
func Uint(name string, value uint, usage string) *uint { | |
+ return flags.Uint(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Uint(name string, value uint, usage string) *uint { | |
p := new(uint) | |
UintVar(p, name, value, usage) | |
return p | |
@@ -344,48 +447,76 @@ | |
// Uint64Var defines a uint64 flag with specified name, default value, and usage string. | |
// The argument p points to a uint64 variable in which to store the value of the flag. | |
func Uint64Var(p *uint64, name string, value uint64, usage string) { | |
- Var(newUint64Value(value, p), name, usage) | |
+ flags.Uint64Var(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Uint64Var(p *uint64, name string, value uint64, usage string) { | |
+ flags.Var(newUint64Value(value, p), name, usage) | |
} | |
// Uint64 defines a uint64 flag with specified name, default value, and usage string. | |
// The return value is the address of a uint64 variable that stores the value of the flag. | |
func Uint64(name string, value uint64, usage string) *uint64 { | |
+ return flags.Uint64(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Uint64(name string, value uint64, usage string) *uint64 { | |
p := new(uint64) | |
- Uint64Var(p, name, value, usage) | |
+ flags.Uint64Var(p, name, value, usage) | |
return p | |
} | |
// StringVar defines a string flag with specified name, default value, and usage string. | |
// The argument p points to a string variable in which to store the value of the flag. | |
func StringVar(p *string, name, value string, usage string) { | |
- Var(newStringValue(value, p), name, usage) | |
+ flags.StringVar(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) StringVar(p *string, name, value string, usage string) { | |
+ flags.Var(newStringValue(value, p), name, usage) | |
} | |
// String defines a string flag with specified name, default value, and usage string. | |
// The return value is the address of a string variable that stores the value of the flag. | |
func String(name, value string, usage string) *string { | |
+ return flags.String(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) String(name, value string, usage string) *string { | |
p := new(string) | |
- StringVar(p, name, value, usage) | |
+ flags.StringVar(p, name, value, usage) | |
return p | |
} | |
// Float64Var defines a float64 flag with specified name, default value, and usage string. | |
// The argument p points to a float64 variable in which to store the value of the flag. | |
func Float64Var(p *float64, name string, value float64, usage string) { | |
- Var(newFloat64Value(value, p), name, usage) | |
+ flags.Float64Var(p, name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Float64Var(p *float64, name string, value float64, usage string) { | |
+ flags.Var(newFloat64Value(value, p), name, usage) | |
} | |
// Float64 defines a float64 flag with specified name, default value, and usage string. | |
// The return value is the address of a float64 variable that stores the value of the flag. | |
func Float64(name string, value float64, usage string) *float64 { | |
+ return flags.Float64(name, value, usage) | |
+} | |
+ | |
+func (flags *Parser) Float64(name string, value float64, usage string) *float64 { | |
p := new(float64) | |
- Float64Var(p, name, value, usage) | |
+ flags.Float64Var(p, name, value, usage) | |
return p | |
} | |
// Var defines a user-typed flag with specified name, default value, and usage string. | |
// The argument p points to a Value variable in which to store the value of the flag. | |
func Var(value Value, name string, usage string) { | |
+ flags.Var(value, name, usage) | |
+} | |
+ | |
+func (flags *Parser) Var(value Value, name string, usage string) { | |
// Remember the default value as a string; it won't change. | |
f := &Flag{name, usage, value, value.String()} | |
_, alreadythere := flags.formal[name] | |
@@ -397,30 +528,29 @@ | |
} | |
-func (f *allFlags) parseOne() (ok bool) { | |
- if len(f.args) == 0 { | |
- return false | |
+func (flags *Parser) parseOne() (ok bool, err os.Error) { | |
+ if len(flags.args) == 0 { | |
+ return false, nil | |
} | |
- s := f.args[0] | |
+ s := flags.args[0] | |
if len(s) == 0 || s[0] != '-' || len(s) == 1 { | |
- return false | |
+ return false, nil | |
} | |
num_minuses := 1 | |
if s[1] == '-' { | |
num_minuses++ | |
if len(s) == 2 { // "--" terminates the flags | |
- f.args = f.args[1:] | |
- return false | |
+ flags.args = flags.args[1:] | |
+ return false, nil | |
} | |
} | |
name := s[num_minuses:] | |
if len(name) == 0 || name[0] == '-' || name[0] == '=' { | |
- fmt.Fprintln(os.Stderr, "bad flag syntax:", s) | |
- fail() | |
+ return false, newParseError(errIllegalSyntax, s, "") | |
} | |
// it's a flag. does it have an argument? | |
- f.args = f.args[1:] | |
+ flags.args = flags.args[1:] | |
has_value := false | |
value := "" | |
for i := 1; i < len(name); i++ { // equals cannot be first | |
@@ -434,47 +564,66 @@ | |
m := flags.formal | |
flag, alreadythere := m[name] // BUG | |
if !alreadythere { | |
- fmt.Fprintf(os.Stderr, "flag provided but not defined: -%s\n", name) | |
- fail() | |
+ return false, newParseError(errUndefinedFlag, name, value) | |
} | |
if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg | |
if has_value { | |
if !fv.Set(value) { | |
- fmt.Fprintf(os.Stderr, "invalid boolean value %q for flag: -%s\n", value, name) | |
- fail() | |
+ return false, newParseError(errInvalidBooleanValue, name, value) | |
} | |
} else { | |
fv.Set("true") | |
} | |
} else { | |
// It must have a value, which might be the next argument. | |
- if !has_value && len(f.args) > 0 { | |
+ if !has_value && len(flags.args) > 0 { | |
// value is the next arg | |
has_value = true | |
- value, f.args = f.args[0], f.args[1:] | |
+ value, flags.args = flags.args[0], flags.args[1:] | |
} | |
if !has_value { | |
- fmt.Fprintf(os.Stderr, "flag needs an argument: -%s\n", name) | |
- fail() | |
+ return false, newParseError(errMissingArgument, name, value) | |
} | |
ok = flag.Value.Set(value) | |
if !ok { | |
- fmt.Fprintf(os.Stderr, "invalid value %q for flag: -%s\n", value, name) | |
- fail() | |
+ return false, newParseError(errInvalidValue, name, value) | |
} | |
} | |
flags.actual[name] = flag | |
- return true | |
+ return true, nil | |
} | |
+// NewParser creates a new flag parser, completely separate from the global | |
+// flag parser. | |
+func NewParser() (*Parser) { | |
+ return &Parser{make(map[string]*Flag), make(map[string]*Flag), nil} | |
+} | |
+ | |
+ | |
+func (f *Parser) Parse(args []string) (err os.Error) { | |
+ var ok bool | |
+ f.args = args | |
+ for { | |
+ ok, err = f.parseOne() | |
+ if ok != true || err != nil { | |
+ break | |
+ } | |
+ } | |
+ return | |
+} | |
+ | |
+ | |
// Parse parses the command-line flags. Must be called after all flags are defined | |
// and before any are accessed by the program. | |
func Parse() { | |
- flags.args = os.Args[1:] | |
- for flags.parseOne() { | |
+ err := flags.Parse(os.Args[1:]) | |
+ if err != nil { | |
+ fmt.Fprintln(os.Stderr, err.String()) | |
+ fail() | |
} | |
} | |
+ | |
func init() { | |
- flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} | |
+ flags = NewParser() | |
} | |
diff -r f487d74ff495 src/pkg/flag/flag_test.go | |
--- a/src/pkg/flag/flag_test.go Thu Feb 10 23:01:45 2011 +0800 | |
+++ b/src/pkg/flag/flag_test.go Fri Feb 18 15:24:20 2011 +0800 | |
@@ -192,3 +192,16 @@ | |
t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) | |
} | |
} | |
+ | |
+func TestFlagSeparation(t *testing.T) { | |
+ ResetForTesting(func() { t.Fatal("bad parse") }) | |
+ parser := NewParser() | |
+ args := []string{"cmd", "-foo", "args"} | |
+ fooGlobal := Bool("foo", false, "") | |
+ fooLocal := parser.Bool("foo", false, "") | |
+ err := parser.Parse(args[1:]) | |
+ | |
+ if err != nil || *fooGlobal == true || *fooLocal != true { | |
+ t.Fatalf("expected nil false true got %v %v %v", err, *fooGlobal, *fooLocal) | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment