Skip to content

flag: add BoolFunc; FlagSet.BoolFunc #59013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/next/53747.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pkg flag, func BoolFunc(string, string, func(string) error) #53747
pkg flag, method (*FlagSet) BoolFunc(string, string, func(string) error) #53747
16 changes: 16 additions & 0 deletions src/flag/example_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,19 @@ func ExampleFunc() {
// IP address to parse
// {ip: <nil>, loopback: false}
}

func ExampleBoolFunc() {
fs := flag.NewFlagSet("ExampleBoolFunc", flag.ContinueOnError)
fs.SetOutput(os.Stdout)

fs.BoolFunc("log", "logs a dummy message", func(s string) error {
fmt.Println("dummy message:", s)
return nil
})
fs.Parse([]string{"-log"})
fs.Parse([]string{"-log=0"})

// Output:
// dummy message: true
// dummy message: 0
}
23 changes: 23 additions & 0 deletions src/flag/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,15 @@ func (f funcValue) Set(s string) error { return f(s) }

func (f funcValue) String() string { return "" }

// -- boolFunc Value
type boolFuncValue func(string) error

func (f boolFuncValue) Set(s string) error { return f(s) }

func (f boolFuncValue) String() string { return "" }

func (f boolFuncValue) IsBoolFlag() bool { return true }

// Value is the interface to the dynamic value stored in a flag.
// (The default value is represented as a string.)
//
Expand Down Expand Up @@ -955,6 +964,20 @@ func Func(name, usage string, fn func(string) error) {
CommandLine.Func(name, usage, fn)
}

// BoolFunc defines a flag with the specified name and usage string without requiring values.
// 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) BoolFunc(name, usage string, fn func(string) error) {
f.Var(boolFuncValue(fn), name, usage)
}

// BoolFunc defines a flag with the specified name and usage string without requiring values.
// 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 BoolFunc(name, usage string, fn func(string) error) {
CommandLine.BoolFunc(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
Expand Down
51 changes: 49 additions & 2 deletions src/flag/flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func TestEverything(t *testing.T) {
Float64("test_float64", 0, "float64 value")
Duration("test_duration", 0, "time.Duration value")
Func("test_func", "func value", func(string) error { return nil })
BoolFunc("test_boolfunc", "func", func(string) error { return nil })

m := make(map[string]*Flag)
desired := "0"
Expand All @@ -54,14 +55,16 @@ func TestEverything(t *testing.T) {
ok = true
case f.Name == "test_func" && f.Value.String() == "":
ok = true
case f.Name == "test_boolfunc" && f.Value.String() == "":
ok = true
}
if !ok {
t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
}
}
}
VisitAll(visitor)
if len(m) != 9 {
if len(m) != 10 {
t.Error("VisitAll misses some flags")
for k, v := range m {
t.Log(k, *v)
Expand All @@ -85,9 +88,10 @@ func TestEverything(t *testing.T) {
Set("test_float64", "1")
Set("test_duration", "1s")
Set("test_func", "1")
Set("test_boolfunc", "")
desired = "1"
Visit(visitor)
if len(m) != 9 {
if len(m) != 10 {
t.Error("Visit fails after set")
for k, v := range m {
t.Log(k, *v)
Expand Down Expand Up @@ -797,3 +801,46 @@ func TestRedefinedFlags(t *testing.T) {
}
}
}

func TestUserDefinedBoolFunc(t *testing.T) {
flags := NewFlagSet("test", ContinueOnError)
flags.SetOutput(io.Discard)
var ss []string
flags.BoolFunc("v", "usage", func(s string) error {
ss = append(ss, s)
return nil
})
if err := flags.Parse([]string{"-v", "", "-v", "1", "-v=2"}); err != nil {
t.Error(err)
}
if len(ss) != 1 {
t.Fatalf("got %d args; want 1 arg", len(ss))
}
want := "[true]"
if got := fmt.Sprint(ss); got != want {
t.Errorf("got %q; want %q", got, want)
}
// 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 BoolFunc error
flags = NewFlagSet("test", ContinueOnError)
flags.SetOutput(io.Discard)
flags.BoolFunc("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", ""}); err == nil {
t.Error("got err == nil; want err != nil")
} else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") {
t.Errorf(`got %q; error should contain "test error"`, errMsg)
}
}