add subcommand aliases
This commit is contained in:
parent
5ec29ce755
commit
0142b0b842
30
parse.go
30
parse.go
|
@ -62,6 +62,7 @@ type spec struct {
|
||||||
// command represents a named subcommand, or the top-level command
|
// command represents a named subcommand, or the top-level command
|
||||||
type command struct {
|
type command struct {
|
||||||
name string
|
name string
|
||||||
|
aliases []string
|
||||||
help string
|
help string
|
||||||
dest path
|
dest path
|
||||||
specs []*spec
|
specs []*spec
|
||||||
|
@ -153,7 +154,7 @@ type Parser struct {
|
||||||
epilogue string
|
epilogue string
|
||||||
|
|
||||||
// the following field changes during processing of command line arguments
|
// the following field changes during processing of command line arguments
|
||||||
lastCmd *command
|
subcommand []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Versioned is the interface that the destination struct should implement to
|
// Versioned is the interface that the destination struct should implement to
|
||||||
|
@ -384,18 +385,24 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
|
||||||
}
|
}
|
||||||
case key == "subcommand":
|
case key == "subcommand":
|
||||||
// decide on a name for the subcommand
|
// decide on a name for the subcommand
|
||||||
cmdname := value
|
var cmdnames []string
|
||||||
if cmdname == "" {
|
if value == "" {
|
||||||
cmdname = strings.ToLower(field.Name)
|
cmdnames = []string{strings.ToLower(field.Name)}
|
||||||
|
} else {
|
||||||
|
cmdnames = strings.Split(value, "|")
|
||||||
|
}
|
||||||
|
for i := range cmdnames {
|
||||||
|
cmdnames[i] = strings.TrimSpace(cmdnames[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the subcommand recursively
|
// parse the subcommand recursively
|
||||||
subcmd, err := cmdFromStruct(cmdname, subdest, field.Type)
|
subcmd, err := cmdFromStruct(cmdnames[0], subdest, field.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err.Error())
|
errs = append(errs, err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subcmd.aliases = cmdnames[1:]
|
||||||
subcmd.parent = &cmd
|
subcmd.parent = &cmd
|
||||||
subcmd.help = field.Tag.Get("help")
|
subcmd.help = field.Tag.Get("help")
|
||||||
|
|
||||||
|
@ -514,13 +521,13 @@ func (p *Parser) MustParse(args []string) {
|
||||||
err := p.Parse(args)
|
err := p.Parse(args)
|
||||||
switch {
|
switch {
|
||||||
case err == ErrHelp:
|
case err == ErrHelp:
|
||||||
p.writeHelpForSubcommand(p.config.Out, p.lastCmd)
|
p.WriteHelpForSubcommand(p.config.Out, p.subcommand...)
|
||||||
p.config.Exit(0)
|
p.config.Exit(0)
|
||||||
case err == ErrVersion:
|
case err == ErrVersion:
|
||||||
fmt.Fprintln(p.config.Out, p.version)
|
fmt.Fprintln(p.config.Out, p.version)
|
||||||
p.config.Exit(0)
|
p.config.Exit(0)
|
||||||
case err != nil:
|
case err != nil:
|
||||||
p.failWithSubcommand(err.Error(), p.lastCmd)
|
p.FailSubcommand(err.Error(), p.subcommand...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +584,7 @@ func (p *Parser) process(args []string) error {
|
||||||
|
|
||||||
// union of specs for the chain of subcommands encountered so far
|
// union of specs for the chain of subcommands encountered so far
|
||||||
curCmd := p.cmd
|
curCmd := p.cmd
|
||||||
p.lastCmd = curCmd
|
p.subcommand = nil
|
||||||
|
|
||||||
// make a copy of the specs because we will add to this list each time we expand a subcommand
|
// make a copy of the specs because we will add to this list each time we expand a subcommand
|
||||||
specs := make([]*spec, len(curCmd.specs))
|
specs := make([]*spec, len(curCmd.specs))
|
||||||
|
@ -648,7 +655,7 @@ func (p *Parser) process(args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
curCmd = subcmd
|
curCmd = subcmd
|
||||||
p.lastCmd = curCmd
|
p.subcommand = append(p.subcommand, arg)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,6 +849,11 @@ func findSubcommand(cmds []*command, name string) *command {
|
||||||
if cmd.name == name {
|
if cmd.name == name {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
for _, alias := range cmd.aliases {
|
||||||
|
if alias == name {
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -883,7 +883,8 @@ func TestEnvironmentVariableInSubcommandIgnored(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = p.Parse([]string{"sub"})
|
err = p.Parse([]string{"sub"})
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, args.Sub)
|
||||||
assert.Equal(t, "", args.Sub.Foo)
|
assert.Equal(t, "", args.Sub.Foo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1731,7 +1732,8 @@ func TestSubcommandGlobalFlag_InCommand_Strict_Inner(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = p.Parse([]string{"sub", "-g"})
|
err = p.Parse([]string{"sub", "-g"})
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.False(t, args.Global)
|
assert.False(t, args.Global)
|
||||||
|
require.NotNil(t, args.Sub)
|
||||||
assert.True(t, args.Sub.Guard)
|
assert.True(t, args.Sub.Guard)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package arg
|
package arg
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// Subcommand returns the user struct for the subcommand selected by
|
// Subcommand returns the user struct for the subcommand selected by
|
||||||
// the command line arguments most recently processed by the parser.
|
// the command line arguments most recently processed by the parser.
|
||||||
// The return value is always a pointer to a struct. If no subcommand
|
// The return value is always a pointer to a struct. If no subcommand
|
||||||
|
@ -7,31 +9,35 @@ package arg
|
||||||
// no command line arguments have been processed by this parser then it
|
// no command line arguments have been processed by this parser then it
|
||||||
// returns nil.
|
// returns nil.
|
||||||
func (p *Parser) Subcommand() interface{} {
|
func (p *Parser) Subcommand() interface{} {
|
||||||
if p.lastCmd == nil || p.lastCmd.parent == nil {
|
if len(p.subcommand) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return p.val(p.lastCmd.dest).Interface()
|
cmd, err := p.lookupCommand(p.subcommand...)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.val(cmd.dest).Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubcommandNames returns the sequence of subcommands specified by the
|
// SubcommandNames returns the sequence of subcommands specified by the
|
||||||
// user. If no subcommands were given then it returns an empty slice.
|
// user. If no subcommands were given then it returns an empty slice.
|
||||||
func (p *Parser) SubcommandNames() []string {
|
func (p *Parser) SubcommandNames() []string {
|
||||||
if p.lastCmd == nil {
|
return p.subcommand
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a list of ancestor commands
|
// lookupCommand finds a subcommand based on a sequence of subcommand names. The
|
||||||
var ancestors []string
|
// first string should be a top-level subcommand, the next should be a child
|
||||||
cur := p.lastCmd
|
// subcommand of that subcommand, and so on. If no strings are given then the
|
||||||
for cur.parent != nil { // we want to exclude the root
|
// root command is returned. If no such subcommand exists then an error is
|
||||||
ancestors = append(ancestors, cur.name)
|
// returned.
|
||||||
cur = cur.parent
|
func (p *Parser) lookupCommand(path ...string) (*command, error) {
|
||||||
|
cmd := p.cmd
|
||||||
|
for _, name := range path {
|
||||||
|
found := findSubcommand(cmd.subcommands, name)
|
||||||
|
if found == nil {
|
||||||
|
return nil, fmt.Errorf("%q is not a subcommand of %s", name, cmd.name)
|
||||||
}
|
}
|
||||||
|
cmd = found
|
||||||
// reverse the list
|
|
||||||
out := make([]string, len(ancestors))
|
|
||||||
for i := 0; i < len(ancestors); i++ {
|
|
||||||
out[i] = ancestors[len(ancestors)-i-1]
|
|
||||||
}
|
}
|
||||||
return out
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
140
usage.go
140
usage.go
|
@ -11,7 +11,7 @@ const colWidth = 25
|
||||||
|
|
||||||
// Fail prints usage information to stderr and exits with non-zero status
|
// Fail prints usage information to stderr and exits with non-zero status
|
||||||
func (p *Parser) Fail(msg string) {
|
func (p *Parser) Fail(msg string) {
|
||||||
p.failWithSubcommand(msg, p.cmd)
|
p.FailSubcommand(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailSubcommand prints usage information for a specified subcommand to stderr,
|
// FailSubcommand prints usage information for a specified subcommand to stderr,
|
||||||
|
@ -21,28 +21,19 @@ func (p *Parser) Fail(msg string) {
|
||||||
// a sequence of subcommand names starting with the top-level subcommand and so
|
// a sequence of subcommand names starting with the top-level subcommand and so
|
||||||
// on down the tree.
|
// on down the tree.
|
||||||
func (p *Parser) FailSubcommand(msg string, subcommand ...string) error {
|
func (p *Parser) FailSubcommand(msg string, subcommand ...string) error {
|
||||||
cmd, err := p.lookupCommand(subcommand...)
|
err := p.WriteUsageForSubcommand(p.config.Out, subcommand...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.failWithSubcommand(msg, cmd)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
p.config.Exit(-1)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteUsage writes usage information to the given writer
|
// WriteUsage writes usage information to the given writer
|
||||||
func (p *Parser) WriteUsage(w io.Writer) {
|
func (p *Parser) WriteUsage(w io.Writer) {
|
||||||
cmd := p.cmd
|
p.WriteUsageForSubcommand(w, p.subcommand...)
|
||||||
if p.lastCmd != nil {
|
|
||||||
cmd = p.lastCmd
|
|
||||||
}
|
|
||||||
p.writeUsageForSubcommand(w, cmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteUsageForSubcommand writes the usage information for a specified
|
// WriteUsageForSubcommand writes the usage information for a specified
|
||||||
|
@ -55,12 +46,7 @@ func (p *Parser) WriteUsageForSubcommand(w io.Writer, subcommand ...string) erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.writeUsageForSubcommand(w, cmd)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeUsageForSubcommand writes usage information for the given subcommand
|
|
||||||
func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
|
||||||
var positionals, longOptions, shortOptions []*spec
|
var positionals, longOptions, shortOptions []*spec
|
||||||
for _, spec := range cmd.specs {
|
for _, spec := range cmd.specs {
|
||||||
switch {
|
switch {
|
||||||
|
@ -77,18 +63,10 @@ func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
||||||
fmt.Fprintln(w, p.version)
|
fmt.Fprintln(w, p.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a list of ancestor commands so that we print with full context
|
|
||||||
var ancestors []string
|
|
||||||
ancestor := cmd
|
|
||||||
for ancestor != nil {
|
|
||||||
ancestors = append(ancestors, ancestor.name)
|
|
||||||
ancestor = ancestor.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
// print the beginning of the usage string
|
// print the beginning of the usage string
|
||||||
fmt.Fprint(w, "Usage:")
|
fmt.Fprintf(w, "Usage: %s", p.cmd.name)
|
||||||
for i := len(ancestors) - 1; i >= 0; i-- {
|
for _, s := range subcommand {
|
||||||
fmt.Fprint(w, " "+ancestors[i])
|
fmt.Fprint(w, " "+s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the option component of the usage message
|
// write the option component of the usage message
|
||||||
|
@ -149,47 +127,66 @@ func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprint(w, "\n")
|
fmt.Fprint(w, "\n")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printTwoCols(w io.Writer, left, help string, defaultVal string, envVal string) {
|
// print prints a line like this:
|
||||||
lhs := " " + left
|
//
|
||||||
|
// --option FOO A description of the option [default: 123]
|
||||||
|
//
|
||||||
|
// If the text on the left is longer than a certain threshold, the description is moved to the next line:
|
||||||
|
//
|
||||||
|
// --verylongoptionoption VERY_LONG_VARIABLE
|
||||||
|
// A description of the option [default: 123]
|
||||||
|
//
|
||||||
|
// If multiple "extras" are provided then they are put inside a single set of square brackets:
|
||||||
|
//
|
||||||
|
// --option FOO A description of the option [default: 123, env: FOO]
|
||||||
|
func print(w io.Writer, item, description string, bracketed ...string) {
|
||||||
|
lhs := " " + item
|
||||||
fmt.Fprint(w, lhs)
|
fmt.Fprint(w, lhs)
|
||||||
if help != "" {
|
if description != "" {
|
||||||
if len(lhs)+2 < colWidth {
|
if len(lhs)+2 < colWidth {
|
||||||
fmt.Fprint(w, strings.Repeat(" ", colWidth-len(lhs)))
|
fmt.Fprint(w, strings.Repeat(" ", colWidth-len(lhs)))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(w, "\n"+strings.Repeat(" ", colWidth))
|
fmt.Fprint(w, "\n"+strings.Repeat(" ", colWidth))
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, help)
|
fmt.Fprint(w, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
bracketsContent := []string{}
|
var brack string
|
||||||
|
for _, s := range bracketed {
|
||||||
if defaultVal != "" {
|
if s != "" {
|
||||||
bracketsContent = append(bracketsContent,
|
if brack != "" {
|
||||||
fmt.Sprintf("default: %s", defaultVal),
|
brack += ", "
|
||||||
)
|
}
|
||||||
|
brack += s
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if envVal != "" {
|
if brack != "" {
|
||||||
bracketsContent = append(bracketsContent,
|
fmt.Fprintf(w, " [%s]", brack)
|
||||||
fmt.Sprintf("env: %s", envVal),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(bracketsContent) > 0 {
|
|
||||||
fmt.Fprintf(w, " [%s]", strings.Join(bracketsContent, ", "))
|
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, "\n")
|
fmt.Fprint(w, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withDefault(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "default: " + s
|
||||||
|
}
|
||||||
|
|
||||||
|
func withEnv(env string) string {
|
||||||
|
if env == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "env: " + env
|
||||||
|
}
|
||||||
|
|
||||||
// WriteHelp writes the usage string followed by the full help string for each option
|
// WriteHelp writes the usage string followed by the full help string for each option
|
||||||
func (p *Parser) WriteHelp(w io.Writer) {
|
func (p *Parser) WriteHelp(w io.Writer) {
|
||||||
cmd := p.cmd
|
p.WriteHelpForSubcommand(w, p.subcommand...)
|
||||||
if p.lastCmd != nil {
|
|
||||||
cmd = p.lastCmd
|
|
||||||
}
|
|
||||||
p.writeHelpForSubcommand(w, cmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHelpForSubcommand writes the usage string followed by the full help
|
// WriteHelpForSubcommand writes the usage string followed by the full help
|
||||||
|
@ -202,12 +199,7 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.writeHelpForSubcommand(w, cmd)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeHelp writes the usage string for the given subcommand
|
|
||||||
func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
|
||||||
var positionals, longOptions, shortOptions, envOnlyOptions []*spec
|
var positionals, longOptions, shortOptions, envOnlyOptions []*spec
|
||||||
var hasVersionOption bool
|
var hasVersionOption bool
|
||||||
for _, spec := range cmd.specs {
|
for _, spec := range cmd.specs {
|
||||||
|
@ -226,13 +218,13 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
||||||
if p.description != "" {
|
if p.description != "" {
|
||||||
fmt.Fprintln(w, p.description)
|
fmt.Fprintln(w, p.description)
|
||||||
}
|
}
|
||||||
p.writeUsageForSubcommand(w, cmd)
|
p.WriteUsageForSubcommand(w, subcommand...)
|
||||||
|
|
||||||
// write the list of positionals
|
// write the list of positionals
|
||||||
if len(positionals) > 0 {
|
if len(positionals) > 0 {
|
||||||
fmt.Fprint(w, "\nPositional arguments:\n")
|
fmt.Fprint(w, "\nPositional arguments:\n")
|
||||||
for _, spec := range positionals {
|
for _, spec := range positionals {
|
||||||
printTwoCols(w, spec.placeholder, spec.help, "", "")
|
print(w, spec.placeholder, spec.help)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,13 +288,15 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
||||||
if len(cmd.subcommands) > 0 {
|
if len(cmd.subcommands) > 0 {
|
||||||
fmt.Fprint(w, "\nCommands:\n")
|
fmt.Fprint(w, "\nCommands:\n")
|
||||||
for _, subcmd := range cmd.subcommands {
|
for _, subcmd := range cmd.subcommands {
|
||||||
printTwoCols(w, subcmd.name, subcmd.help, "", "")
|
names := append([]string{subcmd.name}, subcmd.aliases...)
|
||||||
|
print(w, strings.Join(names, ", "), subcmd.help)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.epilogue != "" {
|
if p.epilogue != "" {
|
||||||
fmt.Fprintln(w, "\n"+p.epilogue)
|
fmt.Fprintln(w, "\n"+p.epilogue)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) printOption(w io.Writer, spec *spec) {
|
func (p *Parser) printOption(w io.Writer, spec *spec) {
|
||||||
|
@ -314,7 +308,7 @@ func (p *Parser) printOption(w io.Writer, spec *spec) {
|
||||||
ways = append(ways, synopsis(spec, "-"+spec.short))
|
ways = append(ways, synopsis(spec, "-"+spec.short))
|
||||||
}
|
}
|
||||||
if len(ways) > 0 {
|
if len(ways) > 0 {
|
||||||
printTwoCols(w, strings.Join(ways, ", "), spec.help, spec.defaultString, spec.env)
|
print(w, strings.Join(ways, ", "), spec.help, withDefault(spec.defaultString), withEnv(spec.env))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,29 +324,7 @@ func (p *Parser) printEnvOnlyVar(w io.Writer, spec *spec) {
|
||||||
ways = append(ways, spec.help)
|
ways = append(ways, spec.help)
|
||||||
}
|
}
|
||||||
|
|
||||||
printTwoCols(w, spec.env, strings.Join(ways, " "), spec.defaultString, "")
|
print(w, spec.env, strings.Join(ways, " "), withDefault(spec.defaultString))
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// root command is returned. If no such subcommand exists then an error is
|
|
||||||
// returned.
|
|
||||||
func (p *Parser) lookupCommand(path ...string) (*command, error) {
|
|
||||||
cmd := p.cmd
|
|
||||||
for _, name := range path {
|
|
||||||
var found *command
|
|
||||||
for _, child := range cmd.subcommands {
|
|
||||||
if child.name == name {
|
|
||||||
found = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if found == nil {
|
|
||||||
return nil, fmt.Errorf("%q is not a subcommand of %s", name, cmd.name)
|
|
||||||
}
|
|
||||||
cmd = found
|
|
||||||
}
|
|
||||||
return cmd, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func synopsis(spec *spec, form string) string {
|
func synopsis(spec *spec, form string) string {
|
||||||
|
|
|
@ -450,6 +450,8 @@ Global options:
|
||||||
|
|
||||||
_ = p.Parse([]string{"child", "nested", "value"})
|
_ = p.Parse([]string{"child", "nested", "value"})
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"child", "nested"}, p.SubcommandNames())
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
@ -471,7 +473,7 @@ func TestNonexistentSubcommand(t *testing.T) {
|
||||||
var args struct {
|
var args struct {
|
||||||
sub *struct{} `arg:"subcommand"`
|
sub *struct{} `arg:"subcommand"`
|
||||||
}
|
}
|
||||||
p, err := NewParser(Config{}, &args)
|
p, err := NewParser(Config{Exit: func(int) {}}, &args)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
|
@ -687,3 +689,29 @@ Options:
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHelpShowsSubcommandAliases(t *testing.T) {
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example <command> [<args>]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help, -h display this help and exit
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
remove, rm, r remove something from somewhere
|
||||||
|
simple do something simple
|
||||||
|
halt, stop stop now
|
||||||
|
`
|
||||||
|
|
||||||
|
var args struct {
|
||||||
|
Remove *struct{} `arg:"subcommand:remove|rm|r" help:"remove something from somewhere"`
|
||||||
|
Simple *struct{} `arg:"subcommand" help:"do something simple"`
|
||||||
|
Stop *struct{} `arg:"subcommand:halt|stop" help:"stop now"`
|
||||||
|
}
|
||||||
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue