Merge c14b278ccf
into 660b9045e1
This commit is contained in:
commit
0c471a4dd9
|
@ -199,13 +199,13 @@ func Example_helpPlaceholder() {
|
|||
MustParse(&args)
|
||||
|
||||
// output:
|
||||
|
||||
//
|
||||
// Usage: example [--optimize LEVEL] [--maxjobs N] SRC [DST [DST ...]]
|
||||
|
||||
//
|
||||
// Positional arguments:
|
||||
// SRC
|
||||
// DST
|
||||
|
||||
//
|
||||
// Options:
|
||||
// --optimize LEVEL, -O LEVEL
|
||||
// optimization level
|
||||
|
@ -501,7 +501,9 @@ func Example_envVarOnly() {
|
|||
os.Args = split("./example")
|
||||
_ = os.Setenv("AUTH_KEY", "my_key")
|
||||
|
||||
defer os.Unsetenv("AUTH_KEY")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("AUTH_KEY")
|
||||
}()
|
||||
|
||||
var args struct {
|
||||
AuthKey string `arg:"--,env:AUTH_KEY"`
|
||||
|
|
8
go.mod
8
go.mod
|
@ -1,14 +1,14 @@
|
|||
module github.com/alexflint/go-arg
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/alexflint/go-scalar v1.2.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
go 1.18
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1,15 +1,13 @@
|
|||
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
|
||||
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
29
parse.go
29
parse.go
|
@ -11,7 +11,7 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
scalar "github.com/alexflint/go-scalar"
|
||||
"github.com/alexflint/go-scalar"
|
||||
)
|
||||
|
||||
// path represents a sequence of steps to find the output location for an
|
||||
|
@ -94,7 +94,7 @@ func mustParse(config Config, dest ...interface{}) *Parser {
|
|||
|
||||
p, err := NewParser(config, dest...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(config.Out, err)
|
||||
_, _ = fmt.Fprintln(config.Out, err)
|
||||
config.Exit(-1)
|
||||
return nil
|
||||
}
|
||||
|
@ -141,6 +141,9 @@ type Config struct {
|
|||
|
||||
// Out is where help text, usage text, and failure messages are printed (defaults to os.Stdout)
|
||||
Out io.Writer
|
||||
|
||||
// Environment is a map of environment variables to override those in the process environment, or provide values to those not in the process environment.
|
||||
Environment map[string]string
|
||||
}
|
||||
|
||||
// Parser represents a set of command line options with destination values
|
||||
|
@ -513,11 +516,11 @@ func (p *Parser) Parse(args []string) error {
|
|||
func (p *Parser) MustParse(args []string) {
|
||||
err := p.Parse(args)
|
||||
switch {
|
||||
case err == ErrHelp:
|
||||
case errors.Is(err, ErrHelp):
|
||||
p.writeHelpForSubcommand(p.config.Out, p.lastCmd)
|
||||
p.config.Exit(0)
|
||||
case err == ErrVersion:
|
||||
fmt.Fprintln(p.config.Out, p.version)
|
||||
case errors.Is(err, ErrVersion):
|
||||
_, _ = fmt.Fprintln(p.config.Out, p.version)
|
||||
p.config.Exit(0)
|
||||
case err != nil:
|
||||
p.failWithSubcommand(err.Error(), p.lastCmd)
|
||||
|
@ -531,7 +534,17 @@ func (p *Parser) captureEnvVars(specs []*spec, wasPresent map[*spec]bool) error
|
|||
continue
|
||||
}
|
||||
|
||||
value, found := os.LookupEnv(spec.env)
|
||||
var value string
|
||||
var found bool
|
||||
|
||||
if !p.config.IgnoreEnv {
|
||||
value, found = os.LookupEnv(spec.env)
|
||||
}
|
||||
|
||||
if p.config.Environment != nil {
|
||||
value, found = p.config.Environment[spec.env]
|
||||
}
|
||||
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
@ -584,7 +597,7 @@ func (p *Parser) process(args []string) error {
|
|||
copy(specs, curCmd.specs)
|
||||
|
||||
// deal with environment vars
|
||||
if !p.config.IgnoreEnv {
|
||||
if !p.config.IgnoreEnv || p.config.Environment != nil {
|
||||
err := p.captureEnvVars(specs, wasPresent)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -640,7 +653,7 @@ func (p *Parser) process(args []string) error {
|
|||
}
|
||||
|
||||
// capture environment vars for these new options
|
||||
if !p.config.IgnoreEnv {
|
||||
if !p.config.IgnoreEnv || p.config.Environment != nil {
|
||||
err := p.captureEnvVars(subcmd.specs, wasPresent)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
390
parse_test.go
390
parse_test.go
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@ import (
|
|||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
scalar "github.com/alexflint/go-scalar"
|
||||
"github.com/alexflint/go-scalar"
|
||||
)
|
||||
|
||||
var textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem()
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestCardinalityOf(t *testing.T) {
|
|||
|
||||
type implementsTextUnmarshaler struct{}
|
||||
|
||||
func (*implementsTextUnmarshaler) UnmarshalText(text []byte) error {
|
||||
func (*implementsTextUnmarshaler) UnmarshalText(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
scalar "github.com/alexflint/go-scalar"
|
||||
"github.com/alexflint/go-scalar"
|
||||
)
|
||||
|
||||
// setSliceOrMap parses a sequence of strings into a slice or map. If clear is
|
||||
|
|
|
@ -42,8 +42,7 @@ func TestMinimalSubcommand(t *testing.T) {
|
|||
var args struct {
|
||||
List *listCmd `arg:"subcommand"`
|
||||
}
|
||||
p, err := pparse("list", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "list", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List, p.Subcommand())
|
||||
assert.Equal(t, []string{"list"}, p.SubcommandNames())
|
||||
|
@ -66,7 +65,7 @@ func TestNoSuchSubcommand(t *testing.T) {
|
|||
var args struct {
|
||||
List *listCmd `arg:"subcommand"`
|
||||
}
|
||||
_, err := pparse("invalid", &args)
|
||||
_, err := parseWithEnvErr(t, "invalid", nil, &args)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -76,8 +75,7 @@ func TestNamedSubcommand(t *testing.T) {
|
|||
var args struct {
|
||||
List *listCmd `arg:"subcommand:ls"`
|
||||
}
|
||||
p, err := pparse("ls", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "ls", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List, p.Subcommand())
|
||||
assert.Equal(t, []string{"ls"}, p.SubcommandNames())
|
||||
|
@ -89,8 +87,7 @@ func TestEmptySubcommand(t *testing.T) {
|
|||
var args struct {
|
||||
List *listCmd `arg:"subcommand"`
|
||||
}
|
||||
p, err := pparse("", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "", &args)
|
||||
assert.Nil(t, args.List)
|
||||
assert.Nil(t, p.Subcommand())
|
||||
assert.Empty(t, p.SubcommandNames())
|
||||
|
@ -105,8 +102,7 @@ func TestTwoSubcommands(t *testing.T) {
|
|||
Get *getCmd `arg:"subcommand"`
|
||||
List *listCmd `arg:"subcommand"`
|
||||
}
|
||||
p, err := pparse("list", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "list", &args)
|
||||
assert.Nil(t, args.Get)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List, p.Subcommand())
|
||||
|
@ -128,16 +124,14 @@ func TestSubcommandsWithOptions(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list", &args)
|
||||
assert.Nil(t, args.Get)
|
||||
assert.NotNil(t, args.List)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list --limit 3", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list --limit 3", &args)
|
||||
assert.Nil(t, args.Get)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List.Limit, 3)
|
||||
|
@ -145,8 +139,7 @@ func TestSubcommandsWithOptions(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list --limit 3 --verbose", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list --limit 3 --verbose", &args)
|
||||
assert.Nil(t, args.Get)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List.Limit, 3)
|
||||
|
@ -155,8 +148,7 @@ func TestSubcommandsWithOptions(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list --verbose --limit 3", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list --verbose --limit 3", &args)
|
||||
assert.Nil(t, args.Get)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List.Limit, 3)
|
||||
|
@ -165,8 +157,7 @@ func TestSubcommandsWithOptions(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("--verbose list --limit 3", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "--verbose list --limit 3", &args)
|
||||
assert.Nil(t, args.Get)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, args.List.Limit, 3)
|
||||
|
@ -175,16 +166,14 @@ func TestSubcommandsWithOptions(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Nil(t, args.List)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get --name test", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get --name test", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Nil(t, args.List)
|
||||
assert.Equal(t, args.Get.Name, "test")
|
||||
|
@ -207,8 +196,7 @@ func TestSubcommandsWithEnvVars(t *testing.T) {
|
|||
{
|
||||
var args cmd
|
||||
setenv(t, "LIMIT", "123")
|
||||
err := parse("list", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list", &args)
|
||||
require.NotNil(t, args.List)
|
||||
assert.Equal(t, 123, args.List.Limit)
|
||||
}
|
||||
|
@ -216,7 +204,7 @@ func TestSubcommandsWithEnvVars(t *testing.T) {
|
|||
{
|
||||
var args cmd
|
||||
setenv(t, "LIMIT", "not_an_integer")
|
||||
err := parse("list", &args)
|
||||
_, err := parseWithEnvErr(t, "list", nil, &args)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
@ -235,8 +223,7 @@ func TestNestedSubcommands(t *testing.T) {
|
|||
|
||||
{
|
||||
var args root
|
||||
p, err := pparse("grandparent parent child", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "grandparent parent child", &args)
|
||||
require.NotNil(t, args.Grandparent)
|
||||
require.NotNil(t, args.Grandparent.Parent)
|
||||
require.NotNil(t, args.Grandparent.Parent.Child)
|
||||
|
@ -246,8 +233,7 @@ func TestNestedSubcommands(t *testing.T) {
|
|||
|
||||
{
|
||||
var args root
|
||||
p, err := pparse("grandparent parent", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "grandparent parent", &args)
|
||||
require.NotNil(t, args.Grandparent)
|
||||
require.NotNil(t, args.Grandparent.Parent)
|
||||
require.Nil(t, args.Grandparent.Parent.Child)
|
||||
|
@ -257,8 +243,7 @@ func TestNestedSubcommands(t *testing.T) {
|
|||
|
||||
{
|
||||
var args root
|
||||
p, err := pparse("grandparent", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "grandparent", &args)
|
||||
require.NotNil(t, args.Grandparent)
|
||||
require.Nil(t, args.Grandparent.Parent)
|
||||
assert.Equal(t, args.Grandparent, p.Subcommand())
|
||||
|
@ -267,8 +252,7 @@ func TestNestedSubcommands(t *testing.T) {
|
|||
|
||||
{
|
||||
var args root
|
||||
p, err := pparse("", &args)
|
||||
require.NoError(t, err)
|
||||
p := pparse(t, "", &args)
|
||||
require.Nil(t, args.Grandparent)
|
||||
assert.Nil(t, p.Subcommand())
|
||||
assert.Empty(t, p.SubcommandNames())
|
||||
|
@ -286,16 +270,14 @@ func TestSubcommandsWithPositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, "", args.List.Pattern)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list --format json", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list --format json", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, "", args.List.Pattern)
|
||||
assert.Equal(t, "json", args.Format)
|
||||
|
@ -303,16 +285,14 @@ func TestSubcommandsWithPositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list somepattern", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list somepattern", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list somepattern --format json", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list somepattern --format json", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
||||
assert.Equal(t, "json", args.Format)
|
||||
|
@ -320,8 +300,7 @@ func TestSubcommandsWithPositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("list --format json somepattern", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "list --format json somepattern", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
||||
assert.Equal(t, "json", args.Format)
|
||||
|
@ -329,8 +308,7 @@ func TestSubcommandsWithPositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("--format json list somepattern", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "--format json list somepattern", &args)
|
||||
assert.NotNil(t, args.List)
|
||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
||||
assert.Equal(t, "json", args.Format)
|
||||
|
@ -338,8 +316,7 @@ func TestSubcommandsWithPositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("--format json", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "--format json", &args)
|
||||
assert.Nil(t, args.List)
|
||||
assert.Equal(t, "json", args.Format)
|
||||
}
|
||||
|
@ -355,16 +332,14 @@ func TestSubcommandsWithMultiplePositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Empty(t, args.Get.Items)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get --limit 5", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get --limit 5", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Empty(t, args.Get.Items)
|
||||
assert.Equal(t, 5, args.Limit)
|
||||
|
@ -372,24 +347,21 @@ func TestSubcommandsWithMultiplePositionals(t *testing.T) {
|
|||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get item1", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get item1", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Equal(t, []string{"item1"}, args.Get.Items)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get item1 item2 item3", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get item1 item2 item3", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Equal(t, []string{"item1", "item2", "item3"}, args.Get.Items)
|
||||
}
|
||||
|
||||
{
|
||||
var args cmd
|
||||
err := parse("get item1 --limit 5 item2", &args)
|
||||
require.NoError(t, err)
|
||||
parse(t, "get item1 --limit 5 item2", &args)
|
||||
assert.NotNil(t, args.Get)
|
||||
assert.Equal(t, []string{"item1", "item2"}, args.Get.Items)
|
||||
assert.Equal(t, 5, args.Limit)
|
||||
|
|
66
usage.go
66
usage.go
|
@ -32,7 +32,7 @@ func (p *Parser) FailSubcommand(msg string, subcommand ...string) error {
|
|||
// failWithSubcommand prints usage information for the given subcommand to stderr and exits with non-zero status
|
||||
func (p *Parser) failWithSubcommand(msg string, cmd *command) {
|
||||
p.writeUsageForSubcommand(p.config.Out, cmd)
|
||||
fmt.Fprintln(p.config.Out, "error:", msg)
|
||||
_, _ = fmt.Fprintln(p.config.Out, "error:", msg)
|
||||
p.config.Exit(-1)
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
|||
}
|
||||
|
||||
if p.version != "" {
|
||||
fmt.Fprintln(w, p.version)
|
||||
_, _ = fmt.Fprintln(w, p.version)
|
||||
}
|
||||
|
||||
// make a list of ancestor commands so that we print with full context
|
||||
|
@ -86,33 +86,33 @@ func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
|||
}
|
||||
|
||||
// print the beginning of the usage string
|
||||
fmt.Fprint(w, "Usage:")
|
||||
_, _ = fmt.Fprint(w, "Usage:")
|
||||
for i := len(ancestors) - 1; i >= 0; i-- {
|
||||
fmt.Fprint(w, " "+ancestors[i])
|
||||
_, _ = fmt.Fprint(w, " "+ancestors[i])
|
||||
}
|
||||
|
||||
// write the option component of the usage message
|
||||
for _, spec := range shortOptions {
|
||||
// prefix with a space
|
||||
fmt.Fprint(w, " ")
|
||||
_, _ = fmt.Fprint(w, " ")
|
||||
if !spec.required {
|
||||
fmt.Fprint(w, "[")
|
||||
_, _ = fmt.Fprint(w, "[")
|
||||
}
|
||||
fmt.Fprint(w, synopsis(spec, "-"+spec.short))
|
||||
_, _ = fmt.Fprint(w, synopsis(spec, "-"+spec.short))
|
||||
if !spec.required {
|
||||
fmt.Fprint(w, "]")
|
||||
_, _ = fmt.Fprint(w, "]")
|
||||
}
|
||||
}
|
||||
|
||||
for _, spec := range longOptions {
|
||||
// prefix with a space
|
||||
fmt.Fprint(w, " ")
|
||||
_, _ = fmt.Fprint(w, " ")
|
||||
if !spec.required {
|
||||
fmt.Fprint(w, "[")
|
||||
_, _ = fmt.Fprint(w, "[")
|
||||
}
|
||||
fmt.Fprint(w, synopsis(spec, "--"+spec.long))
|
||||
_, _ = fmt.Fprint(w, synopsis(spec, "--"+spec.long))
|
||||
if !spec.required {
|
||||
fmt.Fprint(w, "]")
|
||||
_, _ = fmt.Fprint(w, "]")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,40 +130,40 @@ func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
|||
// REQUIRED1 REQUIRED2 [OPTIONAL1 [REPEATEDOPTIONAL [REPEATEDOPTIONAL ...]]]
|
||||
var closeBrackets int
|
||||
for _, spec := range positionals {
|
||||
fmt.Fprint(w, " ")
|
||||
_, _ = fmt.Fprint(w, " ")
|
||||
if !spec.required {
|
||||
fmt.Fprint(w, "[")
|
||||
_, _ = fmt.Fprint(w, "[")
|
||||
closeBrackets += 1
|
||||
}
|
||||
if spec.cardinality == multiple {
|
||||
fmt.Fprintf(w, "%s [%s ...]", spec.placeholder, spec.placeholder)
|
||||
_, _ = fmt.Fprintf(w, "%s [%s ...]", spec.placeholder, spec.placeholder)
|
||||
} else {
|
||||
fmt.Fprint(w, spec.placeholder)
|
||||
_, _ = fmt.Fprint(w, spec.placeholder)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, strings.Repeat("]", closeBrackets))
|
||||
_, _ = fmt.Fprint(w, strings.Repeat("]", closeBrackets))
|
||||
|
||||
// if the program supports subcommands, give a hint to the user about their existence
|
||||
if len(cmd.subcommands) > 0 {
|
||||
fmt.Fprint(w, " <command> [<args>]")
|
||||
_, _ = fmt.Fprint(w, " <command> [<args>]")
|
||||
}
|
||||
|
||||
fmt.Fprint(w, "\n")
|
||||
_, _ = fmt.Fprint(w, "\n")
|
||||
}
|
||||
|
||||
func printTwoCols(w io.Writer, left, help string, defaultVal string, envVal string) {
|
||||
lhs := " " + left
|
||||
fmt.Fprint(w, lhs)
|
||||
_, _ = fmt.Fprint(w, lhs)
|
||||
if help != "" {
|
||||
if len(lhs)+2 < colWidth {
|
||||
fmt.Fprint(w, strings.Repeat(" ", colWidth-len(lhs)))
|
||||
_, _ = fmt.Fprint(w, strings.Repeat(" ", colWidth-len(lhs)))
|
||||
} else {
|
||||
fmt.Fprint(w, "\n"+strings.Repeat(" ", colWidth))
|
||||
_, _ = fmt.Fprint(w, "\n"+strings.Repeat(" ", colWidth))
|
||||
}
|
||||
fmt.Fprint(w, help)
|
||||
_, _ = fmt.Fprint(w, help)
|
||||
}
|
||||
|
||||
bracketsContent := []string{}
|
||||
var bracketsContent []string
|
||||
|
||||
if defaultVal != "" {
|
||||
bracketsContent = append(bracketsContent,
|
||||
|
@ -178,9 +178,9 @@ func printTwoCols(w io.Writer, left, help string, defaultVal string, envVal stri
|
|||
}
|
||||
|
||||
if len(bracketsContent) > 0 {
|
||||
fmt.Fprintf(w, " [%s]", strings.Join(bracketsContent, ", "))
|
||||
_, _ = fmt.Fprintf(w, " [%s]", strings.Join(bracketsContent, ", "))
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
_, _ = fmt.Fprint(w, "\n")
|
||||
}
|
||||
|
||||
// WriteHelp writes the usage string followed by the full help string for each option
|
||||
|
@ -224,13 +224,13 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
|||
}
|
||||
|
||||
if p.description != "" {
|
||||
fmt.Fprintln(w, p.description)
|
||||
_, _ = fmt.Fprintln(w, p.description)
|
||||
}
|
||||
p.writeUsageForSubcommand(w, cmd)
|
||||
|
||||
// write the list of positionals
|
||||
if len(positionals) > 0 {
|
||||
fmt.Fprint(w, "\nPositional arguments:\n")
|
||||
_, _ = fmt.Fprint(w, "\nPositional arguments:\n")
|
||||
for _, spec := range positionals {
|
||||
printTwoCols(w, spec.placeholder, spec.help, "", "")
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
|||
|
||||
// write the list of options with the short-only ones first to match the usage string
|
||||
if len(shortOptions)+len(longOptions) > 0 || cmd.parent == nil {
|
||||
fmt.Fprint(w, "\nOptions:\n")
|
||||
_, _ = fmt.Fprint(w, "\nOptions:\n")
|
||||
for _, spec := range shortOptions {
|
||||
p.printOption(w, spec)
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
|||
|
||||
// write the list of global options
|
||||
if len(globals) > 0 {
|
||||
fmt.Fprint(w, "\nGlobal options:\n")
|
||||
_, _ = fmt.Fprint(w, "\nGlobal options:\n")
|
||||
for _, spec := range globals {
|
||||
p.printOption(w, spec)
|
||||
if spec.long == "version" {
|
||||
|
@ -286,7 +286,7 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
|||
|
||||
// write the list of environment only variables
|
||||
if len(envOnlyOptions) > 0 {
|
||||
fmt.Fprint(w, "\nEnvironment variables:\n")
|
||||
_, _ = fmt.Fprint(w, "\nEnvironment variables:\n")
|
||||
for _, spec := range envOnlyOptions {
|
||||
p.printEnvOnlyVar(w, spec)
|
||||
}
|
||||
|
@ -294,14 +294,14 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
|||
|
||||
// write the list of subcommands
|
||||
if len(cmd.subcommands) > 0 {
|
||||
fmt.Fprint(w, "\nCommands:\n")
|
||||
_, _ = fmt.Fprint(w, "\nCommands:\n")
|
||||
for _, subcmd := range cmd.subcommands {
|
||||
printTwoCols(w, subcmd.name, subcmd.help, "", "")
|
||||
}
|
||||
}
|
||||
|
||||
if p.epilogue != "" {
|
||||
fmt.Fprintln(w, "\n"+p.epilogue)
|
||||
_, _ = fmt.Fprintln(w, "\n"+p.epilogue)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,12 +97,12 @@ Environment variables:
|
|||
|
||||
type MyEnum int
|
||||
|
||||
func (n *MyEnum) UnmarshalText(b []byte) error {
|
||||
func (n *MyEnum) UnmarshalText(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *MyEnum) MarshalText() ([]byte, error) {
|
||||
return nil, errors.New("There was a problem")
|
||||
return nil, errors.New("there was a problem")
|
||||
}
|
||||
|
||||
func TestUsageWithDefaults(t *testing.T) {
|
||||
|
@ -142,7 +142,7 @@ func TestUsageCannotMarshalToString(t *testing.T) {
|
|||
v := MyEnum(42)
|
||||
args.Name = &v
|
||||
_, err := NewParser(Config{Program: "example"}, &args)
|
||||
assert.EqualError(t, err, `args.Name: error marshaling default value to string: There was a problem`)
|
||||
assert.EqualError(t, err, `args.Name: error marshaling default value to string: there was a problem`)
|
||||
}
|
||||
|
||||
func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
|
||||
|
@ -455,7 +455,8 @@ Global options:
|
|||
assert.Equal(t, expectedHelp[1:], help.String())
|
||||
|
||||
var help2 bytes.Buffer
|
||||
p.WriteHelpForSubcommand(&help2, "child", "nested")
|
||||
err = p.WriteHelpForSubcommand(&help2, "child", "nested")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedHelp[1:], help2.String())
|
||||
|
||||
var usage bytes.Buffer
|
||||
|
@ -463,7 +464,8 @@ Global options:
|
|||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||
|
||||
var usage2 bytes.Buffer
|
||||
p.WriteUsageForSubcommand(&usage2, "child", "nested")
|
||||
err = p.WriteUsageForSubcommand(&usage2, "child", "nested")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage2.String()))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue