summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Flint <[email protected]>2024-06-30 12:27:39 -0400
committerGitHub <[email protected]>2024-06-30 12:27:39 -0400
commit0cc152dce52a7a61cc3cd73d25eae82adad99807 (patch)
treee0b101002f87ab9c708f539ce2b9034ca1c1cca1
parentb6422dcbc3ca07c96adfcab27e1f6b9a7af93160 (diff)
parentc087d7180231ea3cfc12a79ee091786ac9954e6a (diff)
Merge pull request #224 from hhromic/better-version-v2
Fix usage writing when using custom version flag
-rw-r--r--README.md3
-rw-r--r--usage.go52
-rw-r--r--usage_test.go212
3 files changed, 243 insertions, 24 deletions
diff --git a/README.md b/README.md
index cac383f..4d23034 100644
--- a/README.md
+++ b/README.md
@@ -301,6 +301,9 @@ $ ./example --version
someprogram 4.3.0
```
+> **Note**
+> If a `--version` flag is defined in `args` or any subcommand, it overrides the built-in versioning.
+
### Overriding option names
```go
diff --git a/usage.go b/usage.go
index f5e4b38..66a5be9 100644
--- a/usage.go
+++ b/usage.go
@@ -48,18 +48,36 @@ func (p *Parser) WriteUsageForSubcommand(w io.Writer, subcommand ...string) erro
}
var positionals, longOptions, shortOptions []*spec
+ var hasVersionOption bool
for _, spec := range cmd.specs {
switch {
case spec.positional:
positionals = append(positionals, spec)
case spec.long != "":
longOptions = append(longOptions, spec)
+ if spec.long == "version" {
+ hasVersionOption = true
+ }
case spec.short != "":
shortOptions = append(shortOptions, spec)
}
}
- if p.version != "" {
+ // make a list of ancestor commands so that we print with full context
+ // also determine if any ancestor has a version option spec
+ var ancestors []string
+ ancestor := cmd
+ for ancestor != nil {
+ for _, spec := range ancestor.specs {
+ if spec.long == "version" {
+ hasVersionOption = true
+ }
+ }
+ ancestors = append(ancestors, ancestor.name)
+ ancestor = ancestor.parent
+ }
+
+ if !hasVersionOption && p.version != "" {
fmt.Fprintln(w, p.version)
}
@@ -208,6 +226,9 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error
positionals = append(positionals, spec)
case spec.long != "":
longOptions = append(longOptions, spec)
+ if spec.long == "version" {
+ hasVersionOption = true
+ }
case spec.short != "":
shortOptions = append(shortOptions, spec)
case spec.short == "" && spec.long == "":
@@ -215,6 +236,21 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error
}
}
+ // obtain a flattened list of options from all ancestors
+ // also determine if any ancestor has a version option spec
+ var globals []*spec
+ ancestor := cmd.parent
+ for ancestor != nil {
+ for _, spec := range ancestor.specs {
+ if spec.long == "version" {
+ hasVersionOption = true
+ break
+ }
+ }
+ globals = append(globals, ancestor.specs...)
+ ancestor = ancestor.parent
+ }
+
if p.description != "" {
fmt.Fprintln(w, p.description)
}
@@ -236,28 +272,14 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error
}
for _, spec := range longOptions {
p.printOption(w, spec)
- if spec.long == "version" {
- hasVersionOption = true
- }
}
}
- // obtain a flattened list of options from all ancestors
- var globals []*spec
- ancestor := cmd.parent
- for ancestor != nil {
- globals = append(globals, ancestor.specs...)
- ancestor = ancestor.parent
- }
-
// write the list of global options
if len(globals) > 0 {
fmt.Fprint(w, "\nGlobal options:\n")
for _, spec := range globals {
p.printOption(w, spec)
- if spec.long == "version" {
- hasVersionOption = true
- }
}
}
diff --git a/usage_test.go b/usage_test.go
index 71324eb..a958abb 100644
--- a/usage_test.go
+++ b/usage_test.go
@@ -260,28 +260,199 @@ Options:
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
-type userDefinedVersionFlag struct {
- ShowVersion bool `arg:"--version" help:"this is a user-defined version flag"`
+func TestUsageWithUserDefinedVersionFlag(t *testing.T) {
+ expectedUsage := "Usage: example [--version]"
+
+ expectedHelp := `
+Usage: example [--version]
+
+Options:
+ --version this is a user-defined version flag
+ --help, -h display this help and exit
+`
+
+ var args struct {
+ ShowVersion bool `arg:"--version" help:"this is a user-defined version flag"`
+ }
+
+ os.Args[0] = "example"
+ p, err := NewParser(Config{}, &args)
+ require.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()))
}
-// Version returns the version for this program
-func (userDefinedVersionFlag) Version() string {
- return "example 3.2.1"
+func TestUsageWithVersionAndUserDefinedVersionFlag(t *testing.T) {
+ expectedUsage := "Usage: example [--version]"
+
+ expectedHelp := `
+Usage: example [--version]
+
+Options:
+ --version this is a user-defined version flag
+ --help, -h display this help and exit
+`
+
+ var args struct {
+ versioned
+ ShowVersion bool `arg:"--version" help:"this is a user-defined version flag"`
+ }
+
+ os.Args[0] = "example"
+ p, err := NewParser(Config{}, &args)
+ require.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 TestUsageWithUserDefinedVersionFlag(t *testing.T) {
- expectedUsage := "example 3.2.1\nUsage: example [--version]"
+type subcommand struct {
+ Number int `arg:"-n,--number" help:"compute something on the given number"`
+}
+
+func TestUsageWithVersionAndSubcommand(t *testing.T) {
+ expectedUsage := "example 3.2.1\nUsage: example <command> [<args>]"
expectedHelp := `
example 3.2.1
-Usage: example [--version]
+Usage: example <command> [<args>]
+
+Options:
+ --help, -h display this help and exit
+ --version display version and exit
+
+Commands:
+ cmd
+`
+
+ var args struct {
+ versioned
+ Cmd *subcommand `arg:"subcommand"`
+ }
+
+ os.Args[0] = "example"
+ p, err := NewParser(Config{}, &args)
+ require.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()))
+
+ expectedUsage = "example 3.2.1\nUsage: example cmd [--number NUMBER]"
+
+ expectedHelp = `
+example 3.2.1
+Usage: example cmd [--number NUMBER]
+
+Options:
+ --number NUMBER, -n NUMBER
+ compute something on the given number
+ --help, -h display this help and exit
+ --version display version and exit
+`
+ _ = p.Parse([]string{"cmd"})
+
+ help = bytes.Buffer{}
+ p.WriteHelp(&help)
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ usage = bytes.Buffer{}
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
+}
+
+func TestUsageWithUserDefinedVersionFlagAndSubcommand(t *testing.T) {
+ expectedUsage := "Usage: example [--version] <command> [<args>]"
+
+ expectedHelp := `
+Usage: example [--version] <command> [<args>]
+
+Options:
+ --version this is a user-defined version flag
+ --help, -h display this help and exit
+
+Commands:
+ cmd
+`
+
+ var args struct {
+ Cmd *subcommand `arg:"subcommand"`
+ ShowVersion bool `arg:"--version" help:"this is a user-defined version flag"`
+ }
+
+ os.Args[0] = "example"
+ p, err := NewParser(Config{}, &args)
+ require.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()))
+
+ expectedUsage = "Usage: example cmd [--number NUMBER]"
+
+ expectedHelp = `
+Usage: example cmd [--number NUMBER]
+
+Options:
+ --number NUMBER, -n NUMBER
+ compute something on the given number
+
+Global options:
+ --version this is a user-defined version flag
+ --help, -h display this help and exit
+`
+ _ = p.Parse([]string{"cmd"})
+
+ help = bytes.Buffer{}
+ p.WriteHelp(&help)
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ usage = bytes.Buffer{}
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
+}
+
+func TestUsageWithVersionAndUserDefinedVersionFlagAndSubcommand(t *testing.T) {
+ expectedUsage := "Usage: example [--version] <command> [<args>]"
+
+ expectedHelp := `
+Usage: example [--version] <command> [<args>]
Options:
--version this is a user-defined version flag
--help, -h display this help and exit
+
+Commands:
+ cmd
`
+
+ var args struct {
+ versioned
+ Cmd *subcommand `arg:"subcommand"`
+ ShowVersion bool `arg:"--version" help:"this is a user-defined version flag"`
+ }
+
os.Args[0] = "example"
- p, err := NewParser(Config{}, &userDefinedVersionFlag{})
+ p, err := NewParser(Config{}, &args)
require.NoError(t, err)
var help bytes.Buffer
@@ -291,6 +462,29 @@ Options:
var usage bytes.Buffer
p.WriteUsage(&usage)
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
+
+ expectedUsage = "Usage: example cmd [--number NUMBER]"
+
+ expectedHelp = `
+Usage: example cmd [--number NUMBER]
+
+Options:
+ --number NUMBER, -n NUMBER
+ compute something on the given number
+
+Global options:
+ --version this is a user-defined version flag
+ --help, -h display this help and exit
+`
+ _ = p.Parse([]string{"cmd"})
+
+ help = bytes.Buffer{}
+ p.WriteHelp(&help)
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ usage = bytes.Buffer{}
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
type described struct{}