diff --git a/README.md b/README.md index 3f9223c..18dc556 100644 --- a/README.md +++ b/README.md @@ -99,9 +99,9 @@ Workers: 4 var args struct { Input string `arg:"positional"` Output []string `arg:"positional"` - Verbose bool `arg:"-v,help:verbosity level"` - Dataset string `arg:"help:dataset to use"` - Optimize int `arg:"-O,help:optimization level"` + Verbose bool `arg:"-v" help:"verbosity level"` + Dataset string `help:"dataset to use"` + Optimize int `arg:"-O" help:"optimization level"` } arg.MustParse(&args) ``` @@ -122,6 +122,9 @@ Options: --help, -h print this help message ``` +As the example above shows, the `help` tag can be used in conjunction with `arg`, or instead. When used +together, they can appear in either order. + ### Default values ```go @@ -306,7 +309,7 @@ Options: --help, -h display this help and exit ``` -### Documentation +### API Documentation https://godoc.org/github.com/alexflint/go-arg @@ -319,3 +322,7 @@ The shortcomings of the `flag` library that ships in the standard library are we Many third-party argument parsing libraries are geared for writing sophisticated command line interfaces. The excellent `codegangsta/cli` is perfect for working with multiple sub-commands and nested flags, but is probably overkill for a simple script with a handful of flags. The main idea behind `go-arg` is that Go already has an excellent way to describe data structures using Go structs, so there is no need to develop more levels of abstraction on top of this. Instead of one API to specify which arguments your program accepts, and then another API to get the values of those arguments, why not replace both with a single struct? + +### Backward Compatibility Notes + +The tags have changed recently. Earlier versions required the help text to be part of the `arg` tag. This is still supported but is now deprecated. Instead, you should use a separate `help` tag, described above, which removes most of the limits on the text you can write. In particular, you will need to use the new `help` tag if your help text includes any commas. diff --git a/doc.go b/doc.go index 500ec83..3b0bafd 100644 --- a/doc.go +++ b/doc.go @@ -19,18 +19,21 @@ // Fields can be bool, string, any float type, or any signed or unsigned integer type. // They can also be slices of any of the above, or slices of pointers to any of the above. // -// Tags can be specified using the `arg` package name: +// Tags can be specified using the `arg` and `help` tag names: // // var args struct { // Input string `arg:"positional"` // Log string `arg:"positional,required"` -// Debug bool `arg:"-d,help:turn on debug mode"` +// Debug bool `arg:"-d" help:"turn on debug mode"` // RealMode bool `arg:"--real" // Wr io.Writer `arg:"-"` // } // -// The valid tag strings are `positional`, `required`, and `help`. Further, any tag string -// that starts with a single hyphen is the short form for an argument (e.g. `./example -d`), -// and any tag string that starts with two hyphens is the long form for the argument -// (instead of the field name). Fields can be excluded from processing with `arg:"-"`. +// Any tag string that starts with a single hyphen is the short form for an argument +// (e.g. `./example -d`), and any tag string that starts with two hyphens is the long +// form for the argument (instead of the field name). +// +// Other valid tag strings are `positional` and `required`. +// +// Fields can be excluded from processing with `arg:"-"`. package arg diff --git a/parse.go b/parse.go index ee10387..462aab9 100644 --- a/parse.go +++ b/parse.go @@ -153,6 +153,11 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) { dest: val, } + help, exists := field.Tag.Lookup("help") + if exists { + spec.help = help + } + // Check whether this field is supported. It's good to do this here rather than // wait until setScalar because it means that a program with invalid argument // fields will always fail regardless of whether the arguments it received @@ -193,7 +198,7 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) { spec.positional = true case key == "separate": spec.separate = true - case key == "help": + case key == "help": // deprecated spec.help = value case key == "env": // Use override name if provided diff --git a/parse_test.go b/parse_test.go index 55f3fa6..701b105 100644 --- a/parse_test.go +++ b/parse_test.go @@ -131,7 +131,7 @@ func TestMixed(t *testing.T) { var args struct { Foo string `arg:"-f"` Bar int - Baz uint `arg:"positional"` + Baz uint `arg:"positional"` Ham bool Spam float32 } diff --git a/usage_test.go b/usage_test.go index 1bb1071..940bf40 100644 --- a/usage_test.go +++ b/usage_test.go @@ -33,15 +33,15 @@ Options: ` var args struct { Input string `arg:"positional"` - Output []string `arg:"positional,help:list of outputs"` - Name string `arg:"help:name to use"` - Value int `arg:"help:secret value"` - Verbose bool `arg:"-v,help:verbosity level"` - Dataset string `arg:"help:dataset to use"` - Optimize int `arg:"-O,help:optimization level"` - Ids []int64 `arg:"help:Ids"` - Values []float64 `arg:"help:Values"` - Workers int `arg:"-w,env:WORKERS,help:number of workers to start"` + Output []string `arg:"positional" help:"list of outputs"` + Name string `help:"name to use"` + Value int `help:"secret value"` + Verbose bool `arg:"-v" help:"verbosity level"` + Dataset string `help:"dataset to use"` + Optimize int `arg:"-O" help:"optimization level"` + Ids []int64 `help:"Ids"` + Values []float64 `help:"Values"` + Workers int `arg:"-w,env:WORKERS" help:"number of workers to start"` } args.Name = "Foo Bar" args.Value = 42 @@ -60,18 +60,41 @@ Options: assert.Equal(t, expectedHelp, help.String()) } -func TestUsageLongPositionalWithHelp(t *testing.T) { +func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) { expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP Positional arguments: VERYLONGPOSITIONALWITHHELP - this positional argument is very long + this positional argument is very long but cannot include commas Options: --help, -h display this help and exit ` var args struct { - VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long"` + VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long but cannot include commas"` + } + + p, err := NewParser(Config{}, &args) + require.NoError(t, err) + + os.Args[0] = "example" + var help bytes.Buffer + p.WriteHelp(&help) + assert.Equal(t, expectedHelp, help.String()) +} + +func TestUsageLongPositionalWithHelp_newForm(t *testing.T) { + expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP + +Positional arguments: + VERYLONGPOSITIONALWITHHELP + this positional argument is very long, and includes: commas, colons etc + +Options: + --help, -h display this help and exit +` + var args struct { + VeryLongPositionalWithHelp string `arg:"positional" help:"this positional argument is very long, and includes: commas, colons etc"` } p, err := NewParser(Config{}, &args) @@ -168,7 +191,7 @@ Options: --help, -h display this help and exit ` var args struct { - RequiredMultiple []string `arg:"positional,required,help:required multiple positional"` + RequiredMultiple []string `arg:"positional,required" help:"required multiple positional"` } p, err := NewParser(Config{}, &args)