From f4fe688b0983077cffe5baf8f4f62dfe196bffe9 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Sun, 22 May 2011 09:22:00 +1000 Subject: [PATCH] flags: allow distinct sets of flags. A FlagSet is an independent set of flags that may be used, for example, to provide flag processing for subcommands in a CLI. The standard, os.Args-derived set of flags is a global but non-exported FlagSet and the standard functions are wrappers for methods of that FlagSet. Allow the programmer to control whether the program exits if there is a parse error. For the default set, the behavior remains to exit on error. The handling of Usage is odd due to backward compatibility. R=golang-dev, bradfitz, r, bradfitz CC=golang-dev https://golang.org/cl/4517092 --- src/pkg/flag/export_test.go | 22 +- src/pkg/flag/flag.go | 408 ++++++++++++++++++++++++++---------- src/pkg/flag/flag_test.go | 59 +++--- 3 files changed, 340 insertions(+), 149 deletions(-) diff --git a/src/pkg/flag/export_test.go b/src/pkg/flag/export_test.go index b5e3243b310..7b190807a8a 100644 --- a/src/pkg/flag/export_test.go +++ b/src/pkg/flag/export_test.go @@ -9,24 +9,14 @@ import "os" // Additional routines compiled into the package only during testing. // ResetForTesting clears all flag state and sets the usage function as directed. -// After calling ResetForTesting, parse errors in flag handling will panic rather -// than exit the program. +// After calling ResetForTesting, parse errors in flag handling will not +// exit the program. func ResetForTesting(usage func()) { - flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} + commandLine = NewFlagSet(os.Args[0], ContinueOnError) Usage = usage - panicOnError = true } -// ParseForTesting parses the flag state using the provided arguments. It -// should be called after 1) ResetForTesting and 2) setting up the new flags. -// The return value reports whether the parse was error-free. -func ParseForTesting(args []string) (result bool) { - defer func() { - if recover() != nil { - result = false - } - }() - os.Args = args - Parse() - return true +// CommandLine returns the default FlagSet. +func CommandLine() *FlagSet { + return commandLine } diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 062d4a52f0e..ec254fd888d 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -50,18 +50,12 @@ Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. - It is safe to call flag.Parse multiple times, possibly after changing - os.Args. This makes it possible to implement command lines with - subcommands that enable additional flags, as in: - - flag.Bool(...) // global options - flag.Parse() // parse leading command - subcmd := flag.Arg(0) - switch subcmd { - // add per-subcommand options - } - os.Args = flag.Args() - flag.Parse() + The default set of command-line flags is controlled by + top-level functions. The FlagSet type allows one to define + independent sets of flags, such as to implement subcommands + in a command-line interface. The methods of FlagSet are + analogous to the top-level functions for the command-line + flag set. */ package flag @@ -190,6 +184,30 @@ type Value interface { Set(string) bool } +// ErrorHandling defines how to handle flag parsing errors. +type ErrorHandling int + +const ( + ContinueOnError ErrorHandling = iota + ExitOnError + PanicOnError +) + +// A FlagSet represents a set of defined flags. +type FlagSet struct { + // Usage is the function called when an error occurs while parsing flags. + // The field is a function (not a method) that may be changed to point to + // a custom error handler. + Usage func() + + name string + actual map[string]*Flag + formal map[string]*Flag + args []string // arguments after flags + exitOnError bool // does the program exit if there's an error? + errorHandling ErrorHandling +} + // A Flag represents the state of a flag. type Flag struct { Name string // name as it appears on command line @@ -198,14 +216,6 @@ type Flag struct { DefValue string // default value (as text); for usage message } -type allFlags struct { - actual map[string]*Flag - formal map[string]*Flag - args []string // arguments after flags -} - -var flags *allFlags - // sortFlags returns the flags as a slice in lexicographical sorted order. func sortFlags(flags map[string]*Flag) []*Flag { list := make(sort.StringArray, len(flags)) @@ -224,43 +234,67 @@ func sortFlags(flags map[string]*Flag) []*Flag { // VisitAll visits the flags in lexicographical order, calling fn for each. // It visits all flags, even those not set. -func VisitAll(fn func(*Flag)) { - for _, f := range sortFlags(flags.formal) { - fn(f) +func (f *FlagSet) VisitAll(fn func(*Flag)) { + for _, flag := range sortFlags(f.formal) { + fn(flag) } } +// VisitAll visits the command-line flags in lexicographical order, calling +// fn for each. It visits all flags, even those not set. +func VisitAll(fn func(*Flag)) { + commandLine.VisitAll(fn) +} + // Visit visits the flags in lexicographical order, calling fn for each. // It visits only those flags that have been set. -func Visit(fn func(*Flag)) { - for _, f := range sortFlags(flags.actual) { - fn(f) +func (f *FlagSet) Visit(fn func(*Flag)) { + for _, flag := range sortFlags(f.actual) { + fn(flag) } } +// Visit visits the command-line flags in lexicographical order, calling fn +// for each. It visits only those flags that have been set. +func Visit(fn func(*Flag)) { + commandLine.Visit(fn) +} + // Lookup returns the Flag structure of the named flag, returning nil if none exists. +func (f *FlagSet) Lookup(name string) *Flag { + return f.formal[name] +} + +// Lookup returns the Flag structure of the named command-line flag, +// returning nil if none exists. func Lookup(name string) *Flag { - return flags.formal[name] + return commandLine.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 { - f, ok := flags.formal[name] +func (f *FlagSet) Set(name, value string) bool { + flag, ok := f.formal[name] if !ok { return false } - ok = f.Value.Set(value) + ok = flag.Value.Set(value) if !ok { return false } - flags.actual[name] = f + f.actual[name] = flag return true } -// PrintDefaults prints to standard error the default values of all defined flags. -func PrintDefaults() { - VisitAll(func(f *Flag) { +// Set sets the value of the named command-line flag. It returns true if the +// set succeeded; false if there is no such flag defined. +func Set(name, value string) bool { + return commandLine.Set(name, value) +} + +// PrintDefaults prints to standard error the default values of all defined flags in the set. +func (f *FlagSet) PrintDefaults() { + f.VisitAll(func(f *Flag) { format := " -%s=%s: %s\n" if _, ok := f.Value.(*stringValue); ok { // put quotes on the value @@ -270,141 +304,255 @@ func PrintDefaults() { }) } -// Usage prints to standard error a default usage message documenting all defined flags. -// The function is a variable that may be changed to point to a custom function. -var Usage = func() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - PrintDefaults() +// PrintDefaults prints to standard error the default values of all defined command-line flags. +func PrintDefaults() { + commandLine.PrintDefaults() } -var panicOnError = false +// defaultUsage is the default function to print a usage message. +func defaultUsage(f *FlagSet) { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", f.name) + f.PrintDefaults() +} -// failf prints to standard error a formatted error and Usage, and then exits the program. -func failf(format string, a ...interface{}) { - fmt.Fprintf(os.Stderr, format, a...) - Usage() - if panicOnError { - panic("flag parse error") - } - os.Exit(2) +// Usage prints to standard error a usage message documenting all defined command-line flags. +// The function is a variable that may be changed to point to a custom function. +var Usage = func() { + defaultUsage(commandLine) } // NFlag returns the number of flags that have been set. -func NFlag() int { return len(flags.actual) } +func (f *FlagSet) NFlag() int { return len(f.actual) } + +// NFlag returns the number of command-line flags that have been set. +func NFlag() int { return len(commandLine.actual) } + +// Arg returns the i'th argument. Arg(0) is the first remaining argument +// after flags have been processed. +func (f *FlagSet) Arg(i int) string { + if i < 0 || i >= len(f.args) { + return "" + } + return f.args[i] +} // 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 { - if i < 0 || i >= len(flags.args) { - return "" - } - return flags.args[i] + return commandLine.Arg(i) } // NArg is the number of arguments remaining after flags have been processed. -func NArg() int { return len(flags.args) } +func (f *FlagSet) NArg() int { return len(f.args) } + +// NArg is the number of arguments remaining after flags have been processed. +func NArg() int { return len(commandLine.args) } + +// Args returns the non-flag arguments. +func (f *FlagSet) Args() []string { return f.args } // Args returns the non-flag command-line arguments. -func Args() []string { return flags.args } +func Args() []string { return commandLine.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 (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { + f.Var(newBoolValue(value, p), name, usage) +} // 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) + commandLine.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 (f *FlagSet) Bool(name string, value bool, usage string) *bool { + p := new(bool) + f.BoolVar(p, name, value, usage) + return p } // 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 { - p := new(bool) - BoolVar(p, name, value, usage) - return p + return commandLine.Bool(name, value, usage) +} + +// 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 (f *FlagSet) IntVar(p *int, name string, value int, usage string) { + f.Var(newIntValue(value, p), name, usage) } // 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) + commandLine.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 (f *FlagSet) Int(name string, value int, usage string) *int { + p := new(int) + f.IntVar(p, name, value, usage) + return p } // 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 { - p := new(int) - IntVar(p, name, value, usage) - return p + return commandLine.Int(name, value, usage) +} + +// 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 (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { + f.Var(newInt64Value(value, p), name, usage) } // 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) + commandLine.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 (f *FlagSet) Int64(name string, value int64, usage string) *int64 { + p := new(int64) + f.Int64Var(p, name, value, usage) + return p } // 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 { - p := new(int64) - Int64Var(p, name, value, usage) - return p + return commandLine.Int64(name, value, usage) } // 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 (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { + f.Var(newUintValue(value, p), name, usage) +} + +// 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) + commandLine.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 { +// The return value is the address of a uint variable that stores the value of the flag. +func (f *FlagSet) Uint(name string, value uint, usage string) *uint { p := new(uint) - UintVar(p, name, value, usage) + f.UintVar(p, name, value, usage) return p } +// 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 commandLine.Uint(name, value, usage) +} + +// 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 (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { + f.Var(newUint64Value(value, p), name, usage) +} + // 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) + commandLine.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 (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { + p := new(uint64) + f.Uint64Var(p, name, value, usage) + return p } // 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 { - p := new(uint64) - Uint64Var(p, name, value, usage) - return p + return commandLine.Uint64(name, value, usage) } // 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) +func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { + f.Var(newStringValue(value, p), name, usage) +} + +// 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 string, value string, usage string) { + commandLine.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 { +func (f *FlagSet) String(name string, value string, usage string) *string { p := new(string) - StringVar(p, name, value, usage) + f.StringVar(p, name, value, usage) return p } +// 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 string, value string, usage string) *string { + return commandLine.String(name, value, usage) +} + +// 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 (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { + f.Var(newFloat64Value(value, p), name, usage) +} + // 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) + commandLine.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 { +func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { p := new(float64) - Float64Var(p, name, value, usage) + f.Float64Var(p, name, value, usage) return p } +// Float64 defines an int 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 commandLine.Float64(name, value, usage) +} + +// Var defines a flag with the specified name and usage string. The type and +// value of the flag are represented by the first argument, of type Value, which +// typically holds a user-defined implementation of Value. For instance, the +// caller could create a flag that turns a comma-separated string into a slice +// of strings by giving the slice the methods of Value; in particular, Set would +// decompose the comma-separated string into the slice. +func (f *FlagSet) Var(value Value, name string, usage string) { + // Remember the default value as a string; it won't change. + flag := &Flag{name, usage, value, value.String()} + _, alreadythere := f.formal[name] + if alreadythere { + fmt.Fprintf(os.Stderr, "%s flag redefined: %s\n", f.name, name) + panic("flag redefinition") // Happens only if flags are declared with identical names + } + f.formal[name] = flag +} + // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the @@ -412,36 +560,42 @@ func Float64(name string, value float64, usage string) *float64 { // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func 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] - if alreadythere { - fmt.Fprintln(os.Stderr, "flag redefined:", name) - panic("flag redefinition") // Happens only if flags are declared with identical names - } - flags.formal[name] = f + commandLine.Var(value, name, usage) } +// failf prints to standard error a formatted error and usage message and +// returns the error. +func (f *FlagSet) failf(format string, a ...interface{}) os.Error { + err := fmt.Errorf(format, a...) + fmt.Println(errc) + if f == commandLine { + Usage() + } else { + f.Usage() + } + return err +} -func (f *allFlags) parseOne() (ok bool) { +// parseOne parses one flag. It returns whether a flag was seen. +func (f *FlagSet) parseOne() (bool, os.Error) { if len(f.args) == 0 { - return false + return false, nil } s := f.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 + return false, nil } } name := s[num_minuses:] if len(name) == 0 || name[0] == '-' || name[0] == '=' { - failf("bad flag syntax: %s\n", s) + return false, f.failf("bad flag syntax: %s", s) } // it's a flag. does it have an argument? @@ -456,15 +610,15 @@ func (f *allFlags) parseOne() (ok bool) { break } } - m := flags.formal + m := f.formal flag, alreadythere := m[name] // BUG if !alreadythere { - failf("flag provided but not defined: -%s\n", name) + return false, f.failf("flag provided but not defined: -%s", name) } if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg if has_value { if !fv.Set(value) { - failf("invalid boolean value %q for flag: -%s\n", value, name) + f.failf("invalid boolean value %q for flag: -%s", value, name) } } else { fv.Set("true") @@ -477,25 +631,61 @@ func (f *allFlags) parseOne() (ok bool) { value, f.args = f.args[0], f.args[1:] } if !has_value { - failf("flag needs an argument: -%s\n", name) + return false, f.failf("flag needs an argument: -%s", name) } ok = flag.Value.Set(value) if !ok { - failf("invalid value %q for flag: -%s\n", value, name) + return false, f.failf("invalid value %q for flag: -%s", value, name) } } - flags.actual[name] = flag - return true + f.actual[name] = flag + return true, nil } -// 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() { +// Parse parses flag definitions from the argument list, which should not +// include the command name. Must be called after all flags in the FlagSet +// are defined and before flags are accessed by the program. +func (f *FlagSet) Parse(arguments []string) os.Error { + f.args = arguments + for { + seen, err := f.parseOne() + if seen { + continue + } + if err == nil { + break + } + switch f.errorHandling { + case ContinueOnError: + return err + case ExitOnError: + os.Exit(2) + case PanicOnError: + panic(err) + } } + return nil } -func init() { - flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} +// Parse parses the command-line flags from os.Args[1:]. Must be called +// after all flags are defined and before flags are accessed by the program. +func Parse() { + // Ignore errors; commandLine is set for ExitOnError. + commandLine.Parse(os.Args[1:]) +} + +// The default set of command-line flags, parsed from os.Args. +var commandLine = NewFlagSet(os.Args[0], ExitOnError) + +// NewFlagSet returns a new, empty flag set with the specified name and +// error handling property. +func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { + f := &FlagSet{ + name: name, + actual: make(map[string]*Flag), + formal: make(map[string]*Flag), + errorHandling: errorHandling, + } + f.Usage = func() { defaultUsage(f) } + return f } diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index 1e47d12e48a..fbd706921ef 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -89,7 +89,7 @@ func TestEverything(t *testing.T) { func TestUsage(t *testing.T) { called := false ResetForTesting(func() { called = true }) - if ParseForTesting([]string{"a.out", "-x"}) { + if CommandLine().Parse([]string{"-x"}) == nil { t.Error("parse did not fail for unknown flag") } if !called { @@ -97,19 +97,17 @@ func TestUsage(t *testing.T) { } } -func TestParse(t *testing.T) { - ResetForTesting(func() { t.Error("bad parse") }) - boolFlag := Bool("bool", false, "bool value") - bool2Flag := Bool("bool2", false, "bool2 value") - intFlag := Int("int", 0, "int value") - int64Flag := Int64("int64", 0, "int64 value") - uintFlag := Uint("uint", 0, "uint value") - uint64Flag := Uint64("uint64", 0, "uint64 value") - stringFlag := String("string", "0", "string value") - float64Flag := Float64("float64", 0, "float64 value") +func testParse(f *FlagSet, t *testing.T) { + boolFlag := f.Bool("bool", false, "bool value") + bool2Flag := f.Bool("bool2", false, "bool2 value") + intFlag := f.Int("int", 0, "int value") + int64Flag := f.Int64("int64", 0, "int64 value") + uintFlag := f.Uint("uint", 0, "uint value") + uint64Flag := f.Uint64("uint64", 0, "uint64 value") + stringFlag := f.String("string", "0", "string value") + float64Flag := f.Float64("float64", 0, "float64 value") extra := "one-extra-argument" args := []string{ - "a.out", "-bool", "-bool2=true", "--int", "22", @@ -120,8 +118,8 @@ func TestParse(t *testing.T) { "-float64", "2718e28", extra, } - if !ParseForTesting(args) { - t.Fatal("parse failed") + if err := f.Parse(args); err != nil { + t.Fatal(err) } if *boolFlag != true { t.Error("bool flag should be true, is ", *boolFlag) @@ -147,14 +145,23 @@ func TestParse(t *testing.T) { if *float64Flag != 2718e28 { t.Error("float64 flag should be 2718e28, is ", *float64Flag) } - if len(Args()) != 1 { - t.Error("expected one argument, got", len(Args())) - } else if Args()[0] != extra { - t.Errorf("expected argument %q got %q", extra, Args()[0]) + if len(f.Args()) != 1 { + t.Error("expected one argument, got", len(f.Args())) + } else if f.Args()[0] != extra { + t.Errorf("expected argument %q got %q", extra, f.Args()[0]) } } -// Declare a user-defined flag. +func TestParse(t *testing.T) { + ResetForTesting(func() { t.Error("bad parse") }) + testParse(CommandLine(), t) +} + +func TestFlagSetParse(t *testing.T) { + testParse(NewFlagSet("test", ContinueOnError), t) +} + +// Declare a user-defined flag type. type flagVar []string func (f *flagVar) String() string { @@ -167,11 +174,11 @@ func (f *flagVar) Set(value string) bool { } func TestUserDefined(t *testing.T) { - ResetForTesting(func() { t.Fatal("bad parse") }) + flags := NewFlagSet("test", ContinueOnError) var v flagVar - Var(&v, "v", "usage") - if !ParseForTesting([]string{"a.out", "-v", "1", "-v", "2", "-v=3"}) { - t.Error("parse failed") + flags.Var(&v, "v", "usage") + if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { + t.Error(err) } if len(v) != 3 { t.Fatal("expected 3 args; got ", len(v)) @@ -182,13 +189,17 @@ func TestUserDefined(t *testing.T) { } } +// This tests that one can reset the flags. This still works but not well, and is +// superseded by FlagSet. func TestChangingArgs(t *testing.T) { ResetForTesting(func() { t.Fatal("bad parse") }) oldArgs := os.Args defer func() { os.Args = oldArgs }() os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} before := Bool("before", false, "") - Parse() + if err := CommandLine().Parse(os.Args[1:]); err != nil { + t.Fatal(err) + } cmd := Arg(0) os.Args = Args() after := Bool("after", false, "")