summaryrefslogtreecommitdiffstats
path: root/src/flag
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/flag
parentInitial commit. (diff)
downloadgolang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz
golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/flag')
-rw-r--r--src/flag/example_func_test.go41
-rw-r--r--src/flag/example_test.go85
-rw-r--r--src/flag/example_textvar_test.go35
-rw-r--r--src/flag/example_value_test.go44
-rw-r--r--src/flag/export_test.go24
-rw-r--r--src/flag/flag.go1182
-rw-r--r--src/flag/flag_test.go799
7 files changed, 2210 insertions, 0 deletions
diff --git a/src/flag/example_func_test.go b/src/flag/example_func_test.go
new file mode 100644
index 0000000..7c30c5e
--- /dev/null
+++ b/src/flag/example_func_test.go
@@ -0,0 +1,41 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flag_test
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "net"
+ "os"
+)
+
+func ExampleFunc() {
+ fs := flag.NewFlagSet("ExampleFunc", flag.ContinueOnError)
+ fs.SetOutput(os.Stdout)
+ var ip net.IP
+ fs.Func("ip", "`IP address` to parse", func(s string) error {
+ ip = net.ParseIP(s)
+ if ip == nil {
+ return errors.New("could not parse IP")
+ }
+ return nil
+ })
+ fs.Parse([]string{"-ip", "127.0.0.1"})
+ fmt.Printf("{ip: %v, loopback: %t}\n\n", ip, ip.IsLoopback())
+
+ // 256 is not a valid IPv4 component
+ fs.Parse([]string{"-ip", "256.0.0.1"})
+ fmt.Printf("{ip: %v, loopback: %t}\n\n", ip, ip.IsLoopback())
+
+ // Output:
+ // {ip: 127.0.0.1, loopback: true}
+ //
+ // invalid value "256.0.0.1" for flag -ip: could not parse IP
+ // Usage of ExampleFunc:
+ // -ip IP address
+ // IP address to parse
+ // {ip: <nil>, loopback: false}
+}
diff --git a/src/flag/example_test.go b/src/flag/example_test.go
new file mode 100644
index 0000000..088447d
--- /dev/null
+++ b/src/flag/example_test.go
@@ -0,0 +1,85 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// These examples demonstrate more intricate uses of the flag package.
+package flag_test
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "strings"
+ "time"
+)
+
+// Example 1: A single string flag called "species" with default value "gopher".
+var species = flag.String("species", "gopher", "the species we are studying")
+
+// Example 2: Two flags sharing a variable, so we can have a shorthand.
+// The order of initialization is undefined, so make sure both use the
+// same default value. They must be set up with an init function.
+var gopherType string
+
+func init() {
+ const (
+ defaultGopher = "pocket"
+ usage = "the variety of gopher"
+ )
+ flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
+ flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
+}
+
+// Example 3: A user-defined flag type, a slice of durations.
+type interval []time.Duration
+
+// String is the method to format the flag's value, part of the flag.Value interface.
+// The String method's output will be used in diagnostics.
+func (i *interval) String() string {
+ return fmt.Sprint(*i)
+}
+
+// Set is the method to set the flag value, part of the flag.Value interface.
+// Set's argument is a string to be parsed to set the flag.
+// It's a comma-separated list, so we split it.
+func (i *interval) Set(value string) error {
+ // If we wanted to allow the flag to be set multiple times,
+ // accumulating values, we would delete this if statement.
+ // That would permit usages such as
+ // -deltaT 10s -deltaT 15s
+ // and other combinations.
+ if len(*i) > 0 {
+ return errors.New("interval flag already set")
+ }
+ for _, dt := range strings.Split(value, ",") {
+ duration, err := time.ParseDuration(dt)
+ if err != nil {
+ return err
+ }
+ *i = append(*i, duration)
+ }
+ return nil
+}
+
+// Define a flag to accumulate durations. Because it has a special type,
+// we need to use the Var function and therefore create the flag during
+// init.
+
+var intervalFlag interval
+
+func init() {
+ // Tie the command-line flag to the intervalFlag variable and
+ // set a usage message.
+ flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
+}
+
+func Example() {
+ // All the interesting pieces are with the variables declared above, but
+ // to enable the flag package to see the flags defined there, one must
+ // execute, typically at the start of main (not init!):
+ // flag.Parse()
+ // We don't call it here because this code is a function called "Example"
+ // that is part of the testing suite for the package, which has already
+ // parsed the flags. When viewed at pkg.go.dev, however, the function is
+ // renamed to "main" and it could be run as a standalone example.
+}
diff --git a/src/flag/example_textvar_test.go b/src/flag/example_textvar_test.go
new file mode 100644
index 0000000..8b8cbf6
--- /dev/null
+++ b/src/flag/example_textvar_test.go
@@ -0,0 +1,35 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flag_test
+
+import (
+ "flag"
+ "fmt"
+ "net"
+ "os"
+)
+
+func ExampleTextVar() {
+ fs := flag.NewFlagSet("ExampleTextVar", flag.ContinueOnError)
+ fs.SetOutput(os.Stdout)
+ var ip net.IP
+ fs.TextVar(&ip, "ip", net.IPv4(192, 168, 0, 100), "`IP address` to parse")
+ fs.Parse([]string{"-ip", "127.0.0.1"})
+ fmt.Printf("{ip: %v}\n\n", ip)
+
+ // 256 is not a valid IPv4 component
+ ip = nil
+ fs.Parse([]string{"-ip", "256.0.0.1"})
+ fmt.Printf("{ip: %v}\n\n", ip)
+
+ // Output:
+ // {ip: 127.0.0.1}
+ //
+ // invalid value "256.0.0.1" for flag -ip: invalid IP address: 256.0.0.1
+ // Usage of ExampleTextVar:
+ // -ip IP address
+ // IP address to parse (default 192.168.0.100)
+ // {ip: <nil>}
+}
diff --git a/src/flag/example_value_test.go b/src/flag/example_value_test.go
new file mode 100644
index 0000000..9d464c6
--- /dev/null
+++ b/src/flag/example_value_test.go
@@ -0,0 +1,44 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flag_test
+
+import (
+ "flag"
+ "fmt"
+ "net/url"
+)
+
+type URLValue struct {
+ URL *url.URL
+}
+
+func (v URLValue) String() string {
+ if v.URL != nil {
+ return v.URL.String()
+ }
+ return ""
+}
+
+func (v URLValue) Set(s string) error {
+ if u, err := url.Parse(s); err != nil {
+ return err
+ } else {
+ *v.URL = *u
+ }
+ return nil
+}
+
+var u = &url.URL{}
+
+func ExampleValue() {
+ fs := flag.NewFlagSet("ExampleValue", flag.ExitOnError)
+ fs.Var(&URLValue{u}, "url", "URL to parse")
+
+ fs.Parse([]string{"-url", "https://golang.org/pkg/flag/"})
+ fmt.Printf(`{scheme: %q, host: %q, path: %q}`, u.Scheme, u.Host, u.Path)
+
+ // Output:
+ // {scheme: "https", host: "golang.org", path: "/pkg/flag/"}
+}
diff --git a/src/flag/export_test.go b/src/flag/export_test.go
new file mode 100644
index 0000000..9ef93ed
--- /dev/null
+++ b/src/flag/export_test.go
@@ -0,0 +1,24 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flag
+
+import (
+ "io"
+ "os"
+)
+
+// Additional routines compiled into the package only during testing.
+
+var DefaultUsage = Usage
+
+// ResetForTesting clears all flag state and sets the usage function as directed.
+// After calling ResetForTesting, parse errors in flag handling will not
+// exit the program.
+func ResetForTesting(usage func()) {
+ CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
+ CommandLine.SetOutput(io.Discard)
+ CommandLine.Usage = commandLineUsage
+ Usage = usage
+}
diff --git a/src/flag/flag.go b/src/flag/flag.go
new file mode 100644
index 0000000..ef3cf29
--- /dev/null
+++ b/src/flag/flag.go
@@ -0,0 +1,1182 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package flag implements command-line flag parsing.
+
+# Usage
+
+Define flags using flag.String(), Bool(), Int(), etc.
+
+This declares an integer flag, -n, stored in the pointer nFlag, with type *int:
+
+ import "flag"
+ var nFlag = flag.Int("n", 1234, "help message for flag n")
+
+If you like, you can bind the flag to a variable using the Var() functions.
+
+ var flagvar int
+ func init() {
+ flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
+ }
+
+Or you can create custom flags that satisfy the Value interface (with
+pointer receivers) and couple them to flag parsing by
+
+ flag.Var(&flagVal, "name", "help message for flagname")
+
+For such flags, the default value is just the initial value of the variable.
+
+After all flags are defined, call
+
+ flag.Parse()
+
+to parse the command line into the defined flags.
+
+Flags may then be used directly. If you're using the flags themselves,
+they are all pointers; if you bind to variables, they're values.
+
+ fmt.Println("ip has value ", *ip)
+ fmt.Println("flagvar has value ", flagvar)
+
+After parsing, the arguments following the flags are available as the
+slice flag.Args() or individually as flag.Arg(i).
+The arguments are indexed from 0 through flag.NArg()-1.
+
+# Command line flag syntax
+
+The following forms are permitted:
+
+ -flag
+ --flag // double dashes are also permitted
+ -flag=x
+ -flag x // non-boolean flags only
+
+One or two dashes may be used; they are equivalent.
+The last form is not permitted for boolean flags because the
+meaning of the command
+
+ cmd -x *
+
+where * is a Unix shell wildcard, will change if there is a file
+called 0, false, etc. You must use the -flag=false form to turn
+off a boolean flag.
+
+Flag parsing stops just before the first non-flag argument
+("-" is a non-flag argument) or after the terminator "--".
+
+Integer flags accept 1234, 0664, 0x1234 and may be negative.
+Boolean flags may be:
+
+ 1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
+
+Duration flags accept any input valid for time.ParseDuration.
+
+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
+
+import (
+ "encoding"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// ErrHelp is the error returned if the -help or -h flag is invoked
+// but no such flag is defined.
+var ErrHelp = errors.New("flag: help requested")
+
+// errParse is returned by Set if a flag's value fails to parse, such as with an invalid integer for Int.
+// It then gets wrapped through failf to provide more information.
+var errParse = errors.New("parse error")
+
+// errRange is returned by Set if a flag's value is out of range.
+// It then gets wrapped through failf to provide more information.
+var errRange = errors.New("value out of range")
+
+func numError(err error) error {
+ ne, ok := err.(*strconv.NumError)
+ if !ok {
+ return err
+ }
+ if ne.Err == strconv.ErrSyntax {
+ return errParse
+ }
+ if ne.Err == strconv.ErrRange {
+ return errRange
+ }
+ return err
+}
+
+// -- bool Value
+type boolValue bool
+
+func newBoolValue(val bool, p *bool) *boolValue {
+ *p = val
+ return (*boolValue)(p)
+}
+
+func (b *boolValue) Set(s string) error {
+ v, err := strconv.ParseBool(s)
+ if err != nil {
+ err = errParse
+ }
+ *b = boolValue(v)
+ return err
+}
+
+func (b *boolValue) Get() any { return bool(*b) }
+
+func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) }
+
+func (b *boolValue) IsBoolFlag() bool { return true }
+
+// optional interface to indicate boolean flags that can be
+// supplied without "=value" text
+type boolFlag interface {
+ Value
+ IsBoolFlag() bool
+}
+
+// -- int Value
+type intValue int
+
+func newIntValue(val int, p *int) *intValue {
+ *p = val
+ return (*intValue)(p)
+}
+
+func (i *intValue) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, strconv.IntSize)
+ if err != nil {
+ err = numError(err)
+ }
+ *i = intValue(v)
+ return err
+}
+
+func (i *intValue) Get() any { return int(*i) }
+
+func (i *intValue) String() string { return strconv.Itoa(int(*i)) }
+
+// -- int64 Value
+type int64Value int64
+
+func newInt64Value(val int64, p *int64) *int64Value {
+ *p = val
+ return (*int64Value)(p)
+}
+
+func (i *int64Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
+ if err != nil {
+ err = numError(err)
+ }
+ *i = int64Value(v)
+ return err
+}
+
+func (i *int64Value) Get() any { return int64(*i) }
+
+func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) }
+
+// -- uint Value
+type uintValue uint
+
+func newUintValue(val uint, p *uint) *uintValue {
+ *p = val
+ return (*uintValue)(p)
+}
+
+func (i *uintValue) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, strconv.IntSize)
+ if err != nil {
+ err = numError(err)
+ }
+ *i = uintValue(v)
+ return err
+}
+
+func (i *uintValue) Get() any { return uint(*i) }
+
+func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) }
+
+// -- uint64 Value
+type uint64Value uint64
+
+func newUint64Value(val uint64, p *uint64) *uint64Value {
+ *p = val
+ return (*uint64Value)(p)
+}
+
+func (i *uint64Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
+ if err != nil {
+ err = numError(err)
+ }
+ *i = uint64Value(v)
+ return err
+}
+
+func (i *uint64Value) Get() any { return uint64(*i) }
+
+func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) }
+
+// -- string Value
+type stringValue string
+
+func newStringValue(val string, p *string) *stringValue {
+ *p = val
+ return (*stringValue)(p)
+}
+
+func (s *stringValue) Set(val string) error {
+ *s = stringValue(val)
+ return nil
+}
+
+func (s *stringValue) Get() any { return string(*s) }
+
+func (s *stringValue) String() string { return string(*s) }
+
+// -- float64 Value
+type float64Value float64
+
+func newFloat64Value(val float64, p *float64) *float64Value {
+ *p = val
+ return (*float64Value)(p)
+}
+
+func (f *float64Value) Set(s string) error {
+ v, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ err = numError(err)
+ }
+ *f = float64Value(v)
+ return err
+}
+
+func (f *float64Value) Get() any { return float64(*f) }
+
+func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) }
+
+// -- time.Duration Value
+type durationValue time.Duration
+
+func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
+ *p = val
+ return (*durationValue)(p)
+}
+
+func (d *durationValue) Set(s string) error {
+ v, err := time.ParseDuration(s)
+ if err != nil {
+ err = errParse
+ }
+ *d = durationValue(v)
+ return err
+}
+
+func (d *durationValue) Get() any { return time.Duration(*d) }
+
+func (d *durationValue) String() string { return (*time.Duration)(d).String() }
+
+// -- encoding.TextUnmarshaler Value
+type textValue struct{ p encoding.TextUnmarshaler }
+
+func newTextValue(val encoding.TextMarshaler, p encoding.TextUnmarshaler) textValue {
+ ptrVal := reflect.ValueOf(p)
+ if ptrVal.Kind() != reflect.Ptr {
+ panic("variable value type must be a pointer")
+ }
+ defVal := reflect.ValueOf(val)
+ if defVal.Kind() == reflect.Ptr {
+ defVal = defVal.Elem()
+ }
+ if defVal.Type() != ptrVal.Type().Elem() {
+ panic(fmt.Sprintf("default type does not match variable type: %v != %v", defVal.Type(), ptrVal.Type().Elem()))
+ }
+ ptrVal.Elem().Set(defVal)
+ return textValue{p}
+}
+
+func (v textValue) Set(s string) error {
+ return v.p.UnmarshalText([]byte(s))
+}
+
+func (v textValue) Get() interface{} {
+ return v.p
+}
+
+func (v textValue) String() string {
+ if m, ok := v.p.(encoding.TextMarshaler); ok {
+ if b, err := m.MarshalText(); err == nil {
+ return string(b)
+ }
+ }
+ return ""
+}
+
+// -- func Value
+type funcValue func(string) error
+
+func (f funcValue) Set(s string) error { return f(s) }
+
+func (f funcValue) String() string { return "" }
+
+// Value is the interface to the dynamic value stored in a flag.
+// (The default value is represented as a string.)
+//
+// If a Value has an IsBoolFlag() bool method returning true,
+// the command-line parser makes -name equivalent to -name=true
+// rather than using the next command-line argument.
+//
+// Set is called once, in command line order, for each flag present.
+// The flag package may call the String method with a zero-valued receiver,
+// such as a nil pointer.
+type Value interface {
+ String() string
+ Set(string) error
+}
+
+// Getter is an interface that allows the contents of a Value to be retrieved.
+// It wraps the Value interface, rather than being part of it, because it
+// appeared after Go 1 and its compatibility rules. All Value types provided
+// by this package satisfy the Getter interface, except the type used by Func.
+type Getter interface {
+ Value
+ Get() any
+}
+
+// ErrorHandling defines how FlagSet.Parse behaves if the parse fails.
+type ErrorHandling int
+
+// These constants cause FlagSet.Parse to behave as described if the parse fails.
+const (
+ ContinueOnError ErrorHandling = iota // Return a descriptive error.
+ ExitOnError // Call os.Exit(2) or for -h/-help Exit(0).
+ PanicOnError // Call panic with a descriptive error.
+)
+
+// A FlagSet represents a set of defined flags. The zero value of a FlagSet
+// has no name and has ContinueOnError error handling.
+//
+// Flag names must be unique within a FlagSet. An attempt to define a flag whose
+// name is already in use will cause a panic.
+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. What happens after Usage is called depends
+ // on the ErrorHandling setting; for the command line, this defaults
+ // to ExitOnError, which exits the program after calling Usage.
+ Usage func()
+
+ name string
+ parsed bool
+ actual map[string]*Flag
+ formal map[string]*Flag
+ args []string // arguments after flags
+ errorHandling ErrorHandling
+ output io.Writer // nil means stderr; use Output() accessor
+}
+
+// A Flag represents the state of a flag.
+type Flag struct {
+ Name string // name as it appears on command line
+ Usage string // help message
+ Value Value // value as set
+ DefValue string // default value (as text); for usage message
+}
+
+// sortFlags returns the flags as a slice in lexicographical sorted order.
+func sortFlags(flags map[string]*Flag) []*Flag {
+ result := make([]*Flag, len(flags))
+ i := 0
+ for _, f := range flags {
+ result[i] = f
+ i++
+ }
+ sort.Slice(result, func(i, j int) bool {
+ return result[i].Name < result[j].Name
+ })
+ return result
+}
+
+// Output returns the destination for usage and error messages. os.Stderr is returned if
+// output was not set or was set to nil.
+func (f *FlagSet) Output() io.Writer {
+ if f.output == nil {
+ return os.Stderr
+ }
+ return f.output
+}
+
+// Name returns the name of the flag set.
+func (f *FlagSet) Name() string {
+ return f.name
+}
+
+// ErrorHandling returns the error handling behavior of the flag set.
+func (f *FlagSet) ErrorHandling() ErrorHandling {
+ return f.errorHandling
+}
+
+// SetOutput sets the destination for usage and error messages.
+// If output is nil, os.Stderr is used.
+func (f *FlagSet) SetOutput(output io.Writer) {
+ f.output = output
+}
+
+// VisitAll visits the flags in lexicographical order, calling fn for each.
+// It visits all flags, even those not set.
+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 (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 CommandLine.formal[name]
+}
+
+// Set sets the value of the named flag.
+func (f *FlagSet) Set(name, value string) error {
+ flag, ok := f.formal[name]
+ if !ok {
+ return fmt.Errorf("no such flag -%v", name)
+ }
+ err := flag.Value.Set(value)
+ if err != nil {
+ return err
+ }
+ if f.actual == nil {
+ f.actual = make(map[string]*Flag)
+ }
+ f.actual[name] = flag
+ return nil
+}
+
+// Set sets the value of the named command-line flag.
+func Set(name, value string) error {
+ return CommandLine.Set(name, value)
+}
+
+// isZeroValue determines whether the string represents the zero
+// value for a flag.
+func isZeroValue(flag *Flag, value string) (ok bool, err error) {
+ // Build a zero value of the flag's Value type, and see if the
+ // result of calling its String method equals the value passed in.
+ // This works unless the Value type is itself an interface type.
+ typ := reflect.TypeOf(flag.Value)
+ var z reflect.Value
+ if typ.Kind() == reflect.Pointer {
+ z = reflect.New(typ.Elem())
+ } else {
+ z = reflect.Zero(typ)
+ }
+ // Catch panics calling the String method, which shouldn't prevent the
+ // usage message from being printed, but that we should report to the
+ // user so that they know to fix their code.
+ defer func() {
+ if e := recover(); e != nil {
+ if typ.Kind() == reflect.Pointer {
+ typ = typ.Elem()
+ }
+ err = fmt.Errorf("panic calling String method on zero %v for flag %s: %v", typ, flag.Name, e)
+ }
+ }()
+ return value == z.Interface().(Value).String(), nil
+}
+
+// UnquoteUsage extracts a back-quoted name from the usage
+// string for a flag and returns it and the un-quoted usage.
+// Given "a `name` to show" it returns ("name", "a name to show").
+// If there are no back quotes, the name is an educated guess of the
+// type of the flag's value, or the empty string if the flag is boolean.
+func UnquoteUsage(flag *Flag) (name string, usage string) {
+ // Look for a back-quoted name, but avoid the strings package.
+ usage = flag.Usage
+ for i := 0; i < len(usage); i++ {
+ if usage[i] == '`' {
+ for j := i + 1; j < len(usage); j++ {
+ if usage[j] == '`' {
+ name = usage[i+1 : j]
+ usage = usage[:i] + name + usage[j+1:]
+ return name, usage
+ }
+ }
+ break // Only one back quote; use type name.
+ }
+ }
+ // No explicit name, so use type if we can find one.
+ name = "value"
+ switch fv := flag.Value.(type) {
+ case boolFlag:
+ if fv.IsBoolFlag() {
+ name = ""
+ }
+ case *durationValue:
+ name = "duration"
+ case *float64Value:
+ name = "float"
+ case *intValue, *int64Value:
+ name = "int"
+ case *stringValue:
+ name = "string"
+ case *uintValue, *uint64Value:
+ name = "uint"
+ }
+ return
+}
+
+// PrintDefaults prints, to standard error unless configured otherwise, the
+// default values of all defined command-line flags in the set. See the
+// documentation for the global function PrintDefaults for more information.
+func (f *FlagSet) PrintDefaults() {
+ var isZeroValueErrs []error
+ f.VisitAll(func(flag *Flag) {
+ var b strings.Builder
+ fmt.Fprintf(&b, " -%s", flag.Name) // Two spaces before -; see next two comments.
+ name, usage := UnquoteUsage(flag)
+ if len(name) > 0 {
+ b.WriteString(" ")
+ b.WriteString(name)
+ }
+ // Boolean flags of one ASCII letter are so common we
+ // treat them specially, putting their usage on the same line.
+ if b.Len() <= 4 { // space, space, '-', 'x'.
+ b.WriteString("\t")
+ } else {
+ // Four spaces before the tab triggers good alignment
+ // for both 4- and 8-space tab stops.
+ b.WriteString("\n \t")
+ }
+ b.WriteString(strings.ReplaceAll(usage, "\n", "\n \t"))
+
+ // Print the default value only if it differs to the zero value
+ // for this flag type.
+ if isZero, err := isZeroValue(flag, flag.DefValue); err != nil {
+ isZeroValueErrs = append(isZeroValueErrs, err)
+ } else if !isZero {
+ if _, ok := flag.Value.(*stringValue); ok {
+ // put quotes on the value
+ fmt.Fprintf(&b, " (default %q)", flag.DefValue)
+ } else {
+ fmt.Fprintf(&b, " (default %v)", flag.DefValue)
+ }
+ }
+ fmt.Fprint(f.Output(), b.String(), "\n")
+ })
+ // If calling String on any zero flag.Values triggered a panic, print
+ // the messages after the full set of defaults so that the programmer
+ // knows to fix the panic.
+ if errs := isZeroValueErrs; len(errs) > 0 {
+ fmt.Fprintln(f.Output())
+ for _, err := range errs {
+ fmt.Fprintln(f.Output(), err)
+ }
+ }
+}
+
+// PrintDefaults prints, to standard error unless configured otherwise,
+// a usage message showing the default settings of all defined
+// command-line flags.
+// For an integer valued flag x, the default output has the form
+//
+// -x int
+// usage-message-for-x (default 7)
+//
+// The usage message will appear on a separate line for anything but
+// a bool flag with a one-byte name. For bool flags, the type is
+// omitted and if the flag name is one byte the usage message appears
+// on the same line. The parenthetical default is omitted if the
+// default is the zero value for the type. The listed type, here int,
+// can be changed by placing a back-quoted name in the flag's usage
+// string; the first such item in the message is taken to be a parameter
+// name to show in the message and the back quotes are stripped from
+// the message when displayed. For instance, given
+//
+// flag.String("I", "", "search `directory` for include files")
+//
+// the output will be
+//
+// -I directory
+// search directory for include files.
+//
+// To change the destination for flag messages, call CommandLine.SetOutput.
+func PrintDefaults() {
+ CommandLine.PrintDefaults()
+}
+
+// defaultUsage is the default function to print a usage message.
+func (f *FlagSet) defaultUsage() {
+ if f.name == "" {
+ fmt.Fprintf(f.Output(), "Usage:\n")
+ } else {
+ fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
+ }
+ f.PrintDefaults()
+}
+
+// NOTE: Usage is not just defaultUsage(CommandLine)
+// because it serves (via godoc flag Usage) as the example
+// for how to write your own usage function.
+
+// Usage prints a usage message documenting all defined command-line flags
+// to CommandLine's output, which by default is os.Stderr.
+// It is called when an error occurs while parsing flags.
+// The function is a variable that may be changed to point to a custom function.
+// By default it prints a simple header and calls PrintDefaults; for details about the
+// format of the output and how to control it, see the documentation for PrintDefaults.
+// Custom usage functions may choose to exit the program; by default exiting
+// happens anyway as the command line's error handling strategy is set to
+// ExitOnError.
+var Usage = func() {
+ fmt.Fprintf(CommandLine.Output(), "Usage of %s:\n", os.Args[0])
+ PrintDefaults()
+}
+
+// NFlag returns the number of flags that have been set.
+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. Arg returns an empty string if the
+// requested element does not exist.
+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. Arg returns an empty string if the
+// requested element does not exist.
+func Arg(i int) string {
+ return CommandLine.Arg(i)
+}
+
+// NArg is the number of arguments remaining after flags have been processed.
+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 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) {
+ 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 {
+ 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) {
+ 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 {
+ 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) {
+ 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 {
+ 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) {
+ 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 (f *FlagSet) Uint(name string, value uint, usage string) *uint {
+ p := new(uint)
+ 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) {
+ 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 {
+ 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 (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 (f *FlagSet) String(name string, value string, usage string) *string {
+ p := new(string)
+ 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) {
+ 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 (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
+ p := new(float64)
+ f.Float64Var(p, name, value, usage)
+ return p
+}
+
+// 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 CommandLine.Float64(name, value, usage)
+}
+
+// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
+// The argument p points to a time.Duration variable in which to store the value of the flag.
+// The flag accepts a value acceptable to time.ParseDuration.
+func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
+ f.Var(newDurationValue(value, p), name, usage)
+}
+
+// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
+// The argument p points to a time.Duration variable in which to store the value of the flag.
+// The flag accepts a value acceptable to time.ParseDuration.
+func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
+ CommandLine.Var(newDurationValue(value, p), name, usage)
+}
+
+// Duration defines a time.Duration flag with specified name, default value, and usage string.
+// The return value is the address of a time.Duration variable that stores the value of the flag.
+// The flag accepts a value acceptable to time.ParseDuration.
+func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
+ p := new(time.Duration)
+ f.DurationVar(p, name, value, usage)
+ return p
+}
+
+// Duration defines a time.Duration flag with specified name, default value, and usage string.
+// The return value is the address of a time.Duration variable that stores the value of the flag.
+// The flag accepts a value acceptable to time.ParseDuration.
+func Duration(name string, value time.Duration, usage string) *time.Duration {
+ return CommandLine.Duration(name, value, usage)
+}
+
+// TextVar defines a flag with a specified name, default value, and usage string.
+// The argument p must be a pointer to a variable that will hold the value
+// of the flag, and p must implement encoding.TextUnmarshaler.
+// If the flag is used, the flag value will be passed to p's UnmarshalText method.
+// The type of the default value must be the same as the type of p.
+func (f *FlagSet) TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) {
+ f.Var(newTextValue(value, p), name, usage)
+}
+
+// TextVar defines a flag with a specified name, default value, and usage string.
+// The argument p must be a pointer to a variable that will hold the value
+// of the flag, and p must implement encoding.TextUnmarshaler.
+// If the flag is used, the flag value will be passed to p's UnmarshalText method.
+// The type of the default value must be the same as the type of p.
+func TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) {
+ CommandLine.Var(newTextValue(value, p), name, usage)
+}
+
+// Func defines a flag with the specified name and usage string.
+// Each time the flag is seen, fn is called with the value of the flag.
+// If fn returns a non-nil error, it will be treated as a flag value parsing error.
+func (f *FlagSet) Func(name, usage string, fn func(string) error) {
+ f.Var(funcValue(fn), name, usage)
+}
+
+// Func defines a flag with the specified name and usage string.
+// Each time the flag is seen, fn is called with the value of the flag.
+// If fn returns a non-nil error, it will be treated as a flag value parsing error.
+func Func(name, usage string, fn func(string) error) {
+ CommandLine.Func(name, usage, fn)
+}
+
+// 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) {
+ // Flag must not begin "-" or contain "=".
+ if strings.HasPrefix(name, "-") {
+ panic(f.sprintf("flag %q begins with -", name))
+ } else if strings.Contains(name, "=") {
+ panic(f.sprintf("flag %q contains =", name))
+ }
+
+ // Remember the default value as a string; it won't change.
+ flag := &Flag{name, usage, value, value.String()}
+ _, alreadythere := f.formal[name]
+ if alreadythere {
+ var msg string
+ if f.name == "" {
+ msg = f.sprintf("flag redefined: %s", name)
+ } else {
+ msg = f.sprintf("%s flag redefined: %s", f.name, name)
+ }
+ panic(msg) // Happens only if flags are declared with identical names
+ }
+ if f.formal == nil {
+ f.formal = make(map[string]*Flag)
+ }
+ 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
+// 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 Var(value Value, name string, usage string) {
+ CommandLine.Var(value, name, usage)
+}
+
+// sprintf formats the message, prints it to output, and returns it.
+func (f *FlagSet) sprintf(format string, a ...any) string {
+ msg := fmt.Sprintf(format, a...)
+ fmt.Fprintln(f.Output(), msg)
+ return msg
+}
+
+// failf prints to standard error a formatted error and usage message and
+// returns the error.
+func (f *FlagSet) failf(format string, a ...any) error {
+ msg := f.sprintf(format, a...)
+ f.usage()
+ return errors.New(msg)
+}
+
+// usage calls the Usage method for the flag set if one is specified,
+// or the appropriate default usage function otherwise.
+func (f *FlagSet) usage() {
+ if f.Usage == nil {
+ f.defaultUsage()
+ } else {
+ f.Usage()
+ }
+}
+
+// parseOne parses one flag. It reports whether a flag was seen.
+func (f *FlagSet) parseOne() (bool, error) {
+ if len(f.args) == 0 {
+ return false, nil
+ }
+ s := f.args[0]
+ if len(s) < 2 || s[0] != '-' {
+ return false, nil
+ }
+ numMinuses := 1
+ if s[1] == '-' {
+ numMinuses++
+ if len(s) == 2 { // "--" terminates the flags
+ f.args = f.args[1:]
+ return false, nil
+ }
+ }
+ name := s[numMinuses:]
+ if len(name) == 0 || name[0] == '-' || name[0] == '=' {
+ return false, f.failf("bad flag syntax: %s", s)
+ }
+
+ // it's a flag. does it have an argument?
+ f.args = f.args[1:]
+ hasValue := false
+ value := ""
+ for i := 1; i < len(name); i++ { // equals cannot be first
+ if name[i] == '=' {
+ value = name[i+1:]
+ hasValue = true
+ name = name[0:i]
+ break
+ }
+ }
+
+ flag, ok := f.formal[name]
+ if !ok {
+ if name == "help" || name == "h" { // special case for nice help message.
+ f.usage()
+ return false, ErrHelp
+ }
+ return false, f.failf("flag provided but not defined: -%s", name)
+ }
+
+ if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
+ if hasValue {
+ if err := fv.Set(value); err != nil {
+ return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
+ }
+ } else {
+ if err := fv.Set("true"); err != nil {
+ return false, f.failf("invalid boolean flag %s: %v", name, err)
+ }
+ }
+ } else {
+ // It must have a value, which might be the next argument.
+ if !hasValue && len(f.args) > 0 {
+ // value is the next arg
+ hasValue = true
+ value, f.args = f.args[0], f.args[1:]
+ }
+ if !hasValue {
+ return false, f.failf("flag needs an argument: -%s", name)
+ }
+ if err := flag.Value.Set(value); err != nil {
+ return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
+ }
+ }
+ if f.actual == nil {
+ f.actual = make(map[string]*Flag)
+ }
+ f.actual[name] = flag
+ return true, nil
+}
+
+// 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.
+// The return value will be ErrHelp if -help or -h were set but not defined.
+func (f *FlagSet) Parse(arguments []string) error {
+ f.parsed = true
+ f.args = arguments
+ for {
+ seen, err := f.parseOne()
+ if seen {
+ continue
+ }
+ if err == nil {
+ break
+ }
+ switch f.errorHandling {
+ case ContinueOnError:
+ return err
+ case ExitOnError:
+ if err == ErrHelp {
+ os.Exit(0)
+ }
+ os.Exit(2)
+ case PanicOnError:
+ panic(err)
+ }
+ }
+ return nil
+}
+
+// Parsed reports whether f.Parse has been called.
+func (f *FlagSet) Parsed() bool {
+ return f.parsed
+}
+
+// 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:])
+}
+
+// Parsed reports whether the command-line flags have been parsed.
+func Parsed() bool {
+ return CommandLine.Parsed()
+}
+
+// CommandLine is the default set of command-line flags, parsed from os.Args.
+// The top-level functions such as BoolVar, Arg, and so on are wrappers for the
+// methods of CommandLine.
+var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
+
+func init() {
+ // Override generic FlagSet default Usage with call to global Usage.
+ // Note: This is not CommandLine.Usage = Usage,
+ // because we want any eventual call to use any updated value of Usage,
+ // not the value it has when this line is run.
+ CommandLine.Usage = commandLineUsage
+}
+
+func commandLineUsage() {
+ Usage()
+}
+
+// NewFlagSet returns a new, empty flag set with the specified name and
+// error handling property. If the name is not empty, it will be printed
+// in the default usage message and in error messages.
+func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
+ f := &FlagSet{
+ name: name,
+ errorHandling: errorHandling,
+ }
+ f.Usage = f.defaultUsage
+ return f
+}
+
+// Init sets the name and error handling property for a flag set.
+// By default, the zero FlagSet uses an empty name and the
+// ContinueOnError error handling policy.
+func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
+ f.name = name
+ f.errorHandling = errorHandling
+}
diff --git a/src/flag/flag_test.go b/src/flag/flag_test.go
new file mode 100644
index 0000000..1755168
--- /dev/null
+++ b/src/flag/flag_test.go
@@ -0,0 +1,799 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flag_test
+
+import (
+ "bytes"
+ . "flag"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "os"
+ "os/exec"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+)
+
+func boolString(s string) string {
+ if s == "0" {
+ return "false"
+ }
+ return "true"
+}
+
+func TestEverything(t *testing.T) {
+ ResetForTesting(nil)
+ Bool("test_bool", false, "bool value")
+ Int("test_int", 0, "int value")
+ Int64("test_int64", 0, "int64 value")
+ Uint("test_uint", 0, "uint value")
+ Uint64("test_uint64", 0, "uint64 value")
+ String("test_string", "0", "string value")
+ Float64("test_float64", 0, "float64 value")
+ Duration("test_duration", 0, "time.Duration value")
+ Func("test_func", "func value", func(string) error { return nil })
+
+ m := make(map[string]*Flag)
+ desired := "0"
+ visitor := func(f *Flag) {
+ if len(f.Name) > 5 && f.Name[0:5] == "test_" {
+ m[f.Name] = f
+ ok := false
+ switch {
+ case f.Value.String() == desired:
+ ok = true
+ case f.Name == "test_bool" && f.Value.String() == boolString(desired):
+ ok = true
+ case f.Name == "test_duration" && f.Value.String() == desired+"s":
+ ok = true
+ case f.Name == "test_func" && f.Value.String() == "":
+ ok = true
+ }
+ if !ok {
+ t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
+ }
+ }
+ }
+ VisitAll(visitor)
+ if len(m) != 9 {
+ t.Error("VisitAll misses some flags")
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ m = make(map[string]*Flag)
+ Visit(visitor)
+ if len(m) != 0 {
+ t.Errorf("Visit sees unset flags")
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ // Now set all flags
+ Set("test_bool", "true")
+ Set("test_int", "1")
+ Set("test_int64", "1")
+ Set("test_uint", "1")
+ Set("test_uint64", "1")
+ Set("test_string", "1")
+ Set("test_float64", "1")
+ Set("test_duration", "1s")
+ Set("test_func", "1")
+ desired = "1"
+ Visit(visitor)
+ if len(m) != 9 {
+ t.Error("Visit fails after set")
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ // Now test they're visited in sort order.
+ var flagNames []string
+ Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
+ if !sort.StringsAreSorted(flagNames) {
+ t.Errorf("flag names not sorted: %v", flagNames)
+ }
+}
+
+func TestGet(t *testing.T) {
+ ResetForTesting(nil)
+ Bool("test_bool", true, "bool value")
+ Int("test_int", 1, "int value")
+ Int64("test_int64", 2, "int64 value")
+ Uint("test_uint", 3, "uint value")
+ Uint64("test_uint64", 4, "uint64 value")
+ String("test_string", "5", "string value")
+ Float64("test_float64", 6, "float64 value")
+ Duration("test_duration", 7, "time.Duration value")
+
+ visitor := func(f *Flag) {
+ if len(f.Name) > 5 && f.Name[0:5] == "test_" {
+ g, ok := f.Value.(Getter)
+ if !ok {
+ t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
+ return
+ }
+ switch f.Name {
+ case "test_bool":
+ ok = g.Get() == true
+ case "test_int":
+ ok = g.Get() == int(1)
+ case "test_int64":
+ ok = g.Get() == int64(2)
+ case "test_uint":
+ ok = g.Get() == uint(3)
+ case "test_uint64":
+ ok = g.Get() == uint64(4)
+ case "test_string":
+ ok = g.Get() == "5"
+ case "test_float64":
+ ok = g.Get() == float64(6)
+ case "test_duration":
+ ok = g.Get() == time.Duration(7)
+ }
+ if !ok {
+ t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name)
+ }
+ }
+ }
+ VisitAll(visitor)
+}
+
+func TestUsage(t *testing.T) {
+ called := false
+ ResetForTesting(func() { called = true })
+ if CommandLine.Parse([]string{"-x"}) == nil {
+ t.Error("parse did not fail for unknown flag")
+ }
+ if !called {
+ t.Error("did not call Usage for unknown flag")
+ }
+}
+
+func testParse(f *FlagSet, t *testing.T) {
+ if f.Parsed() {
+ t.Error("f.Parse() = true before Parse")
+ }
+ 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")
+ durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
+ extra := "one-extra-argument"
+ args := []string{
+ "-bool",
+ "-bool2=true",
+ "--int", "22",
+ "--int64", "0x23",
+ "-uint", "24",
+ "--uint64", "25",
+ "-string", "hello",
+ "-float64", "2718e28",
+ "-duration", "2m",
+ extra,
+ }
+ if err := f.Parse(args); err != nil {
+ t.Fatal(err)
+ }
+ if !f.Parsed() {
+ t.Error("f.Parse() = false after Parse")
+ }
+ if *boolFlag != true {
+ t.Error("bool flag should be true, is ", *boolFlag)
+ }
+ if *bool2Flag != true {
+ t.Error("bool2 flag should be true, is ", *bool2Flag)
+ }
+ if *intFlag != 22 {
+ t.Error("int flag should be 22, is ", *intFlag)
+ }
+ if *int64Flag != 0x23 {
+ t.Error("int64 flag should be 0x23, is ", *int64Flag)
+ }
+ if *uintFlag != 24 {
+ t.Error("uint flag should be 24, is ", *uintFlag)
+ }
+ if *uint64Flag != 25 {
+ t.Error("uint64 flag should be 25, is ", *uint64Flag)
+ }
+ if *stringFlag != "hello" {
+ t.Error("string flag should be `hello`, is ", *stringFlag)
+ }
+ if *float64Flag != 2718e28 {
+ t.Error("float64 flag should be 2718e28, is ", *float64Flag)
+ }
+ if *durationFlag != 2*time.Minute {
+ t.Error("duration flag should be 2m, is ", *durationFlag)
+ }
+ 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])
+ }
+}
+
+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 {
+ return fmt.Sprint([]string(*f))
+}
+
+func (f *flagVar) Set(value string) error {
+ *f = append(*f, value)
+ return nil
+}
+
+func TestUserDefined(t *testing.T) {
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
+ flags.SetOutput(io.Discard)
+ var v flagVar
+ 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))
+ }
+ expect := "[1 2 3]"
+ if v.String() != expect {
+ t.Errorf("expected value %q got %q", expect, v.String())
+ }
+}
+
+func TestUserDefinedFunc(t *testing.T) {
+ flags := NewFlagSet("test", ContinueOnError)
+ flags.SetOutput(io.Discard)
+ var ss []string
+ flags.Func("v", "usage", func(s string) error {
+ ss = append(ss, s)
+ return nil
+ })
+ if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
+ t.Error(err)
+ }
+ if len(ss) != 3 {
+ t.Fatal("expected 3 args; got ", len(ss))
+ }
+ expect := "[1 2 3]"
+ if got := fmt.Sprint(ss); got != expect {
+ t.Errorf("expected value %q got %q", expect, got)
+ }
+ // test usage
+ var buf strings.Builder
+ flags.SetOutput(&buf)
+ flags.Parse([]string{"-h"})
+ if usage := buf.String(); !strings.Contains(usage, "usage") {
+ t.Errorf("usage string not included: %q", usage)
+ }
+ // test Func error
+ flags = NewFlagSet("test", ContinueOnError)
+ flags.SetOutput(io.Discard)
+ flags.Func("v", "usage", func(s string) error {
+ return fmt.Errorf("test error")
+ })
+ // flag not set, so no error
+ if err := flags.Parse(nil); err != nil {
+ t.Error(err)
+ }
+ // flag set, expect error
+ if err := flags.Parse([]string{"-v", "1"}); err == nil {
+ t.Error("expected error; got none")
+ } else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") {
+ t.Errorf(`error should contain "test error"; got %q`, errMsg)
+ }
+}
+
+func TestUserDefinedForCommandLine(t *testing.T) {
+ const help = "HELP"
+ var result string
+ ResetForTesting(func() { result = help })
+ Usage()
+ if result != help {
+ t.Fatalf("got %q; expected %q", result, help)
+ }
+}
+
+// Declare a user-defined boolean flag type.
+type boolFlagVar struct {
+ count int
+}
+
+func (b *boolFlagVar) String() string {
+ return fmt.Sprintf("%d", b.count)
+}
+
+func (b *boolFlagVar) Set(value string) error {
+ if value == "true" {
+ b.count++
+ }
+ return nil
+}
+
+func (b *boolFlagVar) IsBoolFlag() bool {
+ return b.count < 4
+}
+
+func TestUserDefinedBool(t *testing.T) {
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
+ flags.SetOutput(io.Discard)
+ var b boolFlagVar
+ var err error
+ flags.Var(&b, "b", "usage")
+ if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
+ if b.count < 4 {
+ t.Error(err)
+ }
+ }
+
+ if b.count != 4 {
+ t.Errorf("want: %d; got: %d", 4, b.count)
+ }
+
+ if err == nil {
+ t.Error("expected error; got none")
+ }
+}
+
+func TestUserDefinedBoolUsage(t *testing.T) {
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
+ var buf bytes.Buffer
+ flags.SetOutput(&buf)
+ var b boolFlagVar
+ flags.Var(&b, "b", "X")
+ b.count = 0
+ // b.IsBoolFlag() will return true and usage will look boolean.
+ flags.PrintDefaults()
+ got := buf.String()
+ want := " -b\tX\n"
+ if got != want {
+ t.Errorf("false: want %q; got %q", want, got)
+ }
+ b.count = 4
+ // b.IsBoolFlag() will return false and usage will look non-boolean.
+ flags.PrintDefaults()
+ got = buf.String()
+ want = " -b\tX\n -b value\n \tX\n"
+ if got != want {
+ t.Errorf("false: want %q; got %q", want, got)
+ }
+}
+
+func TestSetOutput(t *testing.T) {
+ var flags FlagSet
+ var buf strings.Builder
+ flags.SetOutput(&buf)
+ flags.Init("test", ContinueOnError)
+ flags.Parse([]string{"-unknown"})
+ if out := buf.String(); !strings.Contains(out, "-unknown") {
+ t.Logf("expected output mentioning unknown; got %q", out)
+ }
+}
+
+// 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, "")
+ if err := CommandLine.Parse(os.Args[1:]); err != nil {
+ t.Fatal(err)
+ }
+ cmd := Arg(0)
+ os.Args = Args()
+ after := Bool("after", false, "")
+ Parse()
+ args := Args()
+
+ if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
+ t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
+ }
+}
+
+// Test that -help invokes the usage message and returns ErrHelp.
+func TestHelp(t *testing.T) {
+ var helpCalled = false
+ fs := NewFlagSet("help test", ContinueOnError)
+ fs.Usage = func() { helpCalled = true }
+ var flag bool
+ fs.BoolVar(&flag, "flag", false, "regular flag")
+ // Regular flag invocation should work
+ err := fs.Parse([]string{"-flag=true"})
+ if err != nil {
+ t.Fatal("expected no error; got ", err)
+ }
+ if !flag {
+ t.Error("flag was not set by -flag")
+ }
+ if helpCalled {
+ t.Error("help called for regular flag")
+ helpCalled = false // reset for next test
+ }
+ // Help flag should work as expected.
+ err = fs.Parse([]string{"-help"})
+ if err == nil {
+ t.Fatal("error expected")
+ }
+ if err != ErrHelp {
+ t.Fatal("expected ErrHelp; got ", err)
+ }
+ if !helpCalled {
+ t.Fatal("help was not called")
+ }
+ // If we define a help flag, that should override.
+ var help bool
+ fs.BoolVar(&help, "help", false, "help flag")
+ helpCalled = false
+ err = fs.Parse([]string{"-help"})
+ if err != nil {
+ t.Fatal("expected no error for defined -help; got ", err)
+ }
+ if helpCalled {
+ t.Fatal("help was called; should not have been for defined help flag")
+ }
+}
+
+// zeroPanicker is a flag.Value whose String method panics if its dontPanic
+// field is false.
+type zeroPanicker struct {
+ dontPanic bool
+ v string
+}
+
+func (f *zeroPanicker) Set(s string) error {
+ f.v = s
+ return nil
+}
+
+func (f *zeroPanicker) String() string {
+ if !f.dontPanic {
+ panic("panic!")
+ }
+ return f.v
+}
+
+const defaultOutput = ` -A for bootstrapping, allow 'any' type
+ -Alongflagname
+ disable bounds checking
+ -C a boolean defaulting to true (default true)
+ -D path
+ set relative path for local imports
+ -E string
+ issue 23543 (default "0")
+ -F number
+ a non-zero number (default 2.7)
+ -G float
+ a float that defaults to zero
+ -M string
+ a multiline
+ help
+ string
+ -N int
+ a non-zero int (default 27)
+ -O a flag
+ multiline help string (default true)
+ -V list
+ a list of strings (default [a b])
+ -Z int
+ an int that defaults to zero
+ -ZP0 value
+ a flag whose String method panics when it is zero
+ -ZP1 value
+ a flag whose String method panics when it is zero
+ -maxT timeout
+ set timeout for dial
+
+panic calling String method on zero flag_test.zeroPanicker for flag ZP0: panic!
+panic calling String method on zero flag_test.zeroPanicker for flag ZP1: panic!
+`
+
+func TestPrintDefaults(t *testing.T) {
+ fs := NewFlagSet("print defaults test", ContinueOnError)
+ var buf strings.Builder
+ fs.SetOutput(&buf)
+ fs.Bool("A", false, "for bootstrapping, allow 'any' type")
+ fs.Bool("Alongflagname", false, "disable bounds checking")
+ fs.Bool("C", true, "a boolean defaulting to true")
+ fs.String("D", "", "set relative `path` for local imports")
+ fs.String("E", "0", "issue 23543")
+ fs.Float64("F", 2.7, "a non-zero `number`")
+ fs.Float64("G", 0, "a float that defaults to zero")
+ fs.String("M", "", "a multiline\nhelp\nstring")
+ fs.Int("N", 27, "a non-zero int")
+ fs.Bool("O", true, "a flag\nmultiline help string")
+ fs.Var(&flagVar{"a", "b"}, "V", "a `list` of strings")
+ fs.Int("Z", 0, "an int that defaults to zero")
+ fs.Var(&zeroPanicker{true, ""}, "ZP0", "a flag whose String method panics when it is zero")
+ fs.Var(&zeroPanicker{true, "something"}, "ZP1", "a flag whose String method panics when it is zero")
+ fs.Duration("maxT", 0, "set `timeout` for dial")
+ fs.PrintDefaults()
+ got := buf.String()
+ if got != defaultOutput {
+ t.Errorf("got:\n%q\nwant:\n%q", got, defaultOutput)
+ }
+}
+
+// Issue 19230: validate range of Int and Uint flag values.
+func TestIntFlagOverflow(t *testing.T) {
+ if strconv.IntSize != 32 {
+ return
+ }
+ ResetForTesting(nil)
+ Int("i", 0, "")
+ Uint("u", 0, "")
+ if err := Set("i", "2147483648"); err == nil {
+ t.Error("unexpected success setting Int")
+ }
+ if err := Set("u", "4294967296"); err == nil {
+ t.Error("unexpected success setting Uint")
+ }
+}
+
+// Issue 20998: Usage should respect CommandLine.output.
+func TestUsageOutput(t *testing.T) {
+ ResetForTesting(DefaultUsage)
+ var buf strings.Builder
+ CommandLine.SetOutput(&buf)
+ defer func(old []string) { os.Args = old }(os.Args)
+ os.Args = []string{"app", "-i=1", "-unknown"}
+ Parse()
+ const want = "flag provided but not defined: -i\nUsage of app:\n"
+ if got := buf.String(); got != want {
+ t.Errorf("output = %q; want %q", got, want)
+ }
+}
+
+func TestGetters(t *testing.T) {
+ expectedName := "flag set"
+ expectedErrorHandling := ContinueOnError
+ expectedOutput := io.Writer(os.Stderr)
+ fs := NewFlagSet(expectedName, expectedErrorHandling)
+
+ if fs.Name() != expectedName {
+ t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName)
+ }
+ if fs.ErrorHandling() != expectedErrorHandling {
+ t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling)
+ }
+ if fs.Output() != expectedOutput {
+ t.Errorf("unexpected output: got %#v, expected %#v", fs.Output(), expectedOutput)
+ }
+
+ expectedName = "gopher"
+ expectedErrorHandling = ExitOnError
+ expectedOutput = os.Stdout
+ fs.Init(expectedName, expectedErrorHandling)
+ fs.SetOutput(expectedOutput)
+
+ if fs.Name() != expectedName {
+ t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName)
+ }
+ if fs.ErrorHandling() != expectedErrorHandling {
+ t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling)
+ }
+ if fs.Output() != expectedOutput {
+ t.Errorf("unexpected output: got %v, expected %v", fs.Output(), expectedOutput)
+ }
+}
+
+func TestParseError(t *testing.T) {
+ for _, typ := range []string{"bool", "int", "int64", "uint", "uint64", "float64", "duration"} {
+ fs := NewFlagSet("parse error test", ContinueOnError)
+ fs.SetOutput(io.Discard)
+ _ = fs.Bool("bool", false, "")
+ _ = fs.Int("int", 0, "")
+ _ = fs.Int64("int64", 0, "")
+ _ = fs.Uint("uint", 0, "")
+ _ = fs.Uint64("uint64", 0, "")
+ _ = fs.Float64("float64", 0, "")
+ _ = fs.Duration("duration", 0, "")
+ // Strings cannot give errors.
+ args := []string{"-" + typ + "=x"}
+ err := fs.Parse(args) // x is not a valid setting for any flag.
+ if err == nil {
+ t.Errorf("Parse(%q)=%v; expected parse error", args, err)
+ continue
+ }
+ if !strings.Contains(err.Error(), "invalid") || !strings.Contains(err.Error(), "parse error") {
+ t.Errorf("Parse(%q)=%v; expected parse error", args, err)
+ }
+ }
+}
+
+func TestRangeError(t *testing.T) {
+ bad := []string{
+ "-int=123456789012345678901",
+ "-int64=123456789012345678901",
+ "-uint=123456789012345678901",
+ "-uint64=123456789012345678901",
+ "-float64=1e1000",
+ }
+ for _, arg := range bad {
+ fs := NewFlagSet("parse error test", ContinueOnError)
+ fs.SetOutput(io.Discard)
+ _ = fs.Int("int", 0, "")
+ _ = fs.Int64("int64", 0, "")
+ _ = fs.Uint("uint", 0, "")
+ _ = fs.Uint64("uint64", 0, "")
+ _ = fs.Float64("float64", 0, "")
+ // Strings cannot give errors, and bools and durations do not return strconv.NumError.
+ err := fs.Parse([]string{arg})
+ if err == nil {
+ t.Errorf("Parse(%q)=%v; expected range error", arg, err)
+ continue
+ }
+ if !strings.Contains(err.Error(), "invalid") || !strings.Contains(err.Error(), "value out of range") {
+ t.Errorf("Parse(%q)=%v; expected range error", arg, err)
+ }
+ }
+}
+
+func TestExitCode(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ magic := 123
+ if os.Getenv("GO_CHILD_FLAG") != "" {
+ fs := NewFlagSet("test", ExitOnError)
+ if os.Getenv("GO_CHILD_FLAG_HANDLE") != "" {
+ var b bool
+ fs.BoolVar(&b, os.Getenv("GO_CHILD_FLAG_HANDLE"), false, "")
+ }
+ fs.Parse([]string{os.Getenv("GO_CHILD_FLAG")})
+ os.Exit(magic)
+ }
+
+ tests := []struct {
+ flag string
+ flagHandle string
+ expectExit int
+ }{
+ {
+ flag: "-h",
+ expectExit: 0,
+ },
+ {
+ flag: "-help",
+ expectExit: 0,
+ },
+ {
+ flag: "-undefined",
+ expectExit: 2,
+ },
+ {
+ flag: "-h",
+ flagHandle: "h",
+ expectExit: magic,
+ },
+ {
+ flag: "-help",
+ flagHandle: "help",
+ expectExit: magic,
+ },
+ }
+
+ for _, test := range tests {
+ cmd := exec.Command(os.Args[0], "-test.run=TestExitCode")
+ cmd.Env = append(
+ os.Environ(),
+ "GO_CHILD_FLAG="+test.flag,
+ "GO_CHILD_FLAG_HANDLE="+test.flagHandle,
+ )
+ cmd.Run()
+ got := cmd.ProcessState.ExitCode()
+ // ExitCode is either 0 or 1 on Plan 9.
+ if runtime.GOOS == "plan9" && test.expectExit != 0 {
+ test.expectExit = 1
+ }
+ if got != test.expectExit {
+ t.Errorf("unexpected exit code for test case %+v \n: got %d, expect %d",
+ test, got, test.expectExit)
+ }
+ }
+}
+
+func mustPanic(t *testing.T, testName string, expected string, f func()) {
+ t.Helper()
+ defer func() {
+ switch msg := recover().(type) {
+ case nil:
+ t.Errorf("%s\n: expected panic(%q), but did not panic", testName, expected)
+ case string:
+ if msg != expected {
+ t.Errorf("%s\n: expected panic(%q), but got panic(%q)", testName, expected, msg)
+ }
+ default:
+ t.Errorf("%s\n: expected panic(%q), but got panic(%T%v)", testName, expected, msg, msg)
+ }
+ }()
+ f()
+}
+
+func TestInvalidFlags(t *testing.T) {
+ tests := []struct {
+ flag string
+ errorMsg string
+ }{
+ {
+ flag: "-foo",
+ errorMsg: "flag \"-foo\" begins with -",
+ },
+ {
+ flag: "foo=bar",
+ errorMsg: "flag \"foo=bar\" contains =",
+ },
+ }
+
+ for _, test := range tests {
+ testName := fmt.Sprintf("FlagSet.Var(&v, %q, \"\")", test.flag)
+
+ fs := NewFlagSet("", ContinueOnError)
+ buf := &strings.Builder{}
+ fs.SetOutput(buf)
+
+ mustPanic(t, testName, test.errorMsg, func() {
+ var v flagVar
+ fs.Var(&v, test.flag, "")
+ })
+ if msg := test.errorMsg + "\n"; msg != buf.String() {
+ t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf)
+ }
+ }
+}
+
+func TestRedefinedFlags(t *testing.T) {
+ tests := []struct {
+ flagSetName string
+ errorMsg string
+ }{
+ {
+ flagSetName: "",
+ errorMsg: "flag redefined: foo",
+ },
+ {
+ flagSetName: "fs",
+ errorMsg: "fs flag redefined: foo",
+ },
+ }
+
+ for _, test := range tests {
+ testName := fmt.Sprintf("flag redefined in FlagSet(%q)", test.flagSetName)
+
+ fs := NewFlagSet(test.flagSetName, ContinueOnError)
+ buf := &strings.Builder{}
+ fs.SetOutput(buf)
+
+ var v flagVar
+ fs.Var(&v, "foo", "")
+
+ mustPanic(t, testName, test.errorMsg, func() {
+ fs.Var(&v, "foo", "")
+ })
+ if msg := test.errorMsg + "\n"; msg != buf.String() {
+ t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf)
+ }
+ }
+}