Skip to content

Commit 1f6498e

Browse files
authored
Merge pull request #292 from zeroshade/allow-hidden
feat: allow hiding a subcommand using "-" for help
2 parents db36505 + 371f18e commit 1f6498e

4 files changed

Lines changed: 70 additions & 1 deletion

File tree

doc.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
// (e.g. `./example -d`), and any tag string that starts with two hyphens is the long
3434
// form for the argument (instead of the field name).
3535
//
36-
// Other valid tag strings are `positional` and `required`.
36+
// Other valid tag strings are `positional`, `required`, and `hidden`. Using `hidden` will
37+
// exclude the field from help text and usage messages.
3738
//
3839
// Fields can be excluded from processing with `arg:"-"`.
3940
package arg

parse.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type spec struct {
5353
positional bool // if true, this option will be looked for in the positional flags
5454
separate bool // if true, each slice and map entry will have its own --flag
5555
help string // the help text for this option
56+
hidden bool // if true, this option will be hidden from help text
5657
env string // the name of the environment variable for this option, or empty for none
5758
defaultValue reflect.Value // default value for this option
5859
defaultString string // default value for this option, in string form to be displayed in help text
@@ -68,6 +69,7 @@ type command struct {
6869
specs []*spec
6970
subcommands []*command
7071
parent *command
72+
hidden bool
7173
}
7274

7375
// ErrHelp indicates that the builtin -h or --help were provided
@@ -386,6 +388,8 @@ func cmdFromStruct(name string, dest path, t reflect.Type, envPrefix string) (*c
386388
spec.separate = true
387389
case key == "help": // deprecated
388390
spec.help = value
391+
case key == "hidden":
392+
spec.hidden = true
389393
case key == "env":
390394
// Use override name if provided
391395
if value != "" {
@@ -436,6 +440,9 @@ func cmdFromStruct(name string, dest path, t reflect.Type, envPrefix string) (*c
436440

437441
// if this is a subcommand then we've done everything we need to do
438442
if isSubcommand {
443+
if spec.hidden {
444+
cmd.subcommands[len(cmd.subcommands)-1].hidden = true
445+
}
439446
return false
440447
}
441448

usage.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ func (p *Parser) WriteUsageForSubcommand(w io.Writer, subcommand ...string) erro
4949

5050
var positionals, longOptions, shortOptions []*spec
5151
for _, spec := range cmd.specs {
52+
if spec.hidden {
53+
continue
54+
}
5255
switch {
5356
case spec.positional:
5457
positionals = append(positionals, spec)
@@ -199,6 +202,10 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error
199202
var positionals, longOptions, shortOptions, envOnlyOptions []*spec
200203
var hasVersionOption bool
201204
for _, spec := range cmd.specs {
205+
if spec.hidden {
206+
continue
207+
}
208+
202209
switch {
203210
case spec.positional:
204211
positionals = append(positionals, spec)
@@ -293,6 +300,11 @@ func (p *Parser) WriteHelpForSubcommand(w io.Writer, subcommand ...string) error
293300
if len(cmd.subcommands) > 0 {
294301
fmt.Fprint(w, "\nCommands:\n")
295302
for _, subcmd := range cmd.subcommands {
303+
if subcmd.hidden {
304+
// skip this subcommand in the help message
305+
continue
306+
}
307+
296308
names := append([]string{subcmd.name}, subcmd.aliases...)
297309
print(w, strings.Join(names, ", "), subcmd.help)
298310
}

usage_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,31 @@ Commands:
10161016
assert.Equal(t, expectedHelp[1:], help.String())
10171017
}
10181018

1019+
func TestHelpSkipsSubcommandHelpDash(t *testing.T) {
1020+
expectedHelp := `
1021+
Usage: example <command> [<args>]
1022+
1023+
Options:
1024+
--help, -h display this help and exit
1025+
1026+
Commands:
1027+
remove, rm, r remove something from somewhere
1028+
halt, stop stop now
1029+
`
1030+
1031+
var args struct {
1032+
Remove *struct{} `arg:"subcommand:remove|rm|r" help:"remove something from somewhere"`
1033+
Simple *struct{} `arg:"subcommand,hidden" help:"simple hidden subcommand"`
1034+
Stop *struct{} `arg:"subcommand:halt|stop" help:"stop now"`
1035+
}
1036+
p, err := NewParser(Config{Program: "example"}, &args)
1037+
require.NoError(t, err)
1038+
1039+
var help bytes.Buffer
1040+
p.WriteHelp(&help)
1041+
assert.Equal(t, expectedHelp[1:], help.String())
1042+
}
1043+
10191044
func TestHelpShowsPositionalWithDefault(t *testing.T) {
10201045
expectedHelp := `
10211046
Usage: example [FOO]
@@ -1039,6 +1064,30 @@ Options:
10391064
assert.Equal(t, expectedHelp[1:], help.String())
10401065
}
10411066

1067+
func TestHelpShowsPositionalWithDefaultSkipHidden(t *testing.T) {
1068+
expectedHelp := `
1069+
Usage: example [FOO]
1070+
1071+
Positional arguments:
1072+
FOO this is a positional with a default [default: bar]
1073+
1074+
Options:
1075+
--help, -h display this help and exit
1076+
`
1077+
1078+
var args struct {
1079+
Foo string `arg:"positional" default:"bar" help:"this is a positional with a default"`
1080+
Bar string `arg:"positional,hidden" default:"baz" help:"this is a hidden positional with a default"`
1081+
}
1082+
1083+
p, err := NewParser(Config{Program: "example"}, &args)
1084+
require.NoError(t, err)
1085+
1086+
var help bytes.Buffer
1087+
p.WriteHelp(&help)
1088+
assert.Equal(t, expectedHelp[1:], help.String())
1089+
}
1090+
10421091
func TestHelpShowsPositionalWithEnv(t *testing.T) {
10431092
expectedHelp := `
10441093
Usage: example [FOO]

0 commit comments

Comments
 (0)