diff --git a/bool.go b/bool.go index 5f02373e..7286df8e 100644 --- a/bool.go +++ b/bool.go @@ -12,6 +12,13 @@ type boolFlag interface { IsBoolFlag() bool } +func isNoOptBoolValue(v Value) bool { + if bf, ok := v.(boolFlag); ok { + return bf.IsBoolFlag() + } + return false +} + // -- bool Value type boolValue bool diff --git a/bool_test.go b/bool_test.go index e3704fcd..4cf36ed5 100644 --- a/bool_test.go +++ b/bool_test.go @@ -152,6 +152,34 @@ func TestImplicitFalse(t *testing.T) { } } +func TestCustomIsBoolFlagGetsNoOptDefaultAutomatically(t *testing.T) { + var tristate triStateValue + f := NewFlagSet("test", ContinueOnError) + tristate = triStateFalse + f.VarPF(&tristate, "tristate", "t", "tristate value (true, maybe or false)") + + if err := f.Parse([]string{"--tristate"}); err != nil { + t.Fatal("expected no error; got", err) + } + if tristate != triStateTrue { + t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead") + } +} + +func TestCustomIsBoolFlagShortFormGetsNoOptDefaultAutomatically(t *testing.T) { + var tristate triStateValue + f := NewFlagSet("test", ContinueOnError) + tristate = triStateFalse + f.VarPF(&tristate, "tristate", "t", "tristate value (true, maybe or false)") + + if err := f.Parse([]string{"-t"}); err != nil { + t.Fatal("expected no error; got", err) + } + if tristate != triStateTrue { + t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead") + } +} + func TestInvalidValue(t *testing.T) { var tristate triStateValue f := setUpFlagSet(&tristate) diff --git a/flag.go b/flag.go index 1901a4a2..ced8440a 100644 --- a/flag.go +++ b/flag.go @@ -614,9 +614,10 @@ func (f *FlagSet) PrintDefaults() { // defaultIsZeroValue returns true if the default value for this flag represents // a zero value. func (f *Flag) defaultIsZeroValue() bool { - switch f.Value.(type) { - case boolFlag: + if isNoOptBoolValue(f.Value) { return f.DefValue == "false" || f.DefValue == "" + } + switch f.Value.(type) { case *durationValue: // Beginning in Go 1.7, duration zero values are "0s" return f.DefValue == "0" || f.DefValue == "0s" @@ -665,6 +666,9 @@ func UnquoteUsage(flag *Flag) (name string, usage string) { } name = flag.Value.Type() + if isNoOptBoolValue(flag.Value) { + name = "" + } switch name { case "bool", "boolfunc": name = "" @@ -779,7 +783,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { } varname, usage := UnquoteUsage(flag) - if flag.Value.Type() == "bool" { + if isNoOptBoolValue(flag.Value) && flag.Value.Type() == "bool" { line += "[=true|false]" } else if varname != "" { line += " " + varname @@ -941,6 +945,9 @@ func (f *FlagSet) AddFlag(flag *Flag) { } flag.Name = string(normalizedFlagName) + if flag.NoOptDefVal == "" && isNoOptBoolValue(flag.Value) { + flag.NoOptDefVal = "true" + } f.formal[normalizedFlagName] = flag f.orderedFormal = append(f.orderedFormal, flag) diff --git a/flag_test.go b/flag_test.go index 279b6b28..82fcd48a 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1481,6 +1481,20 @@ func (cv *customValue) Set(s string) error { func (cv *customValue) Type() string { return "custom" } +type customBoolFlagValue bool + +func (cv *customBoolFlagValue) String() string { return strconv.FormatBool(bool(*cv)) } + +func (cv *customBoolFlagValue) Set(s string) error { + v, err := strconv.ParseBool(s) + *cv = customBoolFlagValue(v) + return err +} + +func (cv *customBoolFlagValue) Type() string { return "custom-bool" } + +func (cv *customBoolFlagValue) IsBoolFlag() bool { return true } + func TestPrintDefaults(t *testing.T) { fs := NewFlagSet("print defaults test", ContinueOnError) var buf bytes.Buffer @@ -1529,6 +1543,36 @@ func TestPrintDefaults(t *testing.T) { } } +func TestCustomIsBoolFlagDefaultIsZeroValue(t *testing.T) { + var v customBoolFlagValue + flag := &Flag{ + Name: "custom-bool", + Usage: "custom bool", + Value: &v, + DefValue: "", + } + + if !flag.defaultIsZeroValue() { + t.Fatal("expected empty default for custom IsBoolFlag value to be treated as zero value") + } +} + +func TestPrintDefaultsCustomIsBoolFlagOmitsDefault(t *testing.T) { + fs := NewFlagSet("print defaults custom bool", ContinueOnError) + var buf bytes.Buffer + fs.SetOutput(&buf) + + var v customBoolFlagValue + fs.Var(&v, "custom-bool", "custom bool value implementation") + + fs.PrintDefaults() + got := buf.String() + want := " --custom-bool[=true] custom bool value implementation\n" + if got != want { + t.Errorf("\n--- Got:\n%s--- Wanted:\n%s\n", got, want) + } +} + func TestVisitAllFlagOrder(t *testing.T) { fs := NewFlagSet("TestVisitAllFlagOrder", ContinueOnError) fs.SortFlags = false