help,usage and error messages and tests
This commit is contained in:
parent
b928a1839a
commit
18623d869b
12
parse.go
12
parse.go
|
@ -418,10 +418,14 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
|
||||||
spec.placeholder = strings.ToUpper(spec.field.Name)
|
spec.placeholder = strings.ToUpper(spec.field.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if spec.env == "" && emptyLongAndShort(kvPairs) {
|
noFormSpecs := emptyLongAndShort(kvPairs)
|
||||||
|
|
||||||
|
if spec.env == "" && noFormSpecs {
|
||||||
errs = append(errs, fmt.Sprintf("%s.%s: short arguments must be one character only",
|
errs = append(errs, fmt.Sprintf("%s.%s: short arguments must be one character only",
|
||||||
t.Name(), field.Name))
|
t.Name(), field.Name))
|
||||||
return false
|
return false
|
||||||
|
} else if spec.env != "" && noFormSpecs {
|
||||||
|
spec.envOnly = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is a subcommand then we've done everything we need to do
|
// if this is a subcommand then we've done everything we need to do
|
||||||
|
@ -759,10 +763,16 @@ func (p *Parser) process(args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if spec.required {
|
if spec.required {
|
||||||
|
if spec.envOnly {
|
||||||
|
msg := fmt.Sprintf("environment variable %s is required", spec.env)
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
msg := fmt.Sprintf("%s is required", name)
|
msg := fmt.Sprintf("%s is required", name)
|
||||||
if spec.env != "" {
|
if spec.env != "" {
|
||||||
msg += " (or environment variable " + spec.env + ")"
|
msg += " (or environment variable " + spec.env + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New(msg)
|
return errors.New(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,14 @@ func TestRequiredWithEnv(t *testing.T) {
|
||||||
require.Error(t, err, "--foo is required (or environment variable FOO)")
|
require.Error(t, err, "--foo is required (or environment variable FOO)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequiredWithEnvOnly(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
Foo string `arg:"required,--,-,env:FOO"`
|
||||||
|
}
|
||||||
|
_, err := parseWithEnv("", []string{}, &args)
|
||||||
|
require.Error(t, err, "environment variable FOO is required")
|
||||||
|
}
|
||||||
|
|
||||||
func TestShortFlag(t *testing.T) {
|
func TestShortFlag(t *testing.T) {
|
||||||
var args struct {
|
var args struct {
|
||||||
Foo string `arg:"-f"`
|
Foo string `arg:"-f"`
|
||||||
|
@ -845,6 +853,24 @@ func TestDefaultValuesIgnored(t *testing.T) {
|
||||||
assert.Equal(t, "", args.Foo)
|
assert.Equal(t, "", args.Foo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequiredEnvironmentOnlyVariableIsMissing(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
Foo string `arg:"required,--,env:FOO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := parseWithEnv("", []string{""}, &args)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalEnvironmentOnlyVariable(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
Foo string `arg:"env:FOO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := parseWithEnv("", []string{}, &args)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestEnvironmentVariableInSubcommandIgnored(t *testing.T) {
|
func TestEnvironmentVariableInSubcommandIgnored(t *testing.T) {
|
||||||
var args struct {
|
var args struct {
|
||||||
Sub *struct {
|
Sub *struct {
|
||||||
|
|
13
usage.go
13
usage.go
|
@ -312,7 +312,18 @@ func (p *Parser) printOption(w io.Writer, spec *spec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) printEnvOnlyVar(w io.Writer, spec *spec) {
|
func (p *Parser) printEnvOnlyVar(w io.Writer, spec *spec) {
|
||||||
printTwoCols(w, spec.env, "", "", "")
|
ways := make([]string, 0, 2)
|
||||||
|
if spec.required {
|
||||||
|
ways = append(ways, "Required.")
|
||||||
|
} else {
|
||||||
|
ways = append(ways, "Optional.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.help != "" {
|
||||||
|
ways = append(ways, spec.help)
|
||||||
|
}
|
||||||
|
|
||||||
|
printTwoCols(w, spec.env, strings.Join(ways, " "), spec.defaultString, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupCommand finds a subcommand based on a sequence of subcommand names. The
|
// lookupCommand finds a subcommand based on a sequence of subcommand names. The
|
||||||
|
|
|
@ -56,6 +56,10 @@ Options:
|
||||||
--testenv TESTENV, -a TESTENV [env: TEST_ENV]
|
--testenv TESTENV, -a TESTENV [env: TEST_ENV]
|
||||||
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
|
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
|
||||||
--help, -h display this help and exit
|
--help, -h display this help and exit
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
API_KEY Required. Only via env-var for security reasons
|
||||||
|
TRACE Optional. Record low-level trace
|
||||||
`
|
`
|
||||||
|
|
||||||
var args struct {
|
var args struct {
|
||||||
|
@ -70,6 +74,8 @@ Options:
|
||||||
Values []float64 `help:"Values"`
|
Values []float64 `help:"Values"`
|
||||||
Workers int `arg:"-w,env:WORKERS" help:"number of workers to start" default:"10"`
|
Workers int `arg:"-w,env:WORKERS" help:"number of workers to start" default:"10"`
|
||||||
TestEnv string `arg:"-a,env:TEST_ENV"`
|
TestEnv string `arg:"-a,env:TEST_ENV"`
|
||||||
|
ApiKey string `arg:"required,-,--,env:API_KEY" help:"Only via env-var for security reasons"`
|
||||||
|
Trace bool `arg:"-,--,env" help:"Record low-level trace"`
|
||||||
File *NameDotName `arg:"-f" help:"File with mandatory extension"`
|
File *NameDotName `arg:"-f" help:"File with mandatory extension"`
|
||||||
}
|
}
|
||||||
args.Name = "Foo Bar"
|
args.Name = "Foo Bar"
|
||||||
|
@ -554,12 +560,14 @@ Options:
|
||||||
--help, -h display this help and exit
|
--help, -h display this help and exit
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
ENVONLY
|
ENVONLY Optional.
|
||||||
CUSTOM
|
ENVONLY2 Optional.
|
||||||
|
CUSTOM Optional.
|
||||||
`
|
`
|
||||||
var args struct {
|
var args struct {
|
||||||
Short string `arg:"--,-s,env"`
|
Short string `arg:"--,-s,env"`
|
||||||
EnvOnly string `arg:"--,env"`
|
EnvOnly string `arg:"--,env"`
|
||||||
|
EnvOnly2 string `arg:"--,-,env"`
|
||||||
EnvOnlyOverriden string `arg:"--,env:CUSTOM"`
|
EnvOnlyOverriden string `arg:"--,env:CUSTOM"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,6 +583,35 @@ Environment variables:
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnvOnlyArgs(t *testing.T) {
|
||||||
|
expectedUsage := "Usage: example [--arg ARG]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [--arg ARG]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--arg ARG, -a ARG [env: MY_ARG]
|
||||||
|
--help, -h display this help and exit
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
AUTH_KEY Required.
|
||||||
|
`
|
||||||
|
var args struct {
|
||||||
|
ArgParam string `arg:"-a,--arg,env:MY_ARG"`
|
||||||
|
AuthKey string `arg:"required,--,env:AUTH_KEY"`
|
||||||
|
}
|
||||||
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
func TestFail(t *testing.T) {
|
func TestFail(t *testing.T) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var exitCode int
|
var exitCode int
|
||||||
|
@ -650,32 +687,3 @@ Options:
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFailEnvOnly(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [--arg ARG]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [--arg ARG]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--arg ARG, -a ARG [env: MY_ARG]
|
|
||||||
--help, -h display this help and exit
|
|
||||||
|
|
||||||
Environment variables:
|
|
||||||
AUTH_KEY
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
ArgParam string `arg:"-a,--arg,env:MY_ARG"`
|
|
||||||
AuthKey string `arg:"--,env:AUTH_KEY"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue