Feat: Add epilog after help text

Similar to the Description at the top of the
help text an Epilog is added at the bottom.

Resolves #189
This commit is contained in:
Sebastiaan Pasterkamp 2022-09-17 12:39:31 +02:00
parent ebd7a68a06
commit c8b9567d1b
4 changed files with 79 additions and 0 deletions

View File

@ -462,6 +462,9 @@ Options:
### Description strings ### 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 ```go
type args struct { type args struct {
Foo string Foo string
@ -487,6 +490,35 @@ Options:
--help, -h display this help and exit --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 ### Subcommands
*Introduced in version 1.1.0* *Introduced in version 1.1.0*

View File

@ -134,6 +134,7 @@ type Parser struct {
config Config config Config
version string version string
description string description 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 lastCmd *command
@ -155,6 +156,14 @@ type Described interface {
Description() string 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. // 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) { func walkFields(t reflect.Type, visit func(field reflect.StructField, owner reflect.Type) bool) {
walkFieldsImpl(t, visit, nil) walkFieldsImpl(t, visit, nil)
@ -236,6 +245,9 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
if dest, ok := dest.(Described); ok { if dest, ok := dest.(Described); ok {
p.description = dest.Description() p.description = dest.Description()
} }
if dest, ok := dest.(Epilogued); ok {
p.epilogue = dest.Epilogue()
}
} }
return &p, nil return &p, nil

View File

@ -290,6 +290,10 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
printTwoCols(w, subcmd.name, subcmd.help, "", "") printTwoCols(w, subcmd.name, subcmd.help, "", "")
} }
} }
if p.epilogue != "" {
fmt.Fprintln(w, "\n"+p.epilogue)
}
} }
func (p *Parser) printOption(w io.Writer, spec *spec) { func (p *Parser) printOption(w io.Writer, spec *spec) {

View File

@ -285,6 +285,37 @@ Options:
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String())) 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) { func TestUsageForRequiredPositionals(t *testing.T) {
expectedUsage := "Usage: example REQUIRED1 REQUIRED2\n" expectedUsage := "Usage: example REQUIRED1 REQUIRED2\n"
var args struct { var args struct {