diff options
| author | Eric Paris <[email protected]> | 2015-04-07 16:50:29 -0400 |
|---|---|---|
| committer | Eric Paris <[email protected]> | 2015-04-28 18:37:40 -0400 |
| commit | 8824ec2f84efc5232daca3cb5403690b695190eb (patch) | |
| tree | 5565e551b0c1a9d1f9f7d90562d01c97edf31205 | |
| parent | 60d4c375939ff7ba397a84117d5281256abb298f (diff) | |
Ability to mark flags as deprecated
They will not show up in usage or help, but they will still work.
The usage message will print on os.Stderr any time the flag is set.
| -rw-r--r-- | bool_test.go | 3 | ||||
| -rw-r--r-- | flag.go | 25 | ||||
| -rw-r--r-- | flag_test.go | 72 |
3 files changed, 98 insertions, 2 deletions
diff --git a/bool_test.go b/bool_test.go index fe45b8d..2073590 100644 --- a/bool_test.go +++ b/bool_test.go @@ -156,7 +156,8 @@ func TestImplicitFalse(t *testing.T) { func TestInvalidValue(t *testing.T) { var tristate triStateValue f := setUpFlagSet(&tristate) - err := f.Parse([]string{"--tristate=invalid"}) + args := []string{"--tristate=invalid"} + _, err := parseReturnStderr(t, f, args) if err == nil { t.Fatal("expected an error but did not get any, tristate has value", tristate) } @@ -152,6 +152,7 @@ type Flag struct { Value Value // value as set DefValue string // default value (as text); for usage message Changed bool // If the user set the value (or if left to default) + Deprecated string // If this flag is deprecated, this string is the new or now thing to use Annotations map[string][]string // used by cobra.Command bash autocomple code } @@ -243,6 +244,16 @@ func (f *FlagSet) lookup(name normalizedName) *Flag { return f.formal[name] } +// Mark a flag deprecated in your program +func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { + flag := f.Lookup(name) + if flag == nil { + return fmt.Errorf("flag %q does not exist", name) + } + flag.Deprecated = usageMessage + return nil +} + // Lookup returns the Flag structure of the named command-line flag, // returning nil if none exists. func Lookup(name string) *Flag { @@ -264,7 +275,10 @@ func (f *FlagSet) Set(name, value string) error { f.actual = make(map[normalizedName]*Flag) } f.actual[normalName] = flag - f.lookup(normalName).Changed = true + flag.Changed = true + if len(flag.Deprecated) > 0 { + fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + } return nil } @@ -277,6 +291,9 @@ func Set(name, value string) error { // otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { f.VisitAll(func(flag *Flag) { + if len(flag.Deprecated) > 0 { + return + } format := "--%s=%s: %s\n" if _, ok := flag.Value.(*stringValue); ok { // put quotes on the value @@ -295,6 +312,9 @@ func (f *FlagSet) FlagUsages() string { x := new(bytes.Buffer) f.VisitAll(func(flag *Flag) { + if len(flag.Deprecated) > 0 { + return + } format := "--%s=%s: %s\n" if _, ok := flag.Value.(*stringValue); ok { // put quotes on the value @@ -466,6 +486,9 @@ func (f *FlagSet) setFlag(flag *Flag, value string, origArg string) error { } f.actual[f.normalizeFlagName(flag.Name)] = flag flag.Changed = true + if len(flag.Deprecated) > 0 { + fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + } return nil } diff --git a/flag_test.go b/flag_test.go index c4055ed..a1478e2 100644 --- a/flag_test.go +++ b/flag_test.go @@ -7,6 +7,7 @@ package pflag_test import ( "bytes" "fmt" + "io" "io/ioutil" "os" "sort" @@ -445,3 +446,74 @@ func TestTermination(t *testing.T) { t.Errorf("expected argument %q got %q", arg2, f.Args()[1]) } } + +func TestDeprecatedFlagInDocs(t *testing.T) { + f := NewFlagSet("bob", ContinueOnError) + f.Bool("badflag", true, "always true") + f.MarkDeprecated("badflag", "use --good-flag instead") + + out := new(bytes.Buffer) + f.SetOutput(out) + f.PrintDefaults() + + if strings.Contains(out.String(), "badflag") { + t.Errorf("found deprecated flag in usage!") + } +} + +func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) { + oldStderr := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + + err := f.Parse(args) + + outC := make(chan string) + // copy the output in a separate goroutine so printing can't block indefinitely + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + + w.Close() + os.Stderr = oldStderr + out := <-outC + + return out, err +} + +func TestDeprecatedFlagUsage(t *testing.T) { + f := NewFlagSet("bob", ContinueOnError) + f.Bool("badflag", true, "always true") + usageMsg := "use --good-flag instead" + f.MarkDeprecated("badflag", usageMsg) + + args := []string{"--badflag"} + out, err := parseReturnStderr(t, f, args) + if err != nil { + t.Fatal("expected no error; got ", err) + } + + if !strings.Contains(out, usageMsg) { + t.Errorf("usageMsg not printed when using a deprecated flag!") + } +} + +func TestDeprecatedFlagUsageNormalized(t *testing.T) { + f := NewFlagSet("bob", ContinueOnError) + f.Bool("bad-double_flag", true, "always true") + f.SetWordSeparators([]string{"-", "_"}) + usageMsg := "use --good-flag instead" + f.MarkDeprecated("bad_double-flag", usageMsg) + + args := []string{"--bad_double_flag"} + out, err := parseReturnStderr(t, f, args) + if err != nil { + t.Fatal("expected no error; got ", err) + } + + if !strings.Contains(out, usageMsg) { + t.Errorf("usageMsg not printed when using a deprecated flag!") + } +} |
