Skip to content

Commit 7ef35c6

Browse files
authored
Merge pull request #487 from happysnaker/fix-custom-isbool-compatibility
fix: honor custom IsBoolFlag compatibility
2 parents 95a0aa3 + f64d0fc commit 7ef35c6

4 files changed

Lines changed: 89 additions & 3 deletions

File tree

bool.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ type boolFlag interface {
1212
IsBoolFlag() bool
1313
}
1414

15+
func isNoOptBoolValue(v Value) bool {
16+
if bf, ok := v.(boolFlag); ok {
17+
return bf.IsBoolFlag()
18+
}
19+
return false
20+
}
21+
1522
// -- bool Value
1623
type boolValue bool
1724

bool_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,34 @@ func TestImplicitFalse(t *testing.T) {
152152
}
153153
}
154154

155+
func TestCustomIsBoolFlagGetsNoOptDefaultAutomatically(t *testing.T) {
156+
var tristate triStateValue
157+
f := NewFlagSet("test", ContinueOnError)
158+
tristate = triStateFalse
159+
f.VarPF(&tristate, "tristate", "t", "tristate value (true, maybe or false)")
160+
161+
if err := f.Parse([]string{"--tristate"}); err != nil {
162+
t.Fatal("expected no error; got", err)
163+
}
164+
if tristate != triStateTrue {
165+
t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead")
166+
}
167+
}
168+
169+
func TestCustomIsBoolFlagShortFormGetsNoOptDefaultAutomatically(t *testing.T) {
170+
var tristate triStateValue
171+
f := NewFlagSet("test", ContinueOnError)
172+
tristate = triStateFalse
173+
f.VarPF(&tristate, "tristate", "t", "tristate value (true, maybe or false)")
174+
175+
if err := f.Parse([]string{"-t"}); err != nil {
176+
t.Fatal("expected no error; got", err)
177+
}
178+
if tristate != triStateTrue {
179+
t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead")
180+
}
181+
}
182+
155183
func TestInvalidValue(t *testing.T) {
156184
var tristate triStateValue
157185
f := setUpFlagSet(&tristate)

flag.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,10 @@ func (f *FlagSet) PrintDefaults() {
614614
// defaultIsZeroValue returns true if the default value for this flag represents
615615
// a zero value.
616616
func (f *Flag) defaultIsZeroValue() bool {
617-
switch f.Value.(type) {
618-
case boolFlag:
617+
if isNoOptBoolValue(f.Value) {
619618
return f.DefValue == "false" || f.DefValue == ""
619+
}
620+
switch f.Value.(type) {
620621
case *durationValue:
621622
// Beginning in Go 1.7, duration zero values are "0s"
622623
return f.DefValue == "0" || f.DefValue == "0s"
@@ -665,6 +666,9 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
665666
}
666667

667668
name = flag.Value.Type()
669+
if isNoOptBoolValue(flag.Value) {
670+
name = ""
671+
}
668672
switch name {
669673
case "bool", "boolfunc":
670674
name = ""
@@ -779,7 +783,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
779783
}
780784

781785
varname, usage := UnquoteUsage(flag)
782-
if flag.Value.Type() == "bool" {
786+
if isNoOptBoolValue(flag.Value) && flag.Value.Type() == "bool" {
783787
line += "[=true|false]"
784788
} else if varname != "" {
785789
line += " " + varname
@@ -941,6 +945,9 @@ func (f *FlagSet) AddFlag(flag *Flag) {
941945
}
942946

943947
flag.Name = string(normalizedFlagName)
948+
if flag.NoOptDefVal == "" && isNoOptBoolValue(flag.Value) {
949+
flag.NoOptDefVal = "true"
950+
}
944951
f.formal[normalizedFlagName] = flag
945952
f.orderedFormal = append(f.orderedFormal, flag)
946953

flag_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,20 @@ func (cv *customValue) Set(s string) error {
14811481

14821482
func (cv *customValue) Type() string { return "custom" }
14831483

1484+
type customBoolFlagValue bool
1485+
1486+
func (cv *customBoolFlagValue) String() string { return strconv.FormatBool(bool(*cv)) }
1487+
1488+
func (cv *customBoolFlagValue) Set(s string) error {
1489+
v, err := strconv.ParseBool(s)
1490+
*cv = customBoolFlagValue(v)
1491+
return err
1492+
}
1493+
1494+
func (cv *customBoolFlagValue) Type() string { return "custom-bool" }
1495+
1496+
func (cv *customBoolFlagValue) IsBoolFlag() bool { return true }
1497+
14841498
func TestPrintDefaults(t *testing.T) {
14851499
fs := NewFlagSet("print defaults test", ContinueOnError)
14861500
var buf bytes.Buffer
@@ -1529,6 +1543,36 @@ func TestPrintDefaults(t *testing.T) {
15291543
}
15301544
}
15311545

1546+
func TestCustomIsBoolFlagDefaultIsZeroValue(t *testing.T) {
1547+
var v customBoolFlagValue
1548+
flag := &Flag{
1549+
Name: "custom-bool",
1550+
Usage: "custom bool",
1551+
Value: &v,
1552+
DefValue: "",
1553+
}
1554+
1555+
if !flag.defaultIsZeroValue() {
1556+
t.Fatal("expected empty default for custom IsBoolFlag value to be treated as zero value")
1557+
}
1558+
}
1559+
1560+
func TestPrintDefaultsCustomIsBoolFlagOmitsDefault(t *testing.T) {
1561+
fs := NewFlagSet("print defaults custom bool", ContinueOnError)
1562+
var buf bytes.Buffer
1563+
fs.SetOutput(&buf)
1564+
1565+
var v customBoolFlagValue
1566+
fs.Var(&v, "custom-bool", "custom bool value implementation")
1567+
1568+
fs.PrintDefaults()
1569+
got := buf.String()
1570+
want := " --custom-bool[=true] custom bool value implementation\n"
1571+
if got != want {
1572+
t.Errorf("\n--- Got:\n%s--- Wanted:\n%s\n", got, want)
1573+
}
1574+
}
1575+
15321576
func TestVisitAllFlagOrder(t *testing.T) {
15331577
fs := NewFlagSet("TestVisitAllFlagOrder", ContinueOnError)
15341578
fs.SortFlags = false

0 commit comments

Comments
 (0)