Altered help tag parsing to reduce the constraints on help text content; old behaviour is retained for backward compatibility

This commit is contained in:
Rick 2017-10-02 14:18:41 +01:00
parent 398a01ebab
commit d7961941f0
5 changed files with 58 additions and 23 deletions

View File

@ -99,9 +99,9 @@ Workers: 4
var args struct { var args struct {
Input string `arg:"positional"` Input string `arg:"positional"`
Output []string `arg:"positional"` Output []string `arg:"positional"`
Verbose bool `arg:"-v,help:verbosity level"` Verbose bool `arg:"-v" help:"verbosity level"`
Dataset string `arg:"help:dataset to use"` Dataset string `help:"dataset to use"`
Optimize int `arg:"-O,help:optimization level"` Optimize int `arg:"-O" help:"optimization level"`
} }
arg.MustParse(&args) arg.MustParse(&args)
``` ```
@ -306,7 +306,7 @@ Options:
--help, -h display this help and exit --help, -h display this help and exit
``` ```
### Documentation ### API Documentation
https://godoc.org/github.com/alexflint/go-arg https://godoc.org/github.com/alexflint/go-arg
@ -319,3 +319,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. 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? 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.

15
doc.go
View File

@ -19,18 +19,21 @@
// Fields can be bool, string, any float type, or any signed or unsigned integer type. // 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. // 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 { // var args struct {
// Input string `arg:"positional"` // Input string `arg:"positional"`
// Log string `arg:"positional,required"` // 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" // RealMode bool `arg:"--real"
// Wr io.Writer `arg:"-"` // Wr io.Writer `arg:"-"`
// } // }
// //
// The valid tag strings are `positional`, `required`, and `help`. Further, any tag string // Any tag string that starts with a single hyphen is the short form for an argument
// that starts with a single hyphen is the short form for an argument (e.g. `./example -d`), // (e.g. `./example -d`), and any tag string that starts with two hyphens is the long
// and any tag string that starts with two hyphens is the long form for the argument // form for the argument (instead of the field name).
// (instead of the field name). Fields can be excluded from processing with `arg:"-"`. //
// Other valid tag strings are `positional` and `required`.
//
// Fields can be excluded from processing with `arg:"-"`.
package arg package arg

View File

@ -153,6 +153,11 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
dest: val, 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 // 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 // wait until setScalar because it means that a program with invalid argument
// fields will always fail regardless of whether the arguments it received // 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 spec.positional = true
case key == "separate": case key == "separate":
spec.separate = true spec.separate = true
case key == "help": case key == "help": // deprecated
spec.help = value spec.help = value
case key == "env": case key == "env":
// Use override name if provided // Use override name if provided

View File

@ -131,7 +131,7 @@ func TestMixed(t *testing.T) {
var args struct { var args struct {
Foo string `arg:"-f"` Foo string `arg:"-f"`
Bar int Bar int
Baz uint `arg:"positional"` Baz uint `arg:"positional"`
Ham bool Ham bool
Spam float32 Spam float32
} }

View File

@ -33,15 +33,15 @@ Options:
` `
var args struct { var args struct {
Input string `arg:"positional"` Input string `arg:"positional"`
Output []string `arg:"positional,help:list of outputs"` Output []string `arg:"positional" help:"list of outputs"`
Name string `arg:"help:name to use"` Name string `help:"name to use"`
Value int `arg:"help:secret value"` Value int `help:"secret value"`
Verbose bool `arg:"-v,help:verbosity level"` Verbose bool `arg:"-v" help:"verbosity level"`
Dataset string `arg:"help:dataset to use"` Dataset string `help:"dataset to use"`
Optimize int `arg:"-O,help:optimization level"` Optimize int `arg:"-O" help:"optimization level"`
Ids []int64 `arg:"help:Ids"` Ids []int64 `help:"Ids"`
Values []float64 `arg:"help:Values"` Values []float64 `help:"Values"`
Workers int `arg:"-w,env:WORKERS,help:number of workers to start"` Workers int `arg:"-w,env:WORKERS" help:"number of workers to start"`
} }
args.Name = "Foo Bar" args.Name = "Foo Bar"
args.Value = 42 args.Value = 42
@ -60,7 +60,7 @@ Options:
assert.Equal(t, expectedHelp, help.String()) assert.Equal(t, expectedHelp, help.String())
} }
func TestUsageLongPositionalWithHelp(t *testing.T) { func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP
Positional arguments: Positional arguments:
@ -83,6 +83,29 @@ Options:
assert.Equal(t, expectedHelp, help.String()) 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
Options:
--help, -h display this help and exit
`
var args struct {
VeryLongPositionalWithHelp string `arg:"positional" help:"this positional argument is very long"`
}
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 TestUsageWithProgramName(t *testing.T) { func TestUsageWithProgramName(t *testing.T) {
expectedHelp := `Usage: myprogram expectedHelp := `Usage: myprogram
@ -168,7 +191,7 @@ Options:
--help, -h display this help and exit --help, -h display this help and exit
` `
var args struct { 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) p, err := NewParser(Config{}, &args)