From c3cac76438ca36d9291d2a26f3ba558ef929d588 Mon Sep 17 00:00:00 2001 From: Pablo Diaz Date: Sat, 21 May 2022 17:24:45 +0200 Subject: [PATCH] added tests and fixed usage --- example_test.go | 32 +++++++++++++++++++++++++++++--- parse.go | 2 +- usage.go | 21 ++++++++++++++++++++- usage_test.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/example_test.go b/example_test.go index acfacad..6216e18 100644 --- a/example_test.go +++ b/example_test.go @@ -499,12 +499,12 @@ func Example_allSupportedTypes() { func Example_envVarOnly() { os.Args = split("./example") - _ = os.Setenv("NUM_WORKERS", "my_key") + _ = os.Setenv("AUTH_KEY", "my_key") - defer os.Unsetenv("NUM_WORKERS") + defer os.Unsetenv("AUTH_KEY") var args struct { - AuthKey string `arg:"-,--,env:NUM_WORKERS"` + AuthKey string `arg:"-,--,env:AUTH_KEY"` } MustParse(&args) @@ -512,3 +512,29 @@ func Example_envVarOnly() { fmt.Println(args.AuthKey) // output: my_key } + +func Example_envVarOnlyShouldIgnoreFlag() { + os.Args = split("./example --=my_key") + + var args struct { + AuthKey string `arg:"-,--,env:AUTH_KEY"` + } + + err := Parse(&args) + + fmt.Println(err) + // output: unknown argument --=my_key +} + +func Example_envVarOnlyShouldIgnoreShortFlag() { + os.Args = split("./example -=my_key") + + var args struct { + AuthKey string `arg:"-,--,env:AUTH_KEY"` + } + + err := Parse(&args) + + fmt.Println(err) + // output: unknown argument -=my_key +} diff --git a/parse.go b/parse.go index e5df067..aee60e7 100644 --- a/parse.go +++ b/parse.go @@ -656,7 +656,7 @@ func (p *Parser) process(args []string) error { // lookup the spec for this option (note that the "specs" slice changes as // we expand subcommands so it is better not to use a map) spec := findOption(specs, opt) - if spec == nil { + if spec == nil || opt == "" { return fmt.Errorf("unknown argument %s", arg) } wasPresent[spec] = true diff --git a/usage.go b/usage.go index 43d6231..f032149 100644 --- a/usage.go +++ b/usage.go @@ -84,6 +84,11 @@ func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) { ancestors = append(ancestors, ancestor.name) ancestor = ancestor.parent } + for _, spec := range cmd.specs { + if spec.short == "" && spec.long == "" { + ancestors = append(ancestors, spec.env+"="+strings.ToLower(spec.env)+"_value") + } + } // print the beginning of the usage string fmt.Fprint(w, "Usage:") @@ -208,7 +213,7 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error // writeHelp writes the usage string for the given subcommand func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) { - var positionals, longOptions, shortOptions []*spec + var positionals, longOptions, shortOptions, envOnlyOptions []*spec for _, spec := range cmd.specs { switch { case spec.positional: @@ -217,6 +222,8 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) { longOptions = append(longOptions, spec) case spec.short != "": shortOptions = append(shortOptions, spec) + case spec.short == "" && spec.long == "": + envOnlyOptions = append(envOnlyOptions, spec) } } @@ -275,6 +282,14 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) { }) } + // write the list of environment only variables + if len(shortOptions)+len(longOptions) > 0 || cmd.parent == nil { + fmt.Fprint(w, "\nEnvironment variables:\n") + for _, spec := range envOnlyOptions { + p.printEnvOnlyVar(w, spec) + } + } + // write the list of subcommands if len(cmd.subcommands) > 0 { fmt.Fprint(w, "\nCommands:\n") @@ -301,6 +316,10 @@ func (p *Parser) printOption(w io.Writer, spec *spec) { } } +func (p *Parser) printEnvOnlyVar(w io.Writer, spec *spec) { + printTwoCols(w, spec.env, "", "", "") +} + // lookupCommand finds a subcommand based on a sequence of subcommand names. The // first string should be a top-level subcommand, the next should be a child // subcommand of that subcommand, and so on. If no strings are given then the diff --git a/usage_test.go b/usage_test.go index 69feac2..d5e150f 100644 --- a/usage_test.go +++ b/usage_test.go @@ -646,3 +646,32 @@ Options: p.WriteHelp(&help) assert.Equal(t, expectedHelp[1:], help.String()) } + +func TestFailEnvOnly(t *testing.T) { + expectedUsage := "Usage: AUTH_KEY=auth_key_value example [--arg ARG]" + + expectedHelp := ` +Usage: AUTH_KEY=auth_key_value 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())) +}