diff --git a/README.md b/README.md index 7f1fdca..f105b17 100644 --- a/README.md +++ b/README.md @@ -462,6 +462,9 @@ Options: ### Description strings +A descriptive message can be added at the top of the help text by implementing +a `Description` function that returns a string. + ```go type args struct { Foo string @@ -487,6 +490,35 @@ Options: --help, -h display this help and exit ``` +Similarly an epilogue can be added at the end of the help text by implementing +the `Epilogue` function. + +```go +type args struct { + Foo string +} + +func (args) Epilogue() string { + return "For more information visit github.com/alexflint/go-arg" +} + +func main() { + var args args + arg.MustParse(&args) +} +``` + +```shell +$ ./example -h +Usage: example [--foo FOO] + +Options: + --foo FOO + --help, -h display this help and exit + +For more information visit github.com/alexflint/go-arg +``` + ### Subcommands *Introduced in version 1.1.0* diff --git a/parse.go b/parse.go index a883a10..c8cd79e 100644 --- a/parse.go +++ b/parse.go @@ -134,6 +134,7 @@ type Parser struct { config Config version string description string + epilogue string // the following field changes during processing of command line arguments lastCmd *command @@ -155,6 +156,14 @@ type Described interface { Description() string } +// Epilogued is the interface that the destination struct should implement to +// add an epilogue string at the bottom of the help message. +type Epilogued interface { + // Epilogue returns the string that will be printed on a line by itself + // at the end of the help message. + Epilogue() string +} + // walkFields calls a function for each field of a struct, recursively expanding struct fields. func walkFields(t reflect.Type, visit func(field reflect.StructField, owner reflect.Type) bool) { walkFieldsImpl(t, visit, nil) @@ -236,6 +245,9 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) { if dest, ok := dest.(Described); ok { p.description = dest.Description() } + if dest, ok := dest.(Epilogued); ok { + p.epilogue = dest.Epilogue() + } } return &p, nil diff --git a/usage.go b/usage.go index e936811..7ba06cc 100644 --- a/usage.go +++ b/usage.go @@ -290,6 +290,10 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) { printTwoCols(w, subcmd.name, subcmd.help, "", "") } } + + if p.epilogue != "" { + fmt.Fprintln(w, "\n"+p.epilogue) + } } func (p *Parser) printOption(w io.Writer, spec *spec) { diff --git a/usage_test.go b/usage_test.go index 1744536..fd67fc8 100644 --- a/usage_test.go +++ b/usage_test.go @@ -285,6 +285,37 @@ Options: assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String())) } +type epilogued struct{} + +// Epilogued returns the epilogue for this program +func (epilogued) Epilogue() string { + return "For more information visit github.com/alexflint/go-arg" +} + +func TestUsageWithEpilogue(t *testing.T) { + expectedUsage := "Usage: example" + + expectedHelp := ` +Usage: example + +Options: + --help, -h display this help and exit + +For more information visit github.com/alexflint/go-arg +` + os.Args[0] = "example" + p, err := NewParser(Config{}, &epilogued{}) + 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 TestUsageForRequiredPositionals(t *testing.T) { expectedUsage := "Usage: example REQUIRED1 REQUIRED2\n" var args struct {