From 2f3420689cb28bec583f29f1dd88698a94bbd08c Mon Sep 17 00:00:00 2001 From: sttk Date: Sun, 17 Mar 2024 22:12:00 +0900 Subject: [PATCH] update!: modified OptCfg fields to separate store key and option name. --- doc.go | 62 +++--- example_invalid-option_test.go | 2 +- example_parse-for_test.go | 84 ++++---- example_parse-with_test.go | 26 +-- parse-for.go | 53 +++-- parse-for_test.go | 50 ++--- parse-with.go | 263 +++++++++++++++---------- parse-with_test.go | 348 +++++++++++++++++++++++---------- print-help.go | 51 +++-- print-help_test.go | 192 +++++++++--------- 10 files changed, 667 insertions(+), 464 deletions(-) diff --git a/doc.go b/doc.go index 2d67083..a05909e 100644 --- a/doc.go +++ b/doc.go @@ -49,29 +49,31 @@ This function takes an array of option configurations: []OptCfg as the second argument, and divides command line arguments to options and command arguments with this configurations. -An option configuration has fields: Name, Aliases, HasArg, IsArray, Defaults, -Desc, and ArgHelp. +An option configuration has fields: StoreKey, Names, HasArg, IsArray, Defaults, +Desc, and ArgInHelp. + Name field is an option name and it is used as an argument of the functions: Cmd#HasOpt, Cmd#OptArg, and Cmd#OptArgs. Aliases field is an array of option aliases. + HasArg field indicates the option requires one or more values. IsArray field indicates the option can have multiple values. Defaults field is an array of string which is used as default one or more values if the option is not specified. Desc field is a description of the option for help text. -ArgHelp field is a text which is output after option name and aliases as an +ArgInHelp field is a text which is output after option name and aliases as an option value in help text. // osArgs := []string{"app", "--foo-bar", "hoge", "--baz", "1", "-z=2", "-x" "fuga"} optCfgs := []cliargs.OptCfg{ cliargs.OptCfg{ - Name:"foo-bar", - Desc:"This is description of foo-bar.", + StoreKey: "FooBar", + Names: []string{"foo-bar"}, + Desc: "This is description of foo-bar.", }, cliargs.OptCfg{ - Name:"baz", - Aliases:[]string{"z"}, + Names: []string{"baz", "z"}, HasArg:true, IsArray: true, Defaults: [9,8,7], @@ -79,7 +81,7 @@ option value in help text. ArgHelp:"", }, cliargs.OptCfg{ - Name:"*", + Names: []string{"*"}, Desc: "(Any options are accepted)", }, } @@ -87,13 +89,13 @@ option value in help text. cmd, err := cliargs.ParseWith(optCfgs) cmd.Name // app cmd.Args() // [hoge fuga] - cmd.HasOpt("foo-bar") // true + cmd.HasOpt("FooBar") // true cmd.HasOpt("baz") // true cmd.HasOpt("x") // true, due to "*" config - cmd.OptArg("foo-bar") // true + cmd.OptArg("FooBar") // true cmd.OptArg("baz") // 1 cmd.OptArg("x") // true - cmd.OptArgs("foo-bar") // [] + cmd.OptArgs("FooBar") // [] cmd.OptArgs("baz") // [1 2] cmd.OptArgs("x") // [] @@ -121,7 +123,7 @@ This function creates a Cmd instance and also an array of OptCfg which is transformed from these struct tags and is used to parse command line arguments. The struct tags used in a option store struct are optcfg, optdesc, and optarg. -optcfg is what to specify option configurations other than Desc and AtgHelp. +optcfg is what to specify option configurations other than Desc and AtgInHelp. The format of optcfg is as follows: `optcfg:"name"` // only name @@ -146,15 +148,15 @@ And optarg is what to specify a text for an option argument value in help text. cmd, optCfgs, err := cliargs.ParseFor(osArgs, &options) cmd.Name // app cmd.Args() // [hoge fuga] - cmd.HasOpt("foo-bar") // true - cmd.HasOpt("baz") // true - cmd.HasOpt("x") // true - cmd.OptArg("foo-bar") // true - cmd.OptArg("baz") // 1 - cmd.OptArg("x") // true - cmd.OptArgs("foo-bar") // [] - cmd.OptArgs("baz") // [1 2] - cmd.OptArgs("x") // [] + cmd.HasOpt("FooBar") // true + cmd.HasOpt("Baz") // true + cmd.HasOpt("Qux") // true + cmd.OptArg("FooBar") // true + cmd.OptArg("Baz") // 1 + cmd.OptArg("Qux") // true + cmd.OptArgs("FooBar") // [] + cmd.OptArgs("Baz") // [1 2] + cmd.OptArgs("Qux") // [] options.FooBar // true options.Baz // [1 2] @@ -162,31 +164,31 @@ And optarg is what to specify a text for an option argument value in help text. optCfgs // []OptCfg{ // OptCfg{ - // Name: "foo-bar", - // Aliases: []string{}, + // StoreKey: "FooBar", + // Names: []string{"foo-bar"}, // Desc: "This is description of foo-bar.", // HasArg: false, // IsArray: false, // Defaults: []string(nil), - // ArgHelp: "", + // ArgInHelp: "", // }, // OptCfg{ - // Name: "baz", - // Aliases: []string{"z"}, + // StoreKey: "Baz", + // Aliases: []string{"baz", "z"}, // Desc: "This is description of baz.", // HasArg: true, // IsArray: true, // Defaults: []string{"9","8","7"}, - // ArgHelp: "", + // ArgInHelp: "", // }, // OptCfg{ - // Name: "qux", - // Aliases: []string{"x"}, + // StoreKey: "Qux", + // Aliases: []string{"qux", "x"}, // Desc: "This is description of qux.", // HasArg: false, // IsArray: false, // Defaults: []string(nil), - // ArgHelp: "", + // ArgInHelp: "", // }, // } diff --git a/example_invalid-option_test.go b/example_invalid-option_test.go index e62dd60..7034ad9 100644 --- a/example_invalid-option_test.go +++ b/example_invalid-option_test.go @@ -10,7 +10,7 @@ import ( func ExampleInvalidOption() { optCfgs := []cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo", + Names: []string{"foo"}, Defaults: []string{"123"}, HasArg: false, }, diff --git a/example_parse-for_test.go b/example_parse-for_test.go index a588a46..2b0f345 100644 --- a/example_parse-for_test.go +++ b/example_parse-for_test.go @@ -24,28 +24,28 @@ func ExampleParseFor() { fmt.Printf("cmd.Name = %v\n", cmd.Name) fmt.Printf("cmd.Args() = %v\n", cmd.Args()) - fmt.Printf("optCfgs[0].Name = %v\n", optCfgs[0].Name) - fmt.Printf("optCfgs[0].Aliases = %v\n", optCfgs[0].Aliases) + fmt.Printf("optCfgs[0].StoreKey = %v\n", optCfgs[0].StoreKey) + fmt.Printf("optCfgs[0].Names = %v\n", optCfgs[0].Names) fmt.Printf("optCfgs[0].HasArg = %v\n", optCfgs[0].HasArg) fmt.Printf("optCfgs[0].IsArray = %v\n", optCfgs[0].IsArray) fmt.Printf("optCfgs[0].Defaults = %v\n", optCfgs[0].Defaults) fmt.Printf("optCfgs[0].Desc = %v\n", optCfgs[0].Desc) - fmt.Printf("optCfgs[1].Name = %v\n", optCfgs[1].Name) - fmt.Printf("optCfgs[1].Aliases = %v\n", optCfgs[1].Aliases) + fmt.Printf("optCfgs[1].StoreKey = %v\n", optCfgs[1].StoreKey) + fmt.Printf("optCfgs[1].Names = %v\n", optCfgs[1].Names) fmt.Printf("optCfgs[1].HasArg = %v\n", optCfgs[1].HasArg) fmt.Printf("optCfgs[1].IsArray = %v\n", optCfgs[1].IsArray) fmt.Printf("optCfgs[1].Defaults = %v\n", optCfgs[1].Defaults) fmt.Printf("optCfgs[1].Desc = %v\n", optCfgs[1].Desc) - fmt.Printf("optCfgs[1].ArgHelp = %v\n", optCfgs[1].ArgHelp) + fmt.Printf("optCfgs[1].ArgInHelp = %v\n", optCfgs[1].ArgInHelp) - fmt.Printf("optCfgs[2].Name = %v\n", optCfgs[2].Name) - fmt.Printf("optCfgs[2].Aliases = %v\n", optCfgs[2].Aliases) + fmt.Printf("optCfgs[2].StoreKey = %v\n", optCfgs[2].StoreKey) + fmt.Printf("optCfgs[2].Names = %v\n", optCfgs[2].Names) fmt.Printf("optCfgs[2].HasArg = %v\n", optCfgs[2].HasArg) fmt.Printf("optCfgs[2].IsArray = %v\n", optCfgs[2].IsArray) fmt.Printf("optCfgs[2].Defaults = %v\n", optCfgs[2].Defaults) fmt.Printf("optCfgs[2].Desc = %v\n", optCfgs[2].Desc) - fmt.Printf("optCfgs[2].ArgHelp = %v\n", optCfgs[2].ArgHelp) + fmt.Printf("optCfgs[2].ArgInHelp = %v\n", optCfgs[2].ArgInHelp) fmt.Printf("options.FooBar = %v\n", options.FooBar) fmt.Printf("options.Baz = %v\n", options.Baz) @@ -55,26 +55,26 @@ func ExampleParseFor() { // err = // cmd.Name = app // cmd.Args() = [c1 c2] - // optCfgs[0].Name = foo-bar - // optCfgs[0].Aliases = [f] + // optCfgs[0].StoreKey = FooBar + // optCfgs[0].Names = [foo-bar f] // optCfgs[0].HasArg = false // optCfgs[0].IsArray = false // optCfgs[0].Defaults = [] // optCfgs[0].Desc = FooBar description. - // optCfgs[1].Name = baz - // optCfgs[1].Aliases = [b] + // optCfgs[1].StoreKey = Baz + // optCfgs[1].Names = [baz b] // optCfgs[1].HasArg = true // optCfgs[1].IsArray = false // optCfgs[1].Defaults = [99] // optCfgs[1].Desc = Baz description. - // optCfgs[1].ArgHelp = - // optCfgs[2].Name = qux - // optCfgs[2].Aliases = [q] + // optCfgs[1].ArgInHelp = + // optCfgs[2].StoreKey = Qux + // optCfgs[2].Names = [qux q] // optCfgs[2].HasArg = true // optCfgs[2].IsArray = true // optCfgs[2].Defaults = [A B C] // optCfgs[2].Desc = Qux description. - // optCfgs[2].ArgHelp = + // optCfgs[2].ArgInHelp = // options.FooBar = true // options.Baz = 12 // options.Qux = [D E] @@ -94,39 +94,39 @@ func ExampleMakeOptCfgsFor() { fmt.Printf("err = %v\n", err) fmt.Printf("len(optCfgs) = %v\n", len(optCfgs)) fmt.Println() - fmt.Printf("optCfgs[0].Name = %v\n", optCfgs[0].Name) - fmt.Printf("optCfgs[0].Aliases = %v\n", optCfgs[0].Aliases) + fmt.Printf("optCfgs[0].StoreKey = %v\n", optCfgs[0].StoreKey) + fmt.Printf("optCfgs[0].Names = %v\n", optCfgs[0].Names) fmt.Printf("optCfgs[0].HasArg = %v\n", optCfgs[0].HasArg) fmt.Printf("optCfgs[0].IsArray = %v\n", optCfgs[0].IsArray) fmt.Printf("optCfgs[0].Defaults = %v\n", optCfgs[0].Defaults) fmt.Printf("optCfgs[0].Desc = %v\n", optCfgs[0].Desc) fmt.Println() - fmt.Printf("optCfgs[1].Name = %v\n", optCfgs[1].Name) - fmt.Printf("optCfgs[1].Aliases = %v\n", optCfgs[1].Aliases) + fmt.Printf("optCfgs[1].StoreKey = %v\n", optCfgs[1].StoreKey) + fmt.Printf("optCfgs[1].Names = %v\n", optCfgs[1].Names) fmt.Printf("optCfgs[1].HasArg = %v\n", optCfgs[1].HasArg) fmt.Printf("optCfgs[1].IsArray = %v\n", optCfgs[1].IsArray) fmt.Printf("optCfgs[1].Defaults = %v\n", optCfgs[1].Defaults) fmt.Printf("optCfgs[1].Desc = %v\n", optCfgs[1].Desc) - fmt.Printf("optCfgs[1].ArgHelp = %v\n", optCfgs[1].ArgHelp) + fmt.Printf("optCfgs[1].ArgInHelp = %v\n", optCfgs[1].ArgInHelp) fmt.Println() - fmt.Printf("optCfgs[2].Name = %v\n", optCfgs[2].Name) - fmt.Printf("optCfgs[2].Aliases = %v\n", optCfgs[2].Aliases) + fmt.Printf("optCfgs[2].StoreKey = %v\n", optCfgs[2].StoreKey) + fmt.Printf("optCfgs[2].Names = %v\n", optCfgs[2].Names) fmt.Printf("optCfgs[2].HasArg = %v\n", optCfgs[2].HasArg) fmt.Printf("optCfgs[2].IsArray = %v\n", optCfgs[2].IsArray) fmt.Printf("optCfgs[2].Defaults = %v\n", optCfgs[2].Defaults) fmt.Printf("optCfgs[2].Desc = %v\n", optCfgs[2].Desc) - fmt.Printf("optCfgs[2].ArgHelp = %v\n", optCfgs[2].ArgHelp) + fmt.Printf("optCfgs[2].ArgInHelp = %v\n", optCfgs[2].ArgInHelp) fmt.Println() - fmt.Printf("optCfgs[3].Name = %v\n", optCfgs[3].Name) - fmt.Printf("optCfgs[3].Aliases = %v\n", optCfgs[3].Aliases) + fmt.Printf("optCfgs[3].StoreKey = %v\n", optCfgs[3].StoreKey) + fmt.Printf("optCfgs[3].Names = %v\n", optCfgs[3].Names) fmt.Printf("optCfgs[3].HasArg = %v\n", optCfgs[3].HasArg) fmt.Printf("optCfgs[3].IsArray = %v\n", optCfgs[3].IsArray) fmt.Printf("optCfgs[3].Defaults = %v\n", optCfgs[3].Defaults) fmt.Printf("optCfgs[3].Desc = %v\n", optCfgs[3].Desc) - fmt.Printf("optCfgs[3].ArgHelp = %v\n", optCfgs[3].ArgHelp) + fmt.Printf("optCfgs[3].ArgInHelp = %v\n", optCfgs[3].ArgInHelp) fmt.Println() - fmt.Printf("optCfgs[4].Name = %v\n", optCfgs[4].Name) - fmt.Printf("optCfgs[4].Aliases = %v\n", optCfgs[4].Aliases) + fmt.Printf("optCfgs[4].StoreKey = %v\n", optCfgs[4].StoreKey) + fmt.Printf("optCfgs[4].Names = %v\n", optCfgs[4].Names) fmt.Printf("optCfgs[4].HasArg = %v\n", optCfgs[4].HasArg) fmt.Printf("optCfgs[4].IsArray = %v\n", optCfgs[4].IsArray) fmt.Printf("optCfgs[4].Defaults = %v\n", optCfgs[4].Defaults) @@ -136,39 +136,39 @@ func ExampleMakeOptCfgsFor() { // err = // len(optCfgs) = 5 // - // optCfgs[0].Name = foo-bar - // optCfgs[0].Aliases = [f] + // optCfgs[0].StoreKey = FooBar + // optCfgs[0].Names = [foo-bar f] // optCfgs[0].HasArg = false // optCfgs[0].IsArray = false // optCfgs[0].Defaults = [] // optCfgs[0].Desc = FooBar description // - // optCfgs[1].Name = baz - // optCfgs[1].Aliases = [b] + // optCfgs[1].StoreKey = Baz + // optCfgs[1].Names = [baz b] // optCfgs[1].HasArg = true // optCfgs[1].IsArray = false // optCfgs[1].Defaults = [99] // optCfgs[1].Desc = Baz description - // optCfgs[1].ArgHelp = + // optCfgs[1].ArgInHelp = // - // optCfgs[2].Name = Qux - // optCfgs[2].Aliases = [] + // optCfgs[2].StoreKey = Qux + // optCfgs[2].Names = [Qux] // optCfgs[2].HasArg = true // optCfgs[2].IsArray = false // optCfgs[2].Defaults = [XXX] // optCfgs[2].Desc = Qux description - // optCfgs[2].ArgHelp = + // optCfgs[2].ArgInHelp = // - // optCfgs[3].Name = quux - // optCfgs[3].Aliases = [] + // optCfgs[3].StoreKey = Quux + // optCfgs[3].Names = [quux] // optCfgs[3].HasArg = true // optCfgs[3].IsArray = true // optCfgs[3].Defaults = [A B C] // optCfgs[3].Desc = Quux description - // optCfgs[3].ArgHelp = + // optCfgs[3].ArgInHelp = // - // optCfgs[4].Name = Corge - // optCfgs[4].Aliases = [] + // optCfgs[4].StoreKey = Corge + // optCfgs[4].Names = [Corge] // optCfgs[4].HasArg = true // optCfgs[4].IsArray = true // optCfgs[4].Defaults = [] diff --git a/example_parse-with_test.go b/example_parse-with_test.go index 3d62514..2befa91 100644 --- a/example_parse-with_test.go +++ b/example_parse-with_test.go @@ -12,21 +12,21 @@ func ExampleParseWith() { } optCfgs := []cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", + Names: []string{"foo-bar"}, }, cliargs.OptCfg{ - Name: "baz", - Aliases: []string{"z"}, - HasArg: true, - IsArray: true, + StoreKey: "Bazoo", + Names: []string{"baz", "z"}, + HasArg: true, + IsArray: true, }, cliargs.OptCfg{ - Name: "corge", + Names: []string{"corge"}, HasArg: true, Defaults: []string{"99"}, }, cliargs.OptCfg{ - Name: "*", + StoreKey: "*", }, } @@ -34,12 +34,12 @@ func ExampleParseWith() { fmt.Printf("err = %v\n", err) fmt.Printf("cmd.Name = %v\n", cmd.Name) fmt.Printf("cmd.HasOpt(\"foo-bar\") = %v\n", cmd.HasOpt("foo-bar")) - fmt.Printf("cmd.HasOpt(\"baz\") = %v\n", cmd.HasOpt("baz")) + fmt.Printf("cmd.HasOpt(\"Bazoo\") = %v\n", cmd.HasOpt("Bazoo")) fmt.Printf("cmd.HasOpt(\"X\") = %v\n", cmd.HasOpt("X")) fmt.Printf("cmd.HasOpt(\"corge\") = %v\n", cmd.HasOpt("corge")) - fmt.Printf("cmd.OptArg(\"baz\") = %v\n", cmd.OptArg("baz")) + fmt.Printf("cmd.OptArg(\"Bazoo\") = %v\n", cmd.OptArg("Bazoo")) fmt.Printf("cmd.OptArg(\"corge\") = %v\n", cmd.OptArg("corge")) - fmt.Printf("cmd.OptArgs(\"baz\") = %v\n", cmd.OptArgs("baz")) + fmt.Printf("cmd.OptArgs(\"Bazoo\") = %v\n", cmd.OptArgs("Bazoo")) fmt.Printf("cmd.OptArgs(\"corge\") = %v\n", cmd.OptArgs("corge")) fmt.Printf("cmd.Args() = %v\n", cmd.Args()) @@ -47,12 +47,12 @@ func ExampleParseWith() { // err = // cmd.Name = app // cmd.HasOpt("foo-bar") = true - // cmd.HasOpt("baz") = true + // cmd.HasOpt("Bazoo") = true // cmd.HasOpt("X") = true // cmd.HasOpt("corge") = true - // cmd.OptArg("baz") = 1 + // cmd.OptArg("Bazoo") = 1 // cmd.OptArg("corge") = 99 - // cmd.OptArgs("baz") = [1 2] + // cmd.OptArgs("Bazoo") = [1 2] // cmd.OptArgs("corge") = [99] // cmd.Args() = [qux quux] } diff --git a/parse-for.go b/parse-for.go index 885c1d1..f16feec 100644 --- a/parse-for.go +++ b/parse-for.go @@ -107,25 +107,25 @@ func (e IllegalOptionType) Error() string { // ParseFor is the function to parse command line arguments and set their // values to the option store which is the second argument of this function. // This function divides command line arguments to command arguments and -// options, then stores the options to the option store, and returns the -// command arguments with the generated option configuratins. +// options, then sets the options to the option store, and returns the +// command arguments with the generated option configurations. // // The configurations of options are determined by types and struct tags of // fields of the option store. // If the type is bool, the option takes no argument. // If the type is integer, floating point number or string, the option can -// takes one option argument, therefore it can appear once in command line +// takes single option argument, therefore it can appear once in command line // arguments. // If the type is an array, the option can takes multiple option arguments, // therefore it can appear multiple times in command line arguments. // -// A struct tag can specify an option name, aliases, and a default value. -// It has a special format, like `opt:foo-bar,f=123`. +// A struct tag can be specified an option names and default value(s). +// It has a special format like `opt:foo-bar,f=123`. // This opt: is the struct tag key for the option configuration. -// The string following this key and rounded by double quotes is the content -// of the option configuration. -// The first part of the option configuration is an option name and aliases, -// which are separated by commas, and ends with "=" mark or end of string. +// The string following this key and rounded by double quotes is the content of +// the option configuration. +// The first part of the option configuration is an option names, which are +// separated by commas, and ends with "=" mark or end of string. // If the option name is empty or no struct tag, the option's name becomes same // with the field name of the option store. // @@ -137,8 +137,8 @@ func (e IllegalOptionType) Error() string { // If the type of the option is an array, the string after "=" mark have to be // rounded by square brackets and separate the elements with commas, like // [elem1,elem2,elem3]. -// The element separator can be used other than a comma by put the separator -// before the open square bracket, like :[elem1:elem2:elem3]. +// The element separator can be used other than a comma by putting the +// separator before the open square bracket, like :[elem1:elem2:elem3]. // It's useful when some array elements include commas. // // NOTE: A default value of an empty string array option in a struct tag is [], @@ -173,13 +173,12 @@ func MakeOptCfgsFor(options any) ([]OptCfg, error) { n := t.NumField() optCfgs := make([]OptCfg, n) - var err error for i := 0; i < n; i++ { optCfgs[i] = newOptCfg(t.Field(i)) - var setter func([]string) error - setter, err = newValueSetter(optCfgs[i].Name, t.Field(i).Name, v.Field(i)) + setter, err := newValueSetter( + optCfgs[i].Names[0], t.Field(i).Name, v.Field(i)) if err != nil { return nil, err } @@ -190,18 +189,14 @@ func MakeOptCfgsFor(options any) ([]OptCfg, error) { } func newOptCfg(fld reflect.StructField) OptCfg { + storeKey := fld.Name + opt := fld.Tag.Get("optcfg") arr := strings.SplitN(opt, "=", 2) - names := strings.Split(arr[0], ",") - var name string - var aliases []string + names := strings.Split(arr[0], ",") if len(names) == 0 || len(names[0]) == 0 { - name = fld.Name - aliases = nil - } else { - name = names[0] - aliases = names[1:] + names = []string{storeKey} } isArray := false @@ -246,13 +241,13 @@ func newOptCfg(fld reflect.StructField) OptCfg { desc := fld.Tag.Get("optdesc") return OptCfg{ - Name: name, - Aliases: aliases, - HasArg: hasArg, - IsArray: isArray, - Defaults: defaults, - Desc: desc, - ArgHelp: optArg, + StoreKey: storeKey, + Names: names, + HasArg: hasArg, + IsArray: isArray, + Defaults: defaults, + Desc: desc, + ArgInHelp: optArg, } } diff --git a/parse-for_test.go b/parse-for_test.go index ddf1dbd..b38f0da 100644 --- a/parse-for_test.go +++ b/parse-for_test.go @@ -1515,32 +1515,32 @@ func TestMakeOptCfgsFor_multipleOptsAndMultipleArgs(t *testing.T) { optCfgs, err0 := cliargs.MakeOptCfgsFor(&options) assert.Nil(t, err0) - assert.Equal(t, optCfgs[0].Name, "foo-bar") - assert.Equal(t, optCfgs[0].Aliases, []string{"f"}) + assert.Equal(t, optCfgs[0].StoreKey, "FooBar") + assert.Equal(t, optCfgs[0].Names, []string{"foo-bar", "f"}) assert.False(t, optCfgs[0].HasArg) assert.False(t, optCfgs[0].IsArray) assert.Nil(t, optCfgs[0].Defaults) assert.NotNil(t, optCfgs[0].OnParsed) - assert.Equal(t, optCfgs[1].Name, "baz") - assert.Equal(t, optCfgs[1].Aliases, []string{"b"}) + assert.Equal(t, optCfgs[1].StoreKey, "Baz") + assert.Equal(t, optCfgs[1].Names, []string{"baz", "b"}) assert.True(t, optCfgs[1].HasArg) assert.False(t, optCfgs[1].IsArray) assert.Equal(t, optCfgs[1].Defaults, []string{"99"}) assert.NotNil(t, optCfgs[1].OnParsed) - assert.Equal(t, optCfgs[2].Name, "Qux") - assert.Equal(t, optCfgs[2].Aliases, []string(nil)) + assert.Equal(t, optCfgs[2].StoreKey, "Qux") + assert.Equal(t, optCfgs[2].Names, []string{"Qux"}) assert.True(t, optCfgs[2].HasArg) assert.False(t, optCfgs[2].IsArray) assert.Equal(t, optCfgs[2].Defaults, []string{"XXX"}) assert.NotNil(t, optCfgs[2].OnParsed) - assert.Equal(t, optCfgs[3].Name, "quux") - assert.Equal(t, optCfgs[3].Aliases, []string{}) + assert.Equal(t, optCfgs[3].StoreKey, "Quux") + assert.Equal(t, optCfgs[3].Names, []string{"quux"}) assert.True(t, optCfgs[3].HasArg) assert.True(t, optCfgs[3].IsArray) assert.Equal(t, optCfgs[3].Defaults, []string{"A", "B", "C"}) assert.NotNil(t, optCfgs[3].OnParsed) - assert.Equal(t, optCfgs[4].Name, "Corge") - assert.Equal(t, optCfgs[4].Aliases, []string(nil)) + assert.Equal(t, optCfgs[4].StoreKey, "Corge") + assert.Equal(t, optCfgs[4].Names, []string{"Corge"}) assert.True(t, optCfgs[4].HasArg) assert.True(t, optCfgs[4].IsArray) assert.Equal(t, optCfgs[4].Defaults, []string(nil)) @@ -1550,20 +1550,20 @@ func TestMakeOptCfgsFor_multipleOptsAndMultipleArgs(t *testing.T) { assert.Nil(t, err1) assert.Equal(t, cmd.Name, "app") assert.Equal(t, cmd.Args(), []string{"c1", "c2"}) - assert.True(t, cmd.HasOpt("foo-bar")) - assert.True(t, cmd.HasOpt("baz")) + assert.True(t, cmd.HasOpt("FooBar")) + assert.True(t, cmd.HasOpt("Baz")) assert.True(t, cmd.HasOpt("Qux")) - assert.True(t, cmd.HasOpt("quux")) + assert.True(t, cmd.HasOpt("Quux")) assert.True(t, cmd.HasOpt("Corge")) - assert.Equal(t, cmd.OptArg("foo-bar"), "") - assert.Equal(t, cmd.OptArg("baz"), "12") + assert.Equal(t, cmd.OptArg("FooBar"), "") + assert.Equal(t, cmd.OptArg("Baz"), "12") assert.Equal(t, cmd.OptArg("Qux"), "ABC") - assert.Equal(t, cmd.OptArg("quux"), "A") + assert.Equal(t, cmd.OptArg("Quux"), "A") assert.Equal(t, cmd.OptArg("Corge"), "20") - assert.Equal(t, cmd.OptArgs("foo-bar"), []string{}) - assert.Equal(t, cmd.OptArgs("baz"), []string{"12"}) + assert.Equal(t, cmd.OptArgs("FooBar"), []string{}) + assert.Equal(t, cmd.OptArgs("Baz"), []string{"12"}) assert.Equal(t, cmd.OptArgs("Qux"), []string{"ABC"}) - assert.Equal(t, cmd.OptArgs("quux"), []string{"A", "B", "C"}) + assert.Equal(t, cmd.OptArgs("Quux"), []string{"A", "B", "C"}) assert.Equal(t, cmd.OptArgs("Corge"), []string{"20", "21"}) assert.True(t, options.FooBar) assert.Equal(t, options.Baz, 12) @@ -1577,7 +1577,7 @@ func TestParseFor_emptyArrayOfDefaultValueWithNotCommaSeparator(t *testing.T) { Foo []int `optcfg:"foo=/[]"` Bar []uint `optcfg:"bar=|[]"` Baz []float64 `optcfg:"baz=@[]"` - Qux []string `optcfg:"baz=![]"` + Qux []string `optcfg:"qux=![]"` } options := MyOptions{} @@ -1624,11 +1624,11 @@ func TestMakeOptCfgsFor_optionParam(t *testing.T) { optCfgs, err0 := cliargs.MakeOptCfgsFor(&options) assert.Nil(t, err0) - assert.Equal(t, optCfgs[0].ArgHelp, "") - assert.Equal(t, optCfgs[1].ArgHelp, "bbb") - assert.Equal(t, optCfgs[2].ArgHelp, "ccc") - assert.Equal(t, optCfgs[3].ArgHelp, "ddd (multiple)") - assert.Equal(t, optCfgs[4].ArgHelp, "") + assert.Equal(t, optCfgs[0].ArgInHelp, "") + assert.Equal(t, optCfgs[1].ArgInHelp, "bbb") + assert.Equal(t, optCfgs[2].ArgInHelp, "ccc") + assert.Equal(t, optCfgs[3].ArgInHelp, "ddd (multiple)") + assert.Equal(t, optCfgs[4].ArgInHelp, "") } func TestParseFor_optCfgHasUnsupportedType(t *testing.T) { diff --git a/parse-with.go b/parse-with.go index acf3479..819a910 100644 --- a/parse-with.go +++ b/parse-with.go @@ -9,188 +9,239 @@ import ( "path" ) +// StoreKeyIsDuplicated is the error which indicates that a store key in an +// option configuration is duplicated another among all option configurations. +type StoreKeyIsDuplicated struct{ StoreKey string } + +// Error is the method to retrieve the message of this error. +func (e StoreKeyIsDuplicated) Error() string { + return fmt.Sprintf("StoreKeyIsDuplicated{StoreKey:%s}", e.StoreKey) +} + +// GetOpt is the method to retrieve the store key that caused this error. +func (e StoreKeyIsDuplicated) GetOpt() string { + return e.StoreKey +} + // ConfigIsArrayButHasNoArg is the error which indicates that an option // configuration contradicts that the option must be an array // (.IsArray = true) but must have no option argument (.HasArg = false). -type ConfigIsArrayButHasNoArg struct{ Option string } +type ConfigIsArrayButHasNoArg struct{ StoreKey string } // Error is the method to retrieve the message of this error. func (e ConfigIsArrayButHasNoArg) Error() string { - return fmt.Sprintf("ConfigIsArrayButHasNoArg{Option:%s}", e.Option) + return fmt.Sprintf("ConfigIsArrayButHasNoArg{StoreKey:%s}", e.StoreKey) } -// GetOpt is the method to retrieve the option that caused this error. +// GetOpt is the method to retrieve the store key that caused this error. func (e ConfigIsArrayButHasNoArg) GetOpt() string { - return e.Option + return e.StoreKey } // ConfigHasDefaultsButHasNoArg is the error which indicates that an option // configuration contradicts that the option has default value // (.Defaults != nil) but must have no option argument (.HasArg = false). -type ConfigHasDefaultsButHasNoArg struct{ Option string } +type ConfigHasDefaultsButHasNoArg struct{ StoreKey string } // Error is the method to retrieve the message of this error. func (e ConfigHasDefaultsButHasNoArg) Error() string { - return fmt.Sprintf("ConfigHasDefaultsButHasNoArg{Option:%s}", e.Option) + return fmt.Sprintf("ConfigHasDefaultsButHasNoArg{StoreKey:%s}", e.StoreKey) } -// GetOpt is the method to retrieve the option that caused this error. +// GetOpt is the method to retrieve the store key that caused this error. func (e ConfigHasDefaultsButHasNoArg) GetOpt() string { - return e.Option + return e.StoreKey } -// UnconfiguredOption is the error which indicates that there is no -// configuration about the input option. -type UnconfiguredOption struct{ Option string } +// OptionNameIsDuplicated is the error which indicates that an option name +// in Names field is duplicated another among all option configurations. +type OptionNameIsDuplicated struct{ Name, StoreKey string } // Error is the method to retrieve the message of this error. -func (e UnconfiguredOption) Error() string { - return fmt.Sprintf("UnconfiguredOption{Option:%s}", e.Option) +func (e OptionNameIsDuplicated) Error() string { + return fmt.Sprintf("OptionNameIsDuplicated{Name:%s,StoreKey:%s}", + e.Name, e.StoreKey) } -// GetOpt is the method to retrieve the option that caused this error. -func (e UnconfiguredOption) GetOpt() string { - return e.Option +// GetOpt is the method to retrieve the store key that caused this error. +func (e OptionNameIsDuplicated) GetOpt() string { + return e.Name } -// OptionNeedsArg is the error which indicates that an option is input with -// no option argument though its option configuration requires option -// argument (.HasArg = true). -type OptionNeedsArg struct{ Option string } +// UnconfiguredOption +type UnconfiguredOption struct{ Name string } // Error is the method to retrieve the message of this error. -func (e OptionNeedsArg) Error() string { - return fmt.Sprintf("OptionNeedsArg{Option:%s}", e.Option) +func (e UnconfiguredOption) Error() string { + return fmt.Sprintf("UnconfiguredOption{Name:%s}", e.Name) } -// GetOpt is the method to retrieve the option that caused this error. -func (e OptionNeedsArg) GetOpt() string { - return e.Option +// GetOpt is the method to retrieve the store key that caused this error. +func (e UnconfiguredOption) GetOpt() string { + return e.Name } -// OptionTakesNoArg is the error which indicates that an option is input with -// an option argument though its option configuration does not accept option -// arguments (.HasArg = false). -type OptionTakesNoArg struct{ Option string } +// OptionTakesNoArg +type OptionTakesNoArg struct{ Name, StoreKey string } // Error is the method to retrieve the message of this error. func (e OptionTakesNoArg) Error() string { - return fmt.Sprintf("OptionTakesNoArg{Option:%s}", e.Option) + return fmt.Sprintf("OptionTakesNoArg{Name:%s,StoreKey:%s}", + e.Name, e.StoreKey) } -// GetOpt is the method to retrieve the option that caused this error. +// GetOpt is the method to retrieve the store key that caused this error. func (e OptionTakesNoArg) GetOpt() string { - return e.Option + return e.Name +} + +// OptionNeedsArg +type OptionNeedsArg struct{ Name, StoreKey string } + +// Error is the method to retrieve the message of this error. +func (e OptionNeedsArg) Error() string { + return fmt.Sprintf("OptionNeedsArg{Name:%s,StoreKey:%s}", + e.Name, e.StoreKey) +} + +// GetOpt is the method to retrieve the store key that caused this error. +func (e OptionNeedsArg) GetOpt() string { + return e.Name } -// OptionIsNotArray is the error which indicates that an option is input with -// an option argument multiple times though its option configuration specifies -// the option is not an array (.IsArray = false). -type OptionIsNotArray struct{ Option string } +// OptionIsNotArray +type OptionIsNotArray struct{ Name, StoreKey string } // Error is the method to retrieve the message of this error. func (e OptionIsNotArray) Error() string { - return fmt.Sprintf("OptionIsNotArray{Option:%s}", e.Option) + return fmt.Sprintf("OptionIsNotArray{Name:%s,StoreKey:%s}", + e.Name, e.StoreKey) } -// GetOpt is the method to retrieve the option that caused this error. +// GetOpt is the method to retrieve the store key that caused this error. func (e OptionIsNotArray) GetOpt() string { - return e.Option + return e.Name } const anyOption = "*" // OptCfg is the struct that represents an option configuration. -// An option configuration consists of fields: Name, Aliases, HasArg, -// IsArray, Defaults, OnParsed, Desc, and ArgHelp. +// An option configuration consists of fields: StoreKey, Names, HasArg, +// IsArray, Defaults, Desc, and ArgInHelp. +// +// The StoreKey field is the key to store a option value(s) in the option map. +// If this key is not specified or empty, the first element of Names field is +// used instead. // -// Name is the option name and Aliases are the another names. -// Options given by those names in command line arguments are all registered to -// Args with the Name. +// The Names field is the array for specifing the option name and the aliases. +// The order of the names in this array are used in a help text. // -// HasArg and IsArray are flags which allows the option to take option +// HasArg and IsArray are flags which allow the option to take option // arguments. // If both HasArg and IsArray are true, the option can take one or multiple // option arguments. -// If HasArg is true and IsArray is false, the option can take only one -// option arguments. +// If HasArg is true and IsArray is false, the option can take only one option +// arguments. // If both HasArg and IsArray are false, the option can take no option // argument. // -// Defaults is the field to specify the default value for when the option is +// Defaults is the field to specified the default value for when the option is // not given in command line arguments. // -// OnParsed is the field for the event handler which is called when the option -// has been parsed. -// This handler receives a string array which is the option argument(s) as its -// argument. -// If this field is nil, nothing is done after parsing. +// OnParsed is the field for a function which is called when the option has +// been parsed. // // Desc is the field to set the description of the option. // -// ArgHelp is a display at a argument position of this option in a help text. -// This string is for a display like: -o, --option . +// ArgInHelp is a display of the argument of this option in a help text. +// The example of the display is like: -o, --option . type OptCfg struct { - Name string - Aliases []string - HasArg bool - IsArray bool - Defaults []string - OnParsed *func([]string) error - Desc string - ArgHelp string + StoreKey string + Names []string + HasArg bool + IsArray bool + Defaults []string + OnParsed *func([]string) error + Desc string + ArgInHelp string } // ParseWith is the function which parses command line arguments with option // configurations. // This function divides command line arguments to command arguments and -// options, and an option consists of a name and option arguments. +// options. And an option consists of a name and an option argument. // Options are divided to long format options and short format options. // About long/short format options, since they are same with Parse function, -// see the comment of the function. +// see the comment of that function. // // This function allows only options declared in option configurations. -// A option configuration has fields: Name, Aliases, HasArg, IsArray, Defaults, -// Desc and ArgHelp. -// When an option matches Name or includes in Aliases in an option -// configuration, the option is registered into Cmd with the Name. +// An option configuration has fields: StoreKey, Names, HasArg, IsArray, +// Defaults, Desc and ArgInHelp. +// When an option matches one of the Names in an option configuration, the +// option is registered into Cmd with StoreKey. // If both HasArg and IsArray are true, the option can have one or multiple -// option arguments, and if HasArg is true and IsArray is false, the option +// option argumentsr, and if HasArg is true and IsArray is false, the option // can have only one option argument, otherwise the option cannot have option -// argument. -// If Defaults is specified and the option is not given in command line -// arguments, the value of Defaults is set to the option argument. +// arguments. +// If Defaults field is specified and no option value is given in command line +// arguments, the value of Defaults is set as the option arguments. // -// If options not declared in option configurations are given in command line -// arguments, this function basically returns UnconfiguredOption error. -// If you want to allow other options, add an option configuration of which -// Name is "*" (but HasArg and IsArray of this configuration is ignored). +// If options not declared in option configurationsi are given in command line +// arguments, this function basically returns UnconfiguradOption error. +// However, if you want to allow other options, add an option configuration of +// which StoreKey or the first element of Names is "*". func ParseWith(osArgs []string, optCfgs []OptCfg) (Cmd, error) { var cmdName string if len(osArgs) > 0 { cmdName = path.Base(osArgs[0]) } - hasAnyOpt := false + var opts = make(map[string][]string) + var err error = nil + cfgMap := make(map[string]int) + hasAnyOpt := false + for i, cfg := range optCfgs { + storeKey := cfg.StoreKey + if len(storeKey) == 0 && len(cfg.Names) > 0 { + storeKey = cfg.Names[0] + } + + if len(storeKey) == 0 { + continue + } + + if storeKey == anyOption { + hasAnyOpt = true + continue + } + + _, exists := opts[storeKey] + if exists { + err = StoreKeyIsDuplicated{StoreKey: storeKey} + return Cmd{Name: cmdName, args: empty}, err + } + opts[storeKey] = nil + if !cfg.HasArg { if cfg.IsArray { - err := ConfigIsArrayButHasNoArg{Option: cfg.Name} + err = ConfigIsArrayButHasNoArg{StoreKey: storeKey} return Cmd{Name: cmdName, args: empty}, err } if cfg.Defaults != nil { - err := ConfigHasDefaultsButHasNoArg{Option: cfg.Name} + err = ConfigHasDefaultsButHasNoArg{StoreKey: storeKey} return Cmd{Name: cmdName, args: empty}, err } } - if cfg.Name == anyOption { - hasAnyOpt = true - continue - } - cfgMap[cfg.Name] = i - for _, a := range cfg.Aliases { - cfgMap[a] = i + + for _, nm := range cfg.Names { + _, exists := cfgMap[nm] + if exists { + err = OptionNameIsDuplicated{Name: nm, StoreKey: storeKey} + return Cmd{Name: cmdName, args: empty}, err + } + cfgMap[nm] = i } } @@ -202,8 +253,8 @@ func ParseWith(osArgs []string, optCfgs []OptCfg) (Cmd, error) { return false } - var args = make([]string, 0) - var opts = make(map[string][]string) + args := make([]string, 0) + opts = make(map[string][]string) var collectArgs = func(a ...string) { args = append(args, a...) @@ -212,7 +263,7 @@ func ParseWith(osArgs []string, optCfgs []OptCfg) (Cmd, error) { i, exists := cfgMap[name] if !exists { if !hasAnyOpt { - return UnconfiguredOption{Option: name} + return UnconfiguredOption{Name: name} } arr := opts[name] @@ -224,17 +275,23 @@ func ParseWith(osArgs []string, optCfgs []OptCfg) (Cmd, error) { } cfg := optCfgs[i] + + storeKey := cfg.StoreKey + if len(storeKey) == 0 { + storeKey = cfg.Names[0] + } + if !cfg.HasArg { if len(a) > 0 { - return OptionTakesNoArg{Option: cfg.Name} + return OptionTakesNoArg{Name: name, StoreKey: storeKey} } } else { if len(a) == 0 { - return OptionNeedsArg{Option: cfg.Name} + return OptionNeedsArg{Name: name, StoreKey: storeKey} } } - arr := opts[cfg.Name] + arr := opts[storeKey] if arr == nil { arr = empty } @@ -242,11 +299,11 @@ func ParseWith(osArgs []string, optCfgs []OptCfg) (Cmd, error) { if !cfg.IsArray { if len(arr) > 1 { - return OptionIsNotArray{Option: cfg.Name} + return OptionIsNotArray{Name: name, StoreKey: storeKey} } } - opts[cfg.Name] = arr + opts[storeKey] = arr return nil } @@ -255,14 +312,24 @@ func ParseWith(osArgs []string, optCfgs []OptCfg) (Cmd, error) { osArgs1 = osArgs[1:] } - err := parseArgs(osArgs1, collectArgs, collectOpts, takeArgs) + err = parseArgs(osArgs1, collectArgs, collectOpts, takeArgs) for _, cfg := range optCfgs { - arr, exists := opts[cfg.Name] + if len(cfg.Names) == 0 { + continue + } + + storeKey := cfg.StoreKey + if len(storeKey) == 0 { + storeKey = cfg.Names[0] + } + + arr, exists := opts[storeKey] if !exists && cfg.Defaults != nil { arr = cfg.Defaults - opts[cfg.Name] = arr + opts[storeKey] = arr } + if cfg.OnParsed != nil { e := (*cfg.OnParsed)(arr) if e != nil && err == nil { diff --git a/parse-with_test.go b/parse-with_test.go index e552ea4..50ddea9 100644 --- a/parse-with_test.go +++ b/parse-with_test.go @@ -36,17 +36,17 @@ func TestParseWith_zeroCfgAndOneCommandArg(t *testing.T) { assert.Equal(t, cmd.Args(), []string{"foo-bar"}) } -func testParseWith_zeroCfgAndOneLongOpt(t *testing.T) { +func TestParseWith_zeroCfgAndOneLongOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{} osArgs := []string{"path/to/app", "--foo-bar"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "UnconfiguredOption") + assert.Equal(t, err.Error(), "UnconfiguredOption{Name:foo-bar}") switch e := err.(type) { case cliargs.UnconfiguredOption: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.Name, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -74,10 +74,10 @@ func TestParseWith_zeroCfgAndOneShortOpt(t *testing.T) { cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "UnconfiguredOption{Option:f}") + assert.Equal(t, err.Error(), "UnconfiguredOption{Name:f}") switch e := err.(type) { case cliargs.UnconfiguredOption: - assert.Equal(t, e.Option, "f") + assert.Equal(t, e.Name, "f") default: assert.Fail(t, err.Error()) } @@ -100,7 +100,7 @@ func TestParseWith_zeroCfgAndOneShortOpt(t *testing.T) { func TestParseWith_oneCfgAndZeroOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, } osArgs := []string{"app"} @@ -119,7 +119,7 @@ func TestParseWith_oneCfgAndZeroOpt(t *testing.T) { func TestParseWith_oneCfgAndOneCmdArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, } osArgs := []string{"path/to/app", "foo-bar"} @@ -138,7 +138,7 @@ func TestParseWith_oneCfgAndOneCmdArg(t *testing.T) { func TestParseWith_oneCfgAndOneLongOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, } osArgs := []string{"path/to/app", "--foo-bar"} @@ -157,7 +157,7 @@ func TestParseWith_oneCfgAndOneLongOpt(t *testing.T) { func TestParseWith_oneCfgAndOneShortOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "f"}, + cliargs.OptCfg{Names: []string{"f"}}, } osArgs := []string{"app", "-f"} @@ -176,17 +176,17 @@ func TestParseWith_oneCfgAndOneShortOpt(t *testing.T) { func TestParseWith_oneCfgAndOneDifferentLongOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, } osArgs := []string{"app", "--boo-far"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "UnconfiguredOption{Option:boo-far}") + assert.Equal(t, err.Error(), "UnconfiguredOption{Name:boo-far}") switch e := err.(type) { case cliargs.UnconfiguredOption: - assert.Equal(t, e.Option, "boo-far") + assert.Equal(t, e.Name, "boo-far") default: assert.Fail(t, err.Error()) } @@ -209,17 +209,17 @@ func TestParseWith_oneCfgAndOneDifferentLongOpt(t *testing.T) { func TestParseWith_oneCfgAndOneDifferentShortOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "f"}, + cliargs.OptCfg{Names: []string{"f"}}, } osArgs := []string{"app", "-b"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "UnconfiguredOption{Option:b}") + assert.Equal(t, err.Error(), "UnconfiguredOption{Name:b}") switch e := err.(type) { case cliargs.UnconfiguredOption: - assert.Equal(t, e.Option, "b") + assert.Equal(t, e.Name, "b") default: assert.Fail(t, err.Error()) } @@ -242,8 +242,8 @@ func TestParseWith_oneCfgAndOneDifferentShortOpt(t *testing.T) { func TestParseWith_anyOptCfgAndOneDifferentLongOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, - cliargs.OptCfg{Name: "*"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, + cliargs.OptCfg{Names: []string{"*"}}, } osArgs := []string{"app", "--boo-far"} @@ -266,8 +266,8 @@ func TestParseWith_anyOptCfgAndOneDifferentLongOpt(t *testing.T) { func TestParseWith_anyOptCfgAndOneDifferentShortOpt(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "f"}, - cliargs.OptCfg{Name: "*"}, + cliargs.OptCfg{Names: []string{"f"}}, + cliargs.OptCfg{Names: []string{"*"}}, } osArgs := []string{"app", "-b"} @@ -290,7 +290,7 @@ func TestParseWith_anyOptCfgAndOneDifferentShortOpt(t *testing.T) { func TestParseWith_oneCfgHasArgAndOneLongOptHasArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar", HasArg: true}, + cliargs.OptCfg{Names: []string{"foo-bar"}, HasArg: true}, } osArgs := []string{"app", "--foo-bar", "ABC"} @@ -348,7 +348,7 @@ func TestParseWith_oneCfgHasArgAndOneLongOptHasArg(t *testing.T) { func TestParseWith_oneCfgHasArgAndOneShortOptHasArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "f", HasArg: true}, + cliargs.OptCfg{Names: []string{"f"}, HasArg: true}, } osArgs := []string{"app", "-f", "ABC"} @@ -406,17 +406,18 @@ func TestParseWith_oneCfgHasArgAndOneShortOptHasArg(t *testing.T) { func TestParseWith_oneCfgHasArgButOneLongOptHasNoArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar", HasArg: true}, + cliargs.OptCfg{Names: []string{"foo-bar"}, HasArg: true}, } osArgs := []string{"app", "--foo-bar"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionNeedsArg{Option:foo-bar}") + assert.Equal(t, err.Error(), "OptionNeedsArg{Name:foo-bar,StoreKey:foo-bar}") switch e := err.(type) { case cliargs.OptionNeedsArg: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.Name, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -439,17 +440,18 @@ func TestParseWith_oneCfgHasArgButOneLongOptHasNoArg(t *testing.T) { func TestParseWith_oneCfgHasArgAndOneShortOptHasNoArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "f", HasArg: true}, + cliargs.OptCfg{Names: []string{"f"}, HasArg: true}, } osArgs := []string{"app", "-f"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionNeedsArg{Option:f}") + assert.Equal(t, err.Error(), "OptionNeedsArg{Name:f,StoreKey:f}") switch e := err.(type) { case cliargs.OptionNeedsArg: - assert.Equal(t, e.Option, "f") + assert.Equal(t, e.Name, "f") + assert.Equal(t, e.StoreKey, "f") default: assert.Fail(t, err.Error()) } @@ -472,7 +474,7 @@ func TestParseWith_oneCfgHasArgAndOneShortOptHasNoArg(t *testing.T) { func TestParseWith_oneCfgHasNoArgAndOneLongOptHasArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, } osArgs := []string{"app", "--foo-bar", "ABC"} @@ -492,10 +494,11 @@ func TestParseWith_oneCfgHasNoArgAndOneLongOptHasArg(t *testing.T) { cmd, err = cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionTakesNoArg{Option:foo-bar}") + assert.Equal(t, err.Error(), "OptionTakesNoArg{Name:foo-bar,StoreKey:foo-bar}") switch e := err.(type) { case cliargs.OptionTakesNoArg: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.Name, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -532,10 +535,11 @@ func TestParseWith_oneCfgHasNoArgAndOneLongOptHasArg(t *testing.T) { cmd, err = cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionTakesNoArg{Option:foo-bar}") + assert.Equal(t, err.Error(), "OptionTakesNoArg{Name:foo-bar,StoreKey:foo-bar}") switch e := err.(type) { case cliargs.OptionTakesNoArg: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.Name, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -558,7 +562,7 @@ func TestParseWith_oneCfgHasNoArgAndOneLongOptHasArg(t *testing.T) { func TestParseWith_oneCfgHasNoArgAndOneShortOptHasArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "f"}, + cliargs.OptCfg{Names: []string{"f"}}, } osArgs := []string{"app", "-f", "ABC"} @@ -578,10 +582,11 @@ func TestParseWith_oneCfgHasNoArgAndOneShortOptHasArg(t *testing.T) { cmd, err = cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionTakesNoArg{Option:f}") + assert.Equal(t, err.Error(), "OptionTakesNoArg{Name:f,StoreKey:f}") switch e := err.(type) { case cliargs.OptionTakesNoArg: - assert.Equal(t, e.Option, "f") + assert.Equal(t, e.Name, "f") + assert.Equal(t, e.StoreKey, "f") default: assert.Fail(t, err.Error()) } @@ -618,10 +623,11 @@ func TestParseWith_oneCfgHasNoArgAndOneShortOptHasArg(t *testing.T) { cmd, err = cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionTakesNoArg{Option:f}") + assert.Equal(t, err.Error(), "OptionTakesNoArg{Name:f,StoreKey:f}") switch e := err.(type) { case cliargs.OptionTakesNoArg: - assert.Equal(t, e.Option, "f") + assert.Equal(t, e.Name, "f") + assert.Equal(t, e.StoreKey, "f") default: assert.Fail(t, err.Error()) } @@ -644,17 +650,17 @@ func TestParseWith_oneCfgHasNoArgAndOneShortOptHasArg(t *testing.T) { func TestParseWith_oneCfgHasNoArgButIsArray(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar", HasArg: false, IsArray: true}, + cliargs.OptCfg{Names: []string{"foo-bar"}, HasArg: false, IsArray: true}, } osArgs := []string{"app", "--foo-bar", "ABC"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "ConfigIsArrayButHasNoArg{Option:foo-bar}") + assert.Equal(t, err.Error(), "ConfigIsArrayButHasNoArg{StoreKey:foo-bar}") switch e := err.(type) { case cliargs.ConfigIsArrayButHasNoArg: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -677,8 +683,8 @@ func TestParseWith_oneCfgHasNoArgButIsArray(t *testing.T) { func TestParseWith_oneCfgIsArrayAndOptHasOneArg(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar", HasArg: true, IsArray: true}, - cliargs.OptCfg{Name: "f", HasArg: true, IsArray: true}, + cliargs.OptCfg{Names: []string{"foo-bar"}, HasArg: true, IsArray: true}, + cliargs.OptCfg{Names: []string{"f"}, HasArg: true, IsArray: true}, } osArgs := []string{"app", "--foo-bar", "ABC"} @@ -737,9 +743,8 @@ func TestParseWith_oneCfgIsArrayAndOptHasOneArg(t *testing.T) { func TestParseWith_oneCfgHasAliasesAndArgMatchesName(t *testing.T) { optCfgs := []cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f", "b"}, - HasArg: true, + Names: []string{"foo-bar", "f", "b"}, + HasArg: true, }, } @@ -770,47 +775,10 @@ func TestParseWith_oneCfgHasAliasesAndArgMatchesName(t *testing.T) { assert.Equal(t, cmd.Args(), []string{}) } -func TestParseWith_oneCfgHasAliasesAndArgMatchesAliases(t *testing.T) { +func TestParseWith_hasOptsOfBothNameAndAliase(t *testing.T) { optCfgs := []cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - }, - } - - osArgs := []string{"app", "-f", "ABC"} - - cmd, err := cliargs.ParseWith(osArgs, optCfgs) - assert.Nil(t, err) - assert.Equal(t, cmd.Name, "app") - assert.True(t, cmd.HasOpt("foo-bar")) - assert.Equal(t, cmd.OptArg("foo-bar"), "ABC") - assert.Equal(t, cmd.OptArgs("foo-bar"), []string{"ABC"}) - assert.False(t, cmd.HasOpt("f")) - assert.Equal(t, cmd.OptArg("f"), "") - assert.Equal(t, cmd.OptArgs("f"), []string(nil)) - assert.Equal(t, cmd.Args(), []string{}) - - osArgs = []string{"app", "-f=ABC"} - - cmd, err = cliargs.ParseWith(osArgs, optCfgs) - assert.Nil(t, err) - assert.Equal(t, cmd.Name, "app") - assert.True(t, cmd.HasOpt("foo-bar")) - assert.Equal(t, cmd.OptArg("foo-bar"), "ABC") - assert.Equal(t, cmd.OptArgs("foo-bar"), []string{"ABC"}) - assert.False(t, cmd.HasOpt("f")) - assert.Equal(t, cmd.OptArg("f"), "") - assert.Equal(t, cmd.OptArgs("f"), []string(nil)) - assert.Equal(t, cmd.Args(), []string{}) -} - -func TestParseWith_combineOptsByNameAndAliases(t *testing.T) { - optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, + Names: []string{"foo-bar", "f"}, HasArg: true, IsArray: true, }, @@ -846,8 +814,7 @@ func TestParseWith_combineOptsByNameAndAliases(t *testing.T) { func TestParseWith_oneCfgIsNotArrayButOptsAreMultiple(t *testing.T) { optCfgs := []cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, + Names: []string{"foo-bar", "f"}, HasArg: true, IsArray: false, }, @@ -857,10 +824,11 @@ func TestParseWith_oneCfgIsNotArrayButOptsAreMultiple(t *testing.T) { cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "OptionIsNotArray{Option:foo-bar}") + assert.Equal(t, err.Error(), "OptionIsNotArray{Name:foo-bar,StoreKey:foo-bar}") switch e := err.(type) { case cliargs.OptionIsNotArray: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.Name, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -879,13 +847,41 @@ func TestParseWith_oneCfgIsNotArrayButOptsAreMultiple(t *testing.T) { assert.Equal(t, cmd.OptArg("f"), "") assert.Equal(t, cmd.OptArgs("f"), []string(nil)) assert.Equal(t, cmd.Args(), []string{}) + + osArgs = []string{"app", "-f=1", "ABC", "--foo-bar", "2"} + + cmd, err = cliargs.ParseWith(osArgs, optCfgs) + assert.NotNil(t, err) + assert.Equal(t, err.Error(), "OptionIsNotArray{Name:foo-bar,StoreKey:foo-bar}") + switch e := err.(type) { + case cliargs.OptionIsNotArray: + assert.Equal(t, e.Name, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") + default: + assert.Fail(t, err.Error()) + } + switch e := err.(type) { + case cliargs.InvalidOption: + assert.Equal(t, e.GetOpt(), "foo-bar") + default: + assert.Fail(t, err.Error()) + } + + assert.Equal(t, cmd.Name, "app") + assert.True(t, cmd.HasOpt("foo-bar")) + assert.Equal(t, cmd.OptArg("foo-bar"), "1") + assert.Equal(t, cmd.OptArgs("foo-bar"), []string{"1"}) + assert.False(t, cmd.HasOpt("f")) + assert.Equal(t, cmd.OptArg("f"), "") + assert.Equal(t, cmd.OptArgs("f"), []string(nil)) + assert.Equal(t, cmd.Args(), []string{"ABC"}) } func TestParseWith_specifyDefault(t *testing.T) { osArgs := []string{"app"} optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "bar", HasArg: true, Defaults: []string{"A"}}, - cliargs.OptCfg{Name: "baz", HasArg: true, IsArray: true, Defaults: []string{"A"}}, + cliargs.OptCfg{Names: []string{"bar"}, HasArg: true, Defaults: []string{"A"}}, + cliargs.OptCfg{Names: []string{"baz"}, HasArg: true, IsArray: true, Defaults: []string{"A"}}, } cmd, err := cliargs.ParseWith(osArgs, optCfgs) @@ -904,17 +900,17 @@ func TestParseWith_specifyDefault(t *testing.T) { func TestParseWith_oneCfgHasNoArgButHasDefault(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar", HasArg: false, Defaults: []string{"A"}}, + cliargs.OptCfg{Names: []string{"foo-bar"}, HasArg: false, Defaults: []string{"A"}}, } osArgs := []string{"app"} cmd, err := cliargs.ParseWith(osArgs, optCfgs) assert.NotNil(t, err) - assert.Equal(t, err.Error(), "ConfigHasDefaultsButHasNoArg{Option:foo-bar}") + assert.Equal(t, err.Error(), "ConfigHasDefaultsButHasNoArg{StoreKey:foo-bar}") switch e := err.(type) { case cliargs.ConfigHasDefaultsButHasNoArg: - assert.Equal(t, e.Option, "foo-bar") + assert.Equal(t, e.StoreKey, "foo-bar") default: assert.Fail(t, err.Error()) } @@ -939,15 +935,14 @@ func TestParseWith_multipleArgs(t *testing.T) { osArgs := []string{"app", "--foo-bar", "qux", "--baz", "1", "-z=2", "-X", "quux"} optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo-bar"}, + cliargs.OptCfg{Names: []string{"foo-bar"}}, cliargs.OptCfg{ - Name: "baz", - Aliases: []string{"z"}, + Names: []string{"baz", "z"}, HasArg: true, IsArray: true, }, - cliargs.OptCfg{Name: "corge", HasArg: true, Defaults: []string{"99"}}, - cliargs.OptCfg{Name: "*"}, + cliargs.OptCfg{Names: []string{"corge"}, HasArg: true, Defaults: []string{"99"}}, + cliargs.OptCfg{Names: []string{"*"}}, } cmd, err := cliargs.ParseWith(osArgs, optCfgs) @@ -966,7 +961,7 @@ func TestParseWith_multipleArgs(t *testing.T) { func TestParseWith_parseAllArgsEvenIfError(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "foo", Aliases: []string{"f"}}, + cliargs.OptCfg{Names: []string{"foo", "f"}}, } osArgs := []string{"app", "-ef", "bar"} @@ -981,7 +976,7 @@ func TestParseWith_parseAllArgsEvenIfError(t *testing.T) { switch e := err.(type) { case cliargs.UnconfiguredOption: - assert.Equal(t, e.Option, "e") + assert.Equal(t, e.Name, "e") default: assert.Fail(t, err.Error()) } @@ -995,8 +990,8 @@ func TestParseWith_parseAllArgsEvenIfError(t *testing.T) { func TestParseWith_parseAllArgsEvenIfShortOptionValueIsError(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "e"}, - cliargs.OptCfg{Name: "foo", Aliases: []string{"f"}}, + cliargs.OptCfg{Names: []string{"e"}}, + cliargs.OptCfg{Names: []string{"foo", "f"}}, } osArgs := []string{"app", "-ef=123", "bar"} @@ -1011,13 +1006,14 @@ func TestParseWith_parseAllArgsEvenIfShortOptionValueIsError(t *testing.T) { switch e := err.(type) { case cliargs.OptionTakesNoArg: - assert.Equal(t, e.Option, "foo") + assert.Equal(t, e.Name, "f") + assert.Equal(t, e.StoreKey, "foo") default: assert.Fail(t, err.Error()) } switch e := err.(type) { case cliargs.InvalidOption: - assert.Equal(t, e.GetOpt(), "foo") + assert.Equal(t, e.GetOpt(), "f") default: assert.Fail(t, err.Error()) } @@ -1025,8 +1021,8 @@ func TestParseWith_parseAllArgsEvenIfShortOptionValueIsError(t *testing.T) { func TestParseWith_parseAllArgsEvenIfLongOptionValueIsError(t *testing.T) { optCfgs := []cliargs.OptCfg{ - cliargs.OptCfg{Name: "e"}, - cliargs.OptCfg{Name: "foo", Aliases: []string{"f"}}, + cliargs.OptCfg{Names: []string{"e"}}, + cliargs.OptCfg{Names: []string{"foo", "f"}}, } osArgs := []string{"app", "--foo=123", "-e", "bar"} @@ -1041,7 +1037,8 @@ func TestParseWith_parseAllArgsEvenIfLongOptionValueIsError(t *testing.T) { switch e := err.(type) { case cliargs.OptionTakesNoArg: - assert.Equal(t, e.Option, "foo") + assert.Equal(t, e.Name, "foo") + assert.Equal(t, e.StoreKey, "foo") default: assert.Fail(t, err.Error()) } @@ -1052,3 +1049,140 @@ func TestParseWith_parseAllArgsEvenIfLongOptionValueIsError(t *testing.T) { assert.Fail(t, err.Error()) } } + +func TestParseWith_ignoreCfgifNamesIsEmpty(t *testing.T) { + optCfgs := []cliargs.OptCfg{ + cliargs.OptCfg{Names: []string{}}, + cliargs.OptCfg{ + Names: []string{"foo"}, + }, + } + + osArgs := []string{"app", "--foo", "bar"} + + cmd, err := cliargs.ParseWith(osArgs, optCfgs) + assert.Equal(t, cmd.Name, "app") + assert.Nil(t, err) + + assert.True(t, cmd.HasOpt("foo")) + assert.Equal(t, cmd.Args(), []string{"bar"}) +} + +func TestParseWith_optionNameIsDuplicated(t *testing.T) { + optCfgs := []cliargs.OptCfg{ + cliargs.OptCfg{Names: []string{"foo", "f"}}, + cliargs.OptCfg{Names: []string{"bar", "f"}}, + } + + osArgs := []string{"app", "--foo", "--bar"} + + cmd, err := cliargs.ParseWith(osArgs, optCfgs) + assert.Equal(t, cmd.Name, "app") + + assert.Equal(t, err.Error(), "OptionNameIsDuplicated{Name:f,StoreKey:bar}") + switch e := err.(type) { + case cliargs.OptionNameIsDuplicated: + assert.Equal(t, e.Name, "f") + assert.Equal(t, e.StoreKey, "bar") + default: + assert.Fail(t, err.Error()) + } + switch e := err.(type) { + case cliargs.InvalidOption: + assert.Equal(t, e.GetOpt(), "f") + default: + assert.Fail(t, err.Error()) + } +} + +func TestParseWith_useStoreKey(t *testing.T) { + optCfgs := []cliargs.OptCfg{ + cliargs.OptCfg{ + StoreKey: "FooBar", + Names: []string{"f", "foo"}, + }, + } + + osArgs := []string{"app", "--foo", "bar"} + + cmd, err := cliargs.ParseWith(osArgs, optCfgs) + assert.Equal(t, cmd.Name, "app") + assert.Nil(t, err) + + assert.True(t, cmd.HasOpt("FooBar")) + assert.False(t, cmd.HasOpt("foo")) + assert.False(t, cmd.HasOpt("f")) + assert.Equal(t, cmd.Args(), []string{"bar"}) +} + +func TestParseWith_StoreKeyIsDuplicated(t *testing.T) { + optCfgs := []cliargs.OptCfg{ + cliargs.OptCfg{ + StoreKey: "FooBar", + Names: []string{"f", "foo"}, + }, + cliargs.OptCfg{ + StoreKey: "FooBar", + Names: []string{"b", "bar"}, + }, + } + + osArgs := []string{"app", "--foo", "bar"} + + cmd, err := cliargs.ParseWith(osArgs, optCfgs) + assert.Equal(t, cmd.Name, "app") + assert.Equal(t, err.Error(), "StoreKeyIsDuplicated{StoreKey:FooBar}") + switch e := err.(type) { + case cliargs.StoreKeyIsDuplicated: + assert.Equal(t, e.StoreKey, "FooBar") + default: + assert.Fail(t, err.Error()) + } + switch e := err.(type) { + case cliargs.InvalidOption: + assert.Equal(t, e.GetOpt(), "FooBar") + default: + assert.Fail(t, err.Error()) + } +} + +func TestParseWith_acceptAllOptionsIfStoreKeyIsAterisk(t *testing.T) { + optCfgs := []cliargs.OptCfg{ + cliargs.OptCfg{StoreKey: "*"}, + } + + osArgs := []string{"app", "--foo", "--bar", "baz"} + + cmd, err := cliargs.ParseWith(osArgs, optCfgs) + assert.Equal(t, cmd.Name, "app") + assert.Nil(t, err) + + assert.True(t, cmd.HasOpt("foo")) + assert.True(t, cmd.HasOpt("bar")) + assert.Equal(t, cmd.Args(), []string{"baz"}) +} + +func TestParseWith_acceptUnconfiguredOptionEvenIfItMatchesStoreKey(t *testing.T) { + optCfgs := []cliargs.OptCfg{ + cliargs.OptCfg{ + StoreKey: "Bar", + Names: []string{"foo", "f"}, + HasArg: true, + IsArray: true, + }, + cliargs.OptCfg{ + StoreKey: "*", + }, + } + + osArgs := []string{"app", "--foo", "1", "-f=2", "--Bar=3"} + + cmd, err := cliargs.ParseWith(osArgs, optCfgs) + assert.Equal(t, cmd.Name, "app") + assert.Nil(t, err) + + assert.True(t, cmd.HasOpt("Bar")) + assert.Equal(t, cmd.OptArg("Bar"), "1") + assert.Equal(t, cmd.OptArgs("Bar"), []string{"1", "2", "3"}) + assert.Equal(t, cmd.Args(), []string{}) +} diff --git a/print-help.go b/print-help.go index a384c21..61432ef 100644 --- a/print-help.go +++ b/print-help.go @@ -187,9 +187,16 @@ func (help *Help) AddOpts(optCfgs []OptCfg, wrapOpts ...int) { if b.indent > 0 { i := 0 for _, cfg := range optCfgs { - if cfg.Name == anyOption { + storeKey := cfg.StoreKey + if len(storeKey) == 0 { + if len(cfg.Names) > 0 && cfg.Names[0] == anyOption { + storeKey = cfg.Names[0] + } + } + if storeKey == anyOption { continue } + texts[i] = makeOptTitle(cfg) width := linebreak.TextWidth(texts[i]) if width+2 > b.indent { @@ -207,9 +214,16 @@ func (help *Help) AddOpts(optCfgs []OptCfg, wrapOpts ...int) { i := 0 for _, cfg := range optCfgs { - if cfg.Name == anyOption { + storeKey := cfg.StoreKey + if len(storeKey) == 0 { + if len(cfg.Names) > 0 && cfg.Names[0] == anyOption { + storeKey = cfg.Names[0] + } + } + if cfg.StoreKey == anyOption { continue } + texts[i] = makeOptTitle(cfg) widths[i] = linebreak.TextWidth(texts[i]) if indent < widths[i] { @@ -224,9 +238,16 @@ func (help *Help) AddOpts(optCfgs []OptCfg, wrapOpts ...int) { i = 0 for _, cfg := range optCfgs { - if cfg.Name == anyOption { + storeKey := cfg.StoreKey + if len(storeKey) == 0 { + if len(cfg.Names) > 0 && cfg.Names[0] == anyOption { + storeKey = cfg.Names[0] + } + } + if cfg.StoreKey == anyOption { continue } + texts[i] += strings.Repeat(" ", indent-widths[i]) + cfg.Desc i++ } @@ -237,30 +258,22 @@ func (help *Help) AddOpts(optCfgs []OptCfg, wrapOpts ...int) { } func makeOptTitle(cfg OptCfg) string { - title := cfg.Name - switch len(title) { - case 0: - case 1: - title = "-" + title - default: - title = "--" + title - } - - for _, alias := range cfg.Aliases { - switch len(alias) { + title := "" + for _, name := range cfg.Names { + switch len(name) { case 0: case 1: - title += ", -" + alias + title += ", -" + name default: - title += ", --" + alias + title += ", --" + name } } - if cfg.HasArg && len(cfg.ArgHelp) > 0 { - title += " " + cfg.ArgHelp + if cfg.HasArg && len(cfg.ArgInHelp) > 0 { + title += " " + cfg.ArgInHelp } - return title + return title[2:] } // Print is a method which prints help texts to standard output. diff --git a/print-help_test.go b/print-help_test.go index 446a74c..671d553 100644 --- a/print-help_test.go +++ b/print-help_test.go @@ -360,8 +360,8 @@ func TestAddOpts_oneOpts_withNoWrapping(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Desc: "This is a description of option.", + Names: []string{"foo-bar"}, + Desc: "This is a description of option.", }, }) iter := help.Iter() @@ -379,9 +379,8 @@ func TestAddOpts_oneOpts_withWrapping(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f", "foo", "b", "bar"}, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678", + Names: []string{"foo-bar", "f", "foo", "b", "bar"}, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678", }, }) iter := help.Iter() @@ -403,11 +402,10 @@ func TestAddOpts_oneOpts_withMarginsByNewHelpArg(t *testing.T) { help := cliargs.NewHelp(5, 3) help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", - ArgHelp: "", + Names: []string{"foo-bar", "f"}, + HasArg: true, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", + ArgInHelp: "", }, }) iter := help.Iter() @@ -429,11 +427,10 @@ func TestAddOpts_oneOpts_withMarginsByAddOptsArg(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", - ArgHelp: "", + Names: []string{"foo-bar", "f"}, + HasArg: true, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", + ArgInHelp: "", }, }, 0, 5, 3) iter := help.Iter() @@ -455,11 +452,10 @@ func TestAddOpts_oneOpts_withMarginsByNewHelpArgAndAddOptsArg(t *testing.T) { help := cliargs.NewHelp(2, 2) help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", - ArgHelp: "", + Names: []string{"foo-bar", "f"}, + HasArg: true, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", + ArgInHelp: "", }, }, 0, 3, 1) iter := help.Iter() @@ -481,11 +477,10 @@ func TestAddOpts_oneOpts_withIndentLongerThanTitle(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", - ArgHelp: "", + Names: []string{"foo-bar", "f"}, + HasArg: true, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", + ArgInHelp: "", }, }, 25) iter := help.Iter() @@ -507,11 +502,10 @@ func TestAddOpts_oneOpts_withIndentShorterThanTitle(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", - ArgHelp: "", + Names: []string{"foo-bar", "f"}, + HasArg: true, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", + ArgInHelp: "", }, }, 10) iter := help.Iter() @@ -537,16 +531,14 @@ func TestAddOpts_multipleOpts(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Aliases: []string{"f"}, - HasArg: true, - Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", - ArgHelp: "", + Names: []string{"foo-bar", "f"}, + HasArg: true, + Desc: "a12345678 b12345678 c12345678 d12345678 e12345678 f12345678 g12345678 h12345678", + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "baz", - Aliases: []string{"b"}, - Desc: "i12345678 j12345678 k12345678 l12345678 m12345678 n12345678 o12345678 p12345678", + Names: []string{"baz", "b"}, + Desc: "i12345678 j12345678 k12345678 l12345678 m12345678 n12345678 o12345678 p12345678", }, }) iter := help.Iter() @@ -576,12 +568,12 @@ func TestAddOpts_hasAnyOption(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Desc: "a12345678 b12345678", + Names: []string{"foo-bar"}, + Desc: "a12345678 b12345678", }, cliargs.OptCfg{ - Name: "*", - Desc: "c12345678 d12345678", + StoreKey: "*", + Desc: "c12345678 d12345678", }, }) iter := help.Iter() @@ -599,12 +591,12 @@ func TestAddOpts_hasAnyOption_withIndent(t *testing.T) { help := cliargs.NewHelp() help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "*", - Desc: "c12345678 d12345678", + Names: []string{"*"}, + Desc: "c12345678 d12345678", }, cliargs.OptCfg{ - Name: "foo-bar", - Desc: "a12345678 b12345678", + Names: []string{"foo-bar"}, + Desc: "a12345678 b12345678", }, }, 5) iter := help.Iter() @@ -663,20 +655,20 @@ func TestAddOpts_ifLineWidthLessThanSunOfMargins(t *testing.T) { help := cliargs.NewHelp(10, 30) help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "foo-bar", - Desc: "This is a description of option.", + Names: []string{"foo-bar"}, + Desc: "This is a description of option.", }, }, 10, 10, 20) help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "baz", - Desc: "This is a description of option.", + Names: []string{"baz"}, + Desc: "This is a description of option.", }, }, 10, 10) help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "qux", - Desc: "This is a description of option.", + Names: []string{"qux"}, + Desc: "This is a description of option.", }, }, 10, 10, 21) iter := help.Iter() @@ -716,76 +708,76 @@ func TestPrint_curl(t *testing.T) { help.AddOpts([]cliargs.OptCfg{ cliargs.OptCfg{ - Name: "d", - Aliases: []string{"data"}, - Desc: "HTTP POST data", - HasArg: true, - ArgHelp: "", + StoreKey: "Data", + Names: []string{"d", "data"}, + Desc: "HTTP POST data", + HasArg: true, + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "f", - Aliases: []string{"fail"}, - Desc: "Fail fast with no output on HTTP errors", + StoreKey: "Fail", + Names: []string{"f", "fail"}, + Desc: "Fail fast with no output on HTTP errors", }, cliargs.OptCfg{ - Name: "h", - Aliases: []string{"help"}, - Desc: "Get help for commands", - HasArg: true, - ArgHelp: "", + StoreKey: "Help", + Names: []string{"h", "help"}, + Desc: "Get help for commands", + HasArg: true, + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "i", - Aliases: []string{"include"}, - Desc: "Include protocol response headers in the output", + StoreKey: "Include", + Names: []string{"i", "include"}, + Desc: "Include protocol response headers in the output", }, cliargs.OptCfg{ - Name: "o", - Aliases: []string{"output"}, - Desc: "Write to file instead of stdout", - HasArg: true, - ArgHelp: "", + StoreKey: "Output", + Names: []string{"o", "output"}, + Desc: "Write to file instead of stdout", + HasArg: true, + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "O", - Aliases: []string{"remote-name"}, - Desc: "Write output to a file named as the remote file", + StoreKey: "RemoteName", + Names: []string{"O", "remote-name"}, + Desc: "Write output to a file named as the remote file", }, cliargs.OptCfg{ - Name: "s", - Aliases: []string{"silent"}, - Desc: "Silent mode", + StoreKey: "Silent", + Names: []string{"s", "silent"}, + Desc: "Silent mode", }, cliargs.OptCfg{ - Name: "T", - Aliases: []string{"upload-file"}, - Desc: "Transfer local FILE to destination", - HasArg: true, - ArgHelp: "", + StoreKey: "UploadFile", + Names: []string{"T", "upload-file"}, + Desc: "Transfer local FILE to destination", + HasArg: true, + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "u", - Aliases: []string{"user"}, - Desc: "Server user and password", - HasArg: true, - ArgHelp: "", + StoreKey: "User", + Names: []string{"u", "user"}, + Desc: "Server user and password", + HasArg: true, + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "A", - Aliases: []string{"user-agent"}, - Desc: "Send User-Agent to server", - HasArg: true, - ArgHelp: "", + StoreKey: "UserAgent", + Names: []string{"A", "user-agent"}, + Desc: "Send User-Agent to server", + HasArg: true, + ArgInHelp: "", }, cliargs.OptCfg{ - Name: "v", - Aliases: []string{"verbose"}, - Desc: "Make the operation more talkative", + StoreKey: "Verbose", + Names: []string{"v", "verbose"}, + Desc: "Make the operation more talkative", }, cliargs.OptCfg{ - Name: "V", - Aliases: []string{"version"}, - Desc: "Show version number and quit", + StoreKey: "Version", + Names: []string{"V", "version"}, + Desc: "Show version number and quit", }, }, 0, 1)