Allow optional subcommands
diff --git a/command.go b/command.go
index 5e99cd6..b55597f 100644
--- a/command.go
+++ b/command.go
@@ -14,6 +14,9 @@
// The active sub command (set by parsing) or nil
Active *Command
+ // Whether subcommands are optional
+ SubcommandsOptional bool
+
commands []*Command
hasBuiltinHelpGroup bool
}
diff --git a/command_private.go b/command_private.go
index 0195a46..2cc7fbc 100644
--- a/command_private.go
+++ b/command_private.go
@@ -37,11 +37,18 @@
shortDescription := mtag.Get("description")
longDescription := mtag.Get("long-description")
+ subcommandsOptional := mtag.Get("subcommands-optional")
- if _, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface()); err != nil {
+ subc, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface())
+
+ if err != nil {
return true, err
}
+ if len(subcommandsOptional) > 0 {
+ subc.SubcommandsOptional = true
+ }
+
return true, nil
}
diff --git a/command_test.go b/command_test.go
index 9b903a2..230f981 100644
--- a/command_test.go
+++ b/command_test.go
@@ -293,3 +293,28 @@
}
}
+func TestSubcommandsOptional(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Cmd1 struct {
+ } `command:"remove"`
+
+ Cmd2 struct {
+ } `command:"add"`
+ }{}
+
+ p := NewParser(&opts, None)
+ p.SubcommandsOptional = true
+
+ _, err := p.ParseArgs([]string{"-v"})
+
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ return
+ }
+
+ if !opts.Value {
+ t.Errorf("Expected Value to be true")
+ }
+}
diff --git a/flags.go b/flags.go
index 9b77286..ef81991 100644
--- a/flags.go
+++ b/flags.go
@@ -87,6 +87,9 @@
// command: when specified on a struct field, makes the struct field
// a (sub)command with the given name (optional).
//
+// subcommands-optional: when specified on a command struct field, makes
+// any subcommands of that command optional.
+//
// Either short: or long: must be specified to make the field eligible as an
// option.
//
diff --git a/help.go b/help.go
index c2192ed..04540d2 100644
--- a/help.go
+++ b/help.go
@@ -237,8 +237,16 @@
}
if allcmd.Active == nil && len(allcmd.commands) > 0 {
+ var co, cc string
+
+ if allcmd.SubcommandsOptional {
+ co, cc = "[", "]"
+ } else {
+ co, cc = "<", ">"
+ }
+
if len(allcmd.commands) > 3 {
- fmt.Fprintf(wr, " <command>")
+ fmt.Fprintf(wr, " %scommand%s", co, cc)
} else {
subcommands := allcmd.sortedCommands()
names := make([]string, len(subcommands))
@@ -247,7 +255,7 @@
names[i] = subc.Name
}
- fmt.Fprintf(wr, " <%s>", strings.Join(names, " | "))
+ fmt.Fprintf(wr, " %s%s%s", co, strings.Join(names, " | "), cc)
}
}
diff --git a/parser.go b/parser.go
index b453866..677455b 100644
--- a/parser.go
+++ b/parser.go
@@ -202,7 +202,7 @@
return nil, p.printError(s.err)
}
- if len(s.command.commands) != 0 {
+ if len(s.command.commands) != 0 && !s.command.SubcommandsOptional {
return nil, p.printError(s.estimateCommand())
} else if cmd, ok := s.command.data.(Commander); ok {
return nil, p.printError(cmd.Execute(s.retargs))