diff --git a/README.md b/README.md index 7a322f6..2a544d1 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ func run() error { cli.Stdout(os.Stdout), cli.Example("Do a thing", "quickstart something"), cli.Example("Count the things", "quickstart something --count 3"), - cli.Flag(&count, "count", 'c', 0, "Count the things"), + cli.Flag(&count, "count", 'c', "Count the things"), cli.Run(func(ctx context.Context, cmd *cli.Command) error { fmt.Fprintf(cmd.Stdout(), "Hello from quickstart!, my args were: %v, count was %d\n", cmd.Args(), count) return nil @@ -168,10 +168,10 @@ func buildCmd() (*cli.Command, error) { return cli.New( // ... // Signature is cli.Flag(*T, name, shorthand, default, description) - cli.Flag(&options.name, "name", 'n', "", "The name of something"), - cli.Flag(&options.force, "force", cli.NoShortHand, false, "Force delete without confirmation"), - cli.Flag(&options.size, "size", 's', 0, "Size of something"), - cli.Flag(&options.items, "items", 'i', nil, "Items to include"), + cli.Flag(&options.name, "name", 'n', "The name of something"), + cli.Flag(&options.force, "force", cli.NoShortHand, "Force delete without confirmation"), + cli.Flag(&options.size, "size", 's', "Size of something"), + cli.Flag(&options.items, "items", 'i', "Items to include"), // ... ) } @@ -374,7 +374,7 @@ Consider the following example of a bad shorthand value: var delete bool // Note: "de" is a bad shorthand, it's two letters -cli.New("demo", cli.Flag(&delete, "delete", "de", false, "Delete something")) +cli.New("demo", cli.Flag(&delete, "delete", "de", "Delete something")) ``` In `cli` this is impossible as we use `rune` as the type for a flag shorthand, so the above example would not compile. Instead you must specify a valid rune: @@ -383,14 +383,14 @@ In `cli` this is impossible as we use `rune` as the type for a flag shorthand, s var delete bool // Ahhh, that's better -cli.New("demo", cli.Flag(&delete, "delete", 'd', false, "Delete something")) +cli.New("demo", cli.Flag(&delete, "delete", 'd', "Delete something")) ``` And if you don't want a shorthand? i.e. just `--delete` with no `-d` option: ```go var delete bool -cli.New("demo", cli.Flag(&delete, "delete", cli.NoShortHand, false, "Delete something")) +cli.New("demo", cli.Flag(&delete, "delete", cli.NoShortHand, "Delete something")) ``` ## In the Wild diff --git a/command.go b/command.go index d7673e4..5f1e622 100644 --- a/command.go +++ b/command.go @@ -65,10 +65,10 @@ func New(name string, options ...Option) (*Command, error) { } // Ensure we always have at least help and version flags - err := Flag(&cfg.helpCalled, "help", 'h', false, "Show help for "+name).apply(&cfg) + err := Flag(&cfg.helpCalled, "help", 'h', "Show help for "+name).apply(&cfg) errs = errors.Join(errs, err) // nil errors are discarded in join - err = Flag(&cfg.versionCalled, "version", 'V', false, "Show version info for "+name).apply(&cfg) + err = Flag(&cfg.versionCalled, "version", 'V', "Show version info for "+name).apply(&cfg) errs = errors.Join(errs, err) if errs != nil { diff --git a/command_test.go b/command_test.go index c8bcbaf..b4cc6a5 100644 --- a/command_test.go +++ b/command_test.go @@ -105,7 +105,7 @@ func TestExecute(t *testing.T) { return nil }), - cli.Flag(&force, "force", 'f', false, "Force something"), + cli.Flag(&force, "force", 'f', "Force something"), } cmd, err := cli.New("test", slices.Concat(options, tt.options)...) @@ -270,8 +270,8 @@ func TestSubCommandExecute(t *testing.T) { sub1 := func() (*cli.Command, error) { defaultOpts := []cli.Option{ - cli.Flag(&force, "force", 'f', false, "Force for sub1"), - cli.Flag(&something, "something", 's', "", "Something for sub1"), + cli.Flag(&force, "force", 'f', "Force for sub1"), + cli.Flag(&something, "something", 's', "Something for sub1"), cli.Run(func(ctx context.Context, cmd *cli.Command) error { if something == "" { something = "" @@ -315,8 +315,8 @@ func TestSubCommandExecute(t *testing.T) { return nil }), - cli.Flag(&deleteMe, "delete", 'd', false, "Delete for sub2"), - cli.Flag(&number, "number", 'n', -1, "Number for sub2"), + cli.Flag(&deleteMe, "delete", 'd', "Delete for sub2"), + cli.Flag(&number, "number", 'n', "Number for sub2", cli.FlagDefault(-1)), } opts := slices.Concat(defaultOpts, tt.sub2Options) @@ -433,7 +433,7 @@ func TestHelp(t *testing.T) { cli.OverrideArgs([]string{"--help"}), cli.Arg(new(string), "src", "The file to copy"), // This one is required cli.Arg(new(string), "dest", "Destination to copy to", cli.ArgDefault("destination.txt")), // This one is optional - cli.Flag(new(flag.Count), "verbosity", 'v', 0, "Increase the verbosity level"), + cli.Flag(new(flag.Count), "verbosity", 'v', "Increase the verbosity level"), cli.Run(func(ctx context.Context, cmd *cli.Command) error { return nil }), }, wantErr: false, @@ -493,10 +493,16 @@ func TestHelp(t *testing.T) { cli.Short("A cool CLI to do things"), cli.Long("A longer, probably multiline description"), cli.SubCommands(sub1, sub2), - cli.Flag(new(bool), "delete", 'd', false, "Delete something"), - cli.Flag(new(int), "count", flag.NoShortHand, -1, "Count something"), - cli.Flag(new([]string), "things", flag.NoShortHand, nil, "Names of things"), - cli.Flag(new([]string), "more", flag.NoShortHand, []string{"one", "two"}, "Names of things with a default"), + cli.Flag(new(bool), "delete", 'd', "Delete something"), + cli.Flag(new(int), "count", flag.NoShortHand, "Count something", cli.FlagDefault(-1)), + cli.Flag(new([]string), "things", flag.NoShortHand, "Names of things"), + cli.Flag( + new([]string), + "more", + flag.NoShortHand, + "Names of things with a default", + cli.FlagDefault([]string{"one", "two"}), + ), }, wantErr: false, }, @@ -735,16 +741,16 @@ func TestOptionValidation(t *testing.T) { { name: "flag already exists", options: []cli.Option{ - cli.Flag(new(int), "count", 'c', 0, "Count something"), - cli.Flag(new(int), "count", 'c', 0, "Count something (again)"), + cli.Flag(new(int), "count", 'c', "Count something"), + cli.Flag(new(int), "count", 'c', "Count something (again)"), }, errMsg: `flag "count" already defined`, }, { name: "flag short already exists", options: []cli.Option{ - cli.Flag(new(int), "count", 'c', 0, "Count something"), - cli.Flag(new(string), "config", 'c', "", "Path to config file"), + cli.Flag(new(int), "count", 'c', "Count something"), + cli.Flag(new(string), "config", 'c', "Path to config file"), }, errMsg: `could not add flag "config" to command "test": shorthand "c" already in use for flag "count"`, }, @@ -884,8 +890,8 @@ func TestCommandOptionOrder(t *testing.T) { cli.Arg(new(string), "third", "Third arg"), cli.Version("v1.2.3"), cli.SubCommands(sub), - cli.Flag(&f, "flag", 'f', false, "Set a bool flag"), - cli.Flag(&count, "count", 'c', 0, "Count a thing"), + cli.Flag(&f, "flag", 'f', "Set a bool flag"), + cli.Flag(&count, "count", 'c', "Count a thing"), } baseLineOptions := slices.Concat( @@ -1001,9 +1007,9 @@ func BenchmarkNew(b *testing.B) { cli.Version("dev"), cli.Commit("dfdddaf"), cli.Example("An example", "bench --help"), - cli.Flag(new(bool), "force", 'f', false, "Force something"), - cli.Flag(new(string), "name", 'n', "", "The name of something"), - cli.Flag(new(int), "count", 'c', 1, "Count something"), + cli.Flag(new(bool), "force", 'f', "Force something"), + cli.Flag(new(string), "name", 'n', "The name of something"), + cli.Flag(new(int), "count", 'c', "Count something", cli.FlagDefault(1)), cli.Run(func(ctx context.Context, cmd *cli.Command) error { return nil }), ) if err != nil { diff --git a/docs/img/cancel.gif b/docs/img/cancel.gif index 1dcd367..32bbc42 100644 Binary files a/docs/img/cancel.gif and b/docs/img/cancel.gif differ diff --git a/docs/img/demo.png b/docs/img/demo.png index 70e8fb9..9c5acc3 100644 Binary files a/docs/img/demo.png and b/docs/img/demo.png differ diff --git a/docs/img/namedargs.gif b/docs/img/namedargs.gif index 2cdc07a..aa0e5c1 100644 Binary files a/docs/img/namedargs.gif and b/docs/img/namedargs.gif differ diff --git a/docs/img/quickstart.gif b/docs/img/quickstart.gif index cc4176c..a20025c 100644 Binary files a/docs/img/quickstart.gif and b/docs/img/quickstart.gif differ diff --git a/docs/img/subcommands.gif b/docs/img/subcommands.gif index 53b3d81..a9cf6b7 100644 Binary files a/docs/img/subcommands.gif and b/docs/img/subcommands.gif differ diff --git a/examples/cover/main.go b/examples/cover/main.go index fe9eb13..5cc7912 100644 --- a/examples/cover/main.go +++ b/examples/cover/main.go @@ -20,7 +20,7 @@ func main() { cli.Version("v1.2.3"), cli.Stdout(os.Stdout), cli.Example("Do a thing", "demo thing --count"), - cli.Flag(&count, "count", 'c', 0, "Count the thing"), + cli.Flag(&count, "count", 'c', "Count the thing"), cli.Run(func(ctx context.Context, cmd *cli.Command) error { fmt.Fprintln(cmd.Stdout(), "Hello from demo, my arguments were: ", cmd.Args()) return nil diff --git a/examples/quickstart/main.go b/examples/quickstart/main.go index f6041c6..500f8f8 100644 --- a/examples/quickstart/main.go +++ b/examples/quickstart/main.go @@ -29,7 +29,7 @@ func run() error { cli.Stdout(os.Stdout), cli.Example("Do a thing", "quickstart something"), cli.Example("Count the things", "quickstart something --count 3"), - cli.Flag(&count, "count", 'c', 0, "Count the things"), + cli.Flag(&count, "count", 'c', "Count the things"), cli.Run(func(ctx context.Context, cmd *cli.Command) error { fmt.Fprintf(cmd.Stdout(), "Hello from quickstart!, my args were: %v, count was %d\n", cmd.Args(), count) return nil diff --git a/examples/subcommands/cli.go b/examples/subcommands/cli.go index b37d2c5..3c7c647 100644 --- a/examples/subcommands/cli.go +++ b/examples/subcommands/cli.go @@ -39,10 +39,10 @@ func buildSayCommand() (*cli.Command, error) { cli.Short("Print a message"), cli.Example("Say a well known phrase", "demo say hello world"), cli.Example("Now louder", "demo say hello world --shout"), - cli.Flag(&options.shout, "shout", 's', false, "Say the message louder"), - cli.Flag(&options.count, "count", 'c', 0, "Count the things"), - cli.Flag(&options.thing, "thing", 't', "", "Name of the thing"), - cli.Flag(&options.items, "item", 'i', nil, "Items to add to a list"), + cli.Flag(&options.shout, "shout", 's', "Say the message louder"), + cli.Flag(&options.count, "count", 'c', "Count the things"), + cli.Flag(&options.thing, "thing", 't', "Name of the thing"), + cli.Flag(&options.items, "item", 'i', "Items to add to a list"), cli.Run(func(ctx context.Context, cmd *cli.Command) error { if options.shout { for _, arg := range cmd.Args() { @@ -88,10 +88,10 @@ func buildDoCommand() (*cli.Command, error) { cli.Example("Do it for a specific duration", "demo do something --duration 1m30s"), cli.Version("do version"), cli.Arg(&thing, "thing", "Thing to do"), - cli.Flag(&options.count, "count", 'c', 1, "Number of times to do the thing"), - cli.Flag(&options.fast, "fast", 'f', false, "Do the thing quickly"), - cli.Flag(&options.verbosity, "verbosity", 'v', 0, "Increase the verbosity level"), - cli.Flag(&options.duration, "duration", 'd', 1*time.Second, "Do the thing for a specific duration"), + cli.Flag(&options.count, "count", 'c', "Number of times to do the thing", cli.FlagDefault(1)), + cli.Flag(&options.fast, "fast", 'f', "Do the thing quickly"), + cli.Flag(&options.verbosity, "verbosity", 'v', "Increase the verbosity level"), + cli.Flag(&options.duration, "duration", 'd', "Do the thing for a specific duration", cli.FlagDefault(1*time.Second)), cli.Run(func(ctx context.Context, cmd *cli.Command) error { if options.fast { fmt.Fprintf( diff --git a/internal/flag/config.go b/internal/flag/config.go new file mode 100644 index 0000000..1a94e88 --- /dev/null +++ b/internal/flag/config.go @@ -0,0 +1,9 @@ +package flag + +import "go.followtheprocess.codes/cli/flag" + +// Config represents the internal configuration of a [Flag]. +type Config[T flag.Flaggable] struct { + // DefaultValue holds the intended default value of the flag. + DefaultValue T +} diff --git a/internal/flag/flag.go b/internal/flag/flag.go index fae3600..0fd75e0 100644 --- a/internal/flag/flag.go +++ b/internal/flag/flag.go @@ -33,12 +33,7 @@ type Flag[T flag.Flaggable] struct { // // The name should be as it appears on the command line, e.g. "force" for a --force flag. An optional // shorthand can be created by setting short to a single letter value, e.g. "f" to also create a -f version of "force". -// -// If you want the flag to be longhand only, pass "" for short. -// -// var force bool -// flag.New(&force, "force", 'f', false, "Force deletion without confirmation") -func New[T flag.Flaggable](p *T, name string, short rune, value T, usage string) (Flag[T], error) { +func New[T flag.Flaggable](p *T, name string, short rune, usage string, config Config[T]) (Flag[T], error) { if err := validateFlagName(name); err != nil { return Flag[T]{}, fmt.Errorf("invalid flag name %q: %w", name, err) } @@ -51,14 +46,14 @@ func New[T flag.Flaggable](p *T, name string, short rune, value T, usage string) p = new(T) } - *p = value + *p = config.DefaultValue // If the default value is not the zero value for the type, it is treated as // significant and shown to the user - if !isZeroIsh(value) { + if !isZeroIsh(*p) { // \t so that defaults get aligned by tabwriter when the command // dumps the flags - usage += fmt.Sprintf("\t[default: %v]", value) + usage += fmt.Sprintf("\t[default: %v]", *p) } flag := Flag[T]{ diff --git a/internal/flag/flag_test.go b/internal/flag/flag_test.go index f8e21cf..5bad71b 100644 --- a/internal/flag/flag_test.go +++ b/internal/flag/flag_test.go @@ -22,7 +22,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int valid", func(t *testing.T) { var i int - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int value", flag.Config[int]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -35,7 +35,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int invalid", func(t *testing.T) { var i int - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int value", flag.Config[int]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -46,7 +46,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int8 valid", func(t *testing.T) { var i int8 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int8 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int8 value", flag.Config[int8]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -59,7 +59,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int8 invalid", func(t *testing.T) { var i int8 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int8 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int8 value", flag.Config[int8]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -70,7 +70,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int16 valid", func(t *testing.T) { var i int16 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int16 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int16 value", flag.Config[int16]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -83,7 +83,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int16 invalid", func(t *testing.T) { var i int16 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int16 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int16 value", flag.Config[int16]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -94,7 +94,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int32 valid", func(t *testing.T) { var i int32 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int32 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int32 value", flag.Config[int32]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -107,7 +107,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int32 invalid", func(t *testing.T) { var i int32 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int32 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int32 value", flag.Config[int32]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -118,7 +118,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int64 valid", func(t *testing.T) { var i int64 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int64 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int64 value", flag.Config[int64]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -131,7 +131,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int64 invalid", func(t *testing.T) { var i int64 - intFlag, err := flag.New(&i, "int", 'i', 0, "Set an int64 value") + intFlag, err := flag.New(&i, "int", 'i', "Set an int64 value", flag.Config[int64]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -142,7 +142,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("count valid", func(t *testing.T) { var c publicflag.Count - countFlag, err := flag.New(&c, "count", 'c', 0, "Count something") + countFlag, err := flag.New(&c, "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err) err = countFlag.Set("1") @@ -170,7 +170,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("count invalid", func(t *testing.T) { var c publicflag.Count - countFlag, err := flag.New(&c, "count", 'c', 0, "Count something") + countFlag, err := flag.New(&c, "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err) err = countFlag.Set("a word") @@ -181,7 +181,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint valid", func(t *testing.T) { var i uint - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint value", flag.Config[uint]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -194,7 +194,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint invalid", func(t *testing.T) { var i uint - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint value", flag.Config[uint]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -205,7 +205,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint8 valid", func(t *testing.T) { var i uint8 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint8 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint8 value", flag.Config[uint8]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -218,7 +218,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint8 invalid", func(t *testing.T) { var i uint8 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint8 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint8 value", flag.Config[uint8]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -229,7 +229,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint16 valid", func(t *testing.T) { var i uint16 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint16 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint16 value", flag.Config[uint16]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -242,7 +242,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint16 invalid", func(t *testing.T) { var i uint16 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint16 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint16 value", flag.Config[uint16]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -253,7 +253,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint32 valid", func(t *testing.T) { var i uint32 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint32 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint32 value", flag.Config[uint32]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -266,7 +266,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint32 invalid", func(t *testing.T) { var i uint32 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint32 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint32 value", flag.Config[uint32]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -277,7 +277,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint64 valid", func(t *testing.T) { var i uint64 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint64 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint64 value", flag.Config[uint64]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -290,7 +290,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint64 invalid", func(t *testing.T) { var i uint64 - intFlag, err := flag.New(&i, "uint", 'i', 0, "Set a uint64 value") + intFlag, err := flag.New(&i, "uint", 'i', "Set a uint64 value", flag.Config[uint64]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -301,7 +301,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uintptr valid", func(t *testing.T) { var i uintptr - intFlag, err := flag.New(&i, "uintptr", 'i', 0, "Set a uintptr value") + intFlag, err := flag.New(&i, "uintptr", 'i', "Set a uintptr value", flag.Config[uintptr]{}) test.Ok(t, err) err = intFlag.Set("42") @@ -314,7 +314,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uintptr invalid", func(t *testing.T) { var i uintptr - intFlag, err := flag.New(&i, "uintptr", 'i', 0, "Set a uintptr value") + intFlag, err := flag.New(&i, "uintptr", 'i', "Set a uintptr value", flag.Config[uintptr]{}) test.Ok(t, err) err = intFlag.Set("word") @@ -325,7 +325,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float32 valid", func(t *testing.T) { var f float32 - floatFlag, err := flag.New(&f, "float", 'f', 0, "Set a float32 value") + floatFlag, err := flag.New(&f, "float", 'f', "Set a float32 value", flag.Config[float32]{}) test.Ok(t, err) err = floatFlag.Set("3.14159") @@ -338,7 +338,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float32 invalid", func(t *testing.T) { var f float32 - floatFlag, err := flag.New(&f, "float", 'f', 0, "Set a float32 value") + floatFlag, err := flag.New(&f, "float", 'f', "Set a float32 value", flag.Config[float32]{}) test.Ok(t, err) err = floatFlag.Set("word") @@ -349,7 +349,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float64 valid", func(t *testing.T) { var f float64 - floatFlag, err := flag.New(&f, "float", 'f', 0, "Set a float64 value") + floatFlag, err := flag.New(&f, "float", 'f', "Set a float64 value", flag.Config[float64]{}) test.Ok(t, err) err = floatFlag.Set("3.14159") @@ -362,7 +362,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float64 invalid", func(t *testing.T) { var f float64 - floatFlag, err := flag.New(&f, "float", 'f', 0, "Set a float64 value") + floatFlag, err := flag.New(&f, "float", 'f', "Set a float64 value", flag.Config[float64]{}) test.Ok(t, err) err = floatFlag.Set("word") @@ -373,7 +373,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("bool valid", func(t *testing.T) { var b bool - boolFlag, err := flag.New(&b, "bool", 'b', false, "Set a bool value") + boolFlag, err := flag.New(&b, "bool", 'b', "Set a bool value", flag.Config[bool]{}) test.Ok(t, err) err = boolFlag.Set(format.True) @@ -386,7 +386,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("bool invalid", func(t *testing.T) { var b bool - boolFlag, err := flag.New(&b, "bool", 'b', false, "Set a bool value") + boolFlag, err := flag.New(&b, "bool", 'b', "Set a bool value", flag.Config[bool]{}) test.Ok(t, err) err = boolFlag.Set("word") @@ -399,7 +399,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("string", func(t *testing.T) { var str string - strFlag, err := flag.New(&str, "string", 's', "", "Set a string value") + strFlag, err := flag.New(&str, "string", 's', "Set a string value", flag.Config[string]{}) test.Ok(t, err) err = strFlag.Set("newvalue") @@ -412,7 +412,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("byte slice valid", func(t *testing.T) { var byt []byte - byteFlag, err := flag.New(&byt, "byte", 'b', []byte(""), "Set a byte slice value") + byteFlag, err := flag.New(&byt, "byte", 'b', "Set a byte slice value", flag.Config[[]byte]{}) test.Ok(t, err) err = byteFlag.Set("5e") @@ -425,7 +425,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("byte slice invalid", func(t *testing.T) { var byt []byte - byteFlag, err := flag.New(&byt, "byte", 'b', []byte(""), "Set a byte slice value") + byteFlag, err := flag.New(&byt, "byte", 'b', "Set a byte slice value", flag.Config[[]byte]{}) test.Ok(t, err) err = byteFlag.Set("0xF") @@ -436,7 +436,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("time.Time valid", func(t *testing.T) { var tyme time.Time - timeFlag, err := flag.New(&tyme, "time", 't', time.Now(), "Set a time value") + timeFlag, err := flag.New(&tyme, "time", 't', "Set a time value", flag.Config[time.Time]{}) test.Ok(t, err) err = timeFlag.Set("2024-07-17T07:38:05Z") @@ -452,7 +452,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("time.Time invalid", func(t *testing.T) { var tyme time.Time - timeFlag, err := flag.New(&tyme, "time", 't', time.Now(), "Set a time value") + timeFlag, err := flag.New(&tyme, "time", 't', "Set a time value", flag.Config[time.Time]{}) test.Ok(t, err) err = timeFlag.Set("not a time") @@ -467,8 +467,8 @@ func TestFlaggableTypes(t *testing.T) { &duration, "duration", 'd', - time.Duration(0), "Set a duration value", + flag.Config[time.Duration]{}, ) test.Ok(t, err) @@ -489,8 +489,8 @@ func TestFlaggableTypes(t *testing.T) { &duration, "duration", 'd', - time.Duration(0), "Set a duration value", + flag.Config[time.Duration]{}, ) test.Ok(t, err) @@ -502,7 +502,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("ip valid", func(t *testing.T) { var ip net.IP - ipFlag, err := flag.New(&ip, "ip", 'i', nil, "Set an IP address") + ipFlag, err := flag.New(&ip, "ip", 'i', "Set an IP address", flag.Config[net.IP]{}) test.Ok(t, err) err = ipFlag.Set("192.0.2.1") @@ -515,7 +515,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("ip invalid", func(t *testing.T) { var ip net.IP - ipFlag, err := flag.New(&ip, "ip", 'i', nil, "Set an IP address") + ipFlag, err := flag.New(&ip, "ip", 'i', "Set an IP address", flag.Config[net.IP]{}) test.Ok(t, err) err = ipFlag.Set("not an ip") @@ -526,7 +526,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int slice valid", func(t *testing.T) { var slice []int - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -547,7 +547,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int slice invalid", func(t *testing.T) { var slice []int - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int]{}) test.Ok(t, err) err = sliceFlag.Set("a word") @@ -558,7 +558,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int8 slice valid", func(t *testing.T) { var slice []int8 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int8]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -579,7 +579,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int8 slice invalid", func(t *testing.T) { var slice []int8 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int8]{}) test.Ok(t, err) err = sliceFlag.Set("cheese") @@ -590,7 +590,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int16 slice valid", func(t *testing.T) { var slice []int16 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int16]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -611,7 +611,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int16 slice invalid", func(t *testing.T) { var slice []int16 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int16]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -622,7 +622,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int32 slice valid", func(t *testing.T) { var slice []int32 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int32]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -643,7 +643,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int32 slice invalid", func(t *testing.T) { var slice []int32 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int32]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -654,7 +654,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int64 slice valid", func(t *testing.T) { var slice []int64 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int64]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -675,7 +675,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("int64 slice invalid", func(t *testing.T) { var slice []int64 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int64]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -686,7 +686,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint slice valid", func(t *testing.T) { var slice []uint - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of uints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of uints", flag.Config[[]uint]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -707,7 +707,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint slice invalid", func(t *testing.T) { var slice []uint - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of unsigned integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of unsigned integers", flag.Config[[]uint]{}) test.Ok(t, err) err = sliceFlag.Set("a word") @@ -718,7 +718,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint16 slice valid", func(t *testing.T) { var slice []uint16 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]uint16]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -739,7 +739,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint16 slice invalid", func(t *testing.T) { var slice []uint16 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]uint16]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -750,7 +750,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint32 slice valid", func(t *testing.T) { var slice []uint32 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]uint32]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -771,7 +771,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint32 slice invalid", func(t *testing.T) { var slice []uint32 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]uint32]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -782,7 +782,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint64 slice valid", func(t *testing.T) { var slice []uint64 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of ints") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]uint64]{}) test.Ok(t, err) err = sliceFlag.Set("1") // Append 1 to the slice @@ -803,7 +803,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("uint64 slice invalid", func(t *testing.T) { var slice []uint64 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of integers") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]uint64]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -814,7 +814,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float32 slice valid", func(t *testing.T) { var slice []float32 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of floats") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of floats", flag.Config[[]float32]{}) test.Ok(t, err) err = sliceFlag.Set("3.14159") // Append pi to the slice @@ -835,7 +835,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float32 slice invalid", func(t *testing.T) { var slice []float32 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of floats") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of floats", flag.Config[[]float32]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -846,7 +846,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float64 slice valid", func(t *testing.T) { var slice []float64 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of floats") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of floats", flag.Config[[]float64]{}) test.Ok(t, err) err = sliceFlag.Set("3.14159") // Append pi to the slice @@ -867,7 +867,7 @@ func TestFlaggableTypes(t *testing.T) { t.Run("float64 slice invalid", func(t *testing.T) { var slice []float64 - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Slice of floats") + sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of floats", flag.Config[[]float64]{}) test.Ok(t, err) err = sliceFlag.Set("balls") @@ -880,7 +880,7 @@ func TestFlaggableTypes(t *testing.T) { // it's impossible to make a bad one var slice []string - sliceFlag, err := flag.New(&slice, "slice", 's', nil, "Append to a slice of strings") + sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of strings", flag.Config[[]string]{}) test.Ok(t, err) err = sliceFlag.Set("a string") @@ -1003,7 +1003,7 @@ func TestFlagValidation(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := flag.New(new(string), tt.flagName, tt.short, "", "Test me") + _, err := flag.New(new(string), tt.flagName, tt.short, "Test me", flag.Config[string]{}) test.WantErr(t, err, tt.wantErr) if err != nil { @@ -1018,7 +1018,7 @@ func TestFlagNilSafety(t *testing.T) { // Should be impossible to make a nil pointer dereference when using .New var bang *bool - flag, err := flag.New(bang, "bang", 'b', false, "Nil go bang?") + flag, err := flag.New(bang, "bang", 'b', "Nil go bang?", flag.Config[bool]{}) test.Ok(t, err) test.Equal(t, flag.String(), "false") @@ -1040,7 +1040,7 @@ func TestFlagNilSafety(t *testing.T) { func BenchmarkFlagSet(b *testing.B) { var count int - flag, err := flag.New(&count, "count", 'c', 0, "Count things") + flag, err := flag.New(&count, "count", 'c', "Count things", flag.Config[int]{}) test.Ok(b, err) for b.Loop() { diff --git a/internal/flag/set_test.go b/internal/flag/set_test.go index aaa68f1..0e4b8af 100644 --- a/internal/flag/set_test.go +++ b/internal/flag/set_test.go @@ -205,7 +205,7 @@ func TestParse(t *testing.T) { { name: "bad syntax short trailing whitespace", newSet: func(t *testing.T) *flag.Set { - f, err := flag.New(new(bool), "delete", 'd', false, "Delete something") + f, err := flag.New(new(bool), "delete", 'd', "Delete something", flag.Config[bool]{}) test.Ok(t, err) set := flag.NewSet() @@ -268,7 +268,7 @@ func TestParse(t *testing.T) { name: "valid long", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "delete", 'd', false, "Delete something") + f, err := flag.New(new(bool), "delete", 'd', "Delete something", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -292,7 +292,7 @@ func TestParse(t *testing.T) { name: "valid long with args", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "delete", 'd', false, "Delete something") + f, err := flag.New(new(bool), "delete", 'd', "Delete something", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -317,7 +317,7 @@ func TestParse(t *testing.T) { name: "valid short", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "delete", 'd', false, "Delete something") + f, err := flag.New(new(bool), "delete", 'd', "Delete something", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -349,7 +349,7 @@ func TestParse(t *testing.T) { name: "valid shortvalue", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "number", 'n', 0, "Number of something") + f, err := flag.New(new(int), "number", 'n', "Number of something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -379,7 +379,7 @@ func TestParse(t *testing.T) { name: "valid short with args", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "delete", 'd', false, "Delete something") + f, err := flag.New(new(bool), "delete", 'd', "Delete something", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -411,7 +411,7 @@ func TestParse(t *testing.T) { name: "valid long value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -435,7 +435,7 @@ func TestParse(t *testing.T) { name: "valid long missing value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -460,7 +460,7 @@ func TestParse(t *testing.T) { name: "valid short missing value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -485,7 +485,7 @@ func TestParse(t *testing.T) { name: "valid long value with args", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -509,7 +509,7 @@ func TestParse(t *testing.T) { name: "invalid long value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(uint), "number", 'n', 0, "Uint") + f, err := flag.New(new(uint), "number", 'n', "Uint", flag.Config[uint]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -532,7 +532,7 @@ func TestParse(t *testing.T) { name: "valid short value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -564,7 +564,7 @@ func TestParse(t *testing.T) { name: "valid short value with args", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -596,7 +596,7 @@ func TestParse(t *testing.T) { name: "invalid short value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(uint), "number", 'n', 0, "Uint") + f, err := flag.New(new(uint), "number", 'n', "Uint", flag.Config[uint]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -619,7 +619,7 @@ func TestParse(t *testing.T) { name: "valid long equals value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -641,7 +641,7 @@ func TestParse(t *testing.T) { name: "valid long equals value with args", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -665,7 +665,7 @@ func TestParse(t *testing.T) { name: "invalid long equals value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(uint), "number", 'n', 0, "Uint") + f, err := flag.New(new(uint), "number", 'n', "Uint", flag.Config[uint]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -688,7 +688,7 @@ func TestParse(t *testing.T) { name: "valid short equals value", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -718,7 +718,7 @@ func TestParse(t *testing.T) { name: "valid short equals value with args", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -750,7 +750,7 @@ func TestParse(t *testing.T) { name: "no shorthand use long", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", publicflag.NoShortHand, 0, "Count something") + f, err := flag.New(new(int), "count", publicflag.NoShortHand, "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -778,7 +778,7 @@ func TestParse(t *testing.T) { name: "no shorthand use short", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "count", publicflag.NoShortHand, 0, "Count something") + f, err := flag.New(new(int), "count", publicflag.NoShortHand, "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -807,7 +807,7 @@ func TestParse(t *testing.T) { name: "valid count long", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(publicflag.Count), "count", 'c', 0, "Count something") + f, err := flag.New(new(publicflag.Count), "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -829,7 +829,7 @@ func TestParse(t *testing.T) { name: "valid count short", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(publicflag.Count), "count", 'c', 0, "Count something") + f, err := flag.New(new(publicflag.Count), "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -851,7 +851,7 @@ func TestParse(t *testing.T) { name: "valid count super short", newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(publicflag.Count), "count", 'c', 0, "Count something") + f, err := flag.New(new(publicflag.Count), "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -944,7 +944,7 @@ func TestFlagSet(t *testing.T) { return nil // uh oh }, test: func(t *testing.T, set *flag.Set) { - f, err := flag.New(new(bool), "force", 'f', false, "Force something") + f, err := flag.New(new(bool), "force", 'f', "Force something", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -984,7 +984,7 @@ func TestFlagSet(t *testing.T) { return flag.NewSet() }, test: func(t *testing.T, set *flag.Set) { - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1005,10 +1005,10 @@ func TestFlagSet(t *testing.T) { return flag.NewSet() }, test: func(t *testing.T, set *flag.Set) { - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) - f2, err := flag.New(new(uint), "count", 'c', 0, "Count something 2") + f2, err := flag.New(new(uint), "count", 'c', "Count something 2", flag.Config[uint]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1029,10 +1029,10 @@ func TestFlagSet(t *testing.T) { return flag.NewSet() }, test: func(t *testing.T, set *flag.Set) { - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(t, err) - f2, err := flag.New(new(string), "config", 'c', "", "Choose a config file") + f2, err := flag.New(new(string), "config", 'c', "Choose a config file", flag.Config[string]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1090,7 +1090,7 @@ func TestHelpVersion(t *testing.T) { newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "help", 'h', false, "Show help") + f, err := flag.New(new(bool), "help", 'h', "Show help", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1109,7 +1109,7 @@ func TestHelpVersion(t *testing.T) { newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "help", 'h', 0, "Show help") + f, err := flag.New(new(int), "help", 'h', "Show help", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1128,7 +1128,7 @@ func TestHelpVersion(t *testing.T) { newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "help", 'h', true, "Show help") + f, err := flag.New(new(bool), "help", 'h', "Show help", flag.Config[bool]{DefaultValue: true}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1147,7 +1147,7 @@ func TestHelpVersion(t *testing.T) { newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "version", 'v', false, "Show version") + f, err := flag.New(new(bool), "version", 'v', "Show version", flag.Config[bool]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1166,7 +1166,7 @@ func TestHelpVersion(t *testing.T) { newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(int), "version", 'v', 0, "Show version") + f, err := flag.New(new(int), "version", 'v', "Show version", flag.Config[int]{}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1185,7 +1185,7 @@ func TestHelpVersion(t *testing.T) { newSet: func(t *testing.T) *flag.Set { set := flag.NewSet() - f, err := flag.New(new(bool), "version", 'v', true, "Show version") + f, err := flag.New(new(bool), "version", 'v', "Show version", flag.Config[bool]{DefaultValue: true}) test.Ok(t, err) err = flag.AddToSet(set, f) @@ -1217,10 +1217,10 @@ func TestUsage(t *testing.T) { { name: "simple", newSet: func(t *testing.T) *flag.Set { - help, err := flag.New(new(bool), "help", 'h', false, "Show help for test") + help, err := flag.New(new(bool), "help", 'h', "Show help for test", flag.Config[bool]{}) test.Ok(t, err) - version, err := flag.New(new(bool), "version", 'V', false, "Show version info for test") + version, err := flag.New(new(bool), "version", 'V', "Show version info for test", flag.Config[bool]{}) test.Ok(t, err) set := flag.NewSet() @@ -1237,13 +1237,13 @@ func TestUsage(t *testing.T) { { name: "no shorthand", newSet: func(t *testing.T) *flag.Set { - help, err := flag.New(new(bool), "help", 'h', false, "Show help for test") + help, err := flag.New(new(bool), "help", 'h', "Show help for test", flag.Config[bool]{}) test.Ok(t, err) - version, err := flag.New(new(bool), "version", 'V', false, "Show version info for test") + version, err := flag.New(new(bool), "version", 'V', "Show version info for test", flag.Config[bool]{}) test.Ok(t, err) - up, err := flag.New(new(bool), "update", publicflag.NoShortHand, false, "Update something") + up, err := flag.New(new(bool), "update", publicflag.NoShortHand, "Update something", flag.Config[bool]{}) test.Ok(t, err) set := flag.NewSet() @@ -1263,19 +1263,19 @@ func TestUsage(t *testing.T) { { name: "full", newSet: func(t *testing.T) *flag.Set { - help, err := flag.New(new(bool), "help", 'h', false, "Show help for test") + help, err := flag.New(new(bool), "help", 'h', "Show help for test", flag.Config[bool]{}) test.Ok(t, err) - version, err := flag.New(new(bool), "version", 'V', false, "Show version info for test") + version, err := flag.New(new(bool), "version", 'V', "Show version info for test", flag.Config[bool]{}) test.Ok(t, err) - up, err := flag.New(new(bool), "update", publicflag.NoShortHand, false, "Update something") + up, err := flag.New(new(bool), "update", publicflag.NoShortHand, "Update something", flag.Config[bool]{}) test.Ok(t, err) - count, err := flag.New(new(int), "count", 'c', 0, "Count things") + count, err := flag.New(new(int), "count", 'c', "Count things", flag.Config[int]{}) test.Ok(t, err) - thing, err := flag.New(new(string), "thing", 't', "", "Name the thing") + thing, err := flag.New(new(string), "thing", 't', "Name the thing", flag.Config[string]{}) test.Ok(t, err) set := flag.NewSet() @@ -1319,13 +1319,13 @@ func TestUsage(t *testing.T) { func BenchmarkParse(b *testing.B) { set := flag.NewSet() - f, err := flag.New(new(int), "count", 'c', 0, "Count something") + f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{}) test.Ok(b, err) - f2, err := flag.New(new(bool), "delete", 'd', false, "Delete something") + f2, err := flag.New(new(bool), "delete", 'd', "Delete something", flag.Config[bool]{}) test.Ok(b, err) - f3, err := flag.New(new(string), "name", 'n', "", "Name something") + f3, err := flag.New(new(string), "name", 'n', "Name something", flag.Config[string]{}) test.Ok(b, err) err = flag.AddToSet(set, f) diff --git a/option.go b/option.go index b8a3f22..829b83b 100644 --- a/option.go +++ b/option.go @@ -388,9 +388,9 @@ func SubCommands(builders ...Builder) Option { // Flag is an [Option] that adds a typed flag to a [Command], storing its value in a variable via its // pointer 'target'. // -// The variable is set when the flag is parsed during command execution. The value provided -// by the 'value' argument to [Flag] is used as the default value, which will be used if the -// flag value was not given via the command line. +// The variable is set when the flag is parsed during command execution. By default, the flag +// will assume the zero value for its type, the default may be provided explicitly using +// the [FlagDefault] option. // // If the default value is not the zero value for the type T, the flags usage message will // show the default value in the commands help text. @@ -402,14 +402,22 @@ func SubCommands(builders ...Builder) Option { // // // Add a force flag // var force bool -// cli.New("rm", cli.Flag(&force, "force", 'f', false, "Force deletion without confirmation")) -func Flag[T flag.Flaggable](target *T, name string, short rune, value T, usage string) Option { +// cli.New("rm", cli.Flag(&force, "force", 'f', "Force deletion without confirmation")) +func Flag[T flag.Flaggable](target *T, name string, short rune, usage string, options ...FlagOption[T]) Option { f := func(cfg *config) error { if _, ok := cfg.flags.Get(name); ok { return fmt.Errorf("flag %q already defined", name) } - f, err := internalflag.New(target, name, short, value, usage) + var flagCfg internalflag.Config[T] + + for _, option := range options { + if err := option.apply(&flagCfg); err != nil { + return fmt.Errorf("could not apply flag option: %w", err) + } + } + + f, err := internalflag.New(target, name, short, usage, flagCfg) if err != nil { return err } @@ -479,6 +487,20 @@ func ArgDefault[T arg.Argable](value T) ArgOption[T] { return argOption[T](f) } +// FlagDefault is a [cli.FlagOption] that sets the default value for command line flag. +// +// By default, a flag's default value is the zero value for its type. But using this +// option, you may set a non-zero default value that the flag should inherit if not +// provided on the command line. +func FlagDefault[T flag.Flaggable](value T) FlagOption[T] { + f := func(cfg *internalflag.Config[T]) error { + cfg.DefaultValue = value + return nil + } + + return flagOption[T](f) +} + // anyDuplicates checks the list of commands for ones with duplicate names, if a duplicate // is found, it's name and true are returned, else "", false. func anyDuplicates(cmds ...*Command) (string, bool) { @@ -516,3 +538,21 @@ type argOption[T arg.Argable] func(cfg *internalarg.Config[T]) error func (a argOption[T]) apply(cfg *internalarg.Config[T]) error { return a(cfg) } + +// FlagOption is a functional option for configuring a [Flag]. +type FlagOption[T flag.Flaggable] interface { + // Apply the option to the config, returning an error if the + // option cannot be applied for whatever reason. + apply(cfg *internalflag.Config[T]) error +} + +// option is a function adapter implementing the Option interface, analogous +// to http.HandlerFunc. +type flagOption[T flag.Flaggable] func(cfg *internalflag.Config[T]) error + +// apply implements the Option interface for option. +// +//nolint:unused // This is a false positive, this has to be here +func (f flagOption[T]) apply(cfg *internalflag.Config[T]) error { + return f(cfg) +}