options: Add kong.ShortUsageOnError() option

Add kong.ShortUsageOnError() option similar to kong.UsageOnError().
Add tests for UsageOnError and ShortUsageOnError.

Remove the HelpOption summary reset at the beginning of DefaultHelpPrinter:

	 func DefaultHelpPrinter(options HelpOptions, ctx *Context) error {
	-       if ctx.Empty() {
	-               options.Summary = false
	-       }

⚠️ I'm not really sure what the implications of this are, tests still
seem to pass, but maybe this has unintended side effects.
This commit is contained in:
Julia Ogris
2021-02-13 18:37:59 +11:00
committed by Alec Thomas
parent f306ae1529
commit 1300b2a3bd
4 changed files with 137 additions and 6 deletions
+15
View File
@@ -87,6 +87,21 @@ func DefaultHelpValueFormatter(value *Value) string {
}
}
// DefaultShortHelpPrinter is the default HelpPrinter for short help on error.
func DefaultShortHelpPrinter(options HelpOptions, ctx *Context) error {
w := newHelpWriter(ctx, options)
cmd := ctx.Selected()
app := ctx.Model
if cmd == nil {
w.Printf("Usage: %s%s", app.Name, app.Summary())
w.Printf(`Run "%s --help" for more information.`, app.Name)
} else {
w.Printf("Usage: %s %s", app.Name, cmd.Summary())
w.Printf(`Run "%s --help" for more information.`, cmd.FullPath())
}
return w.Write(ctx.Stdout)
}
// DefaultHelpPrinter is the default HelpPrinter.
func DefaultHelpPrinter(options HelpOptions, ctx *Context) error {
if ctx.Empty() {
+78
View File
@@ -2,6 +2,7 @@ package kong_test
import (
"bytes"
"fmt"
"strings"
"testing"
@@ -512,3 +513,80 @@ Group 2
require.Equal(t, expected, w.String())
})
}
func TestUsageOnError(t *testing.T) {
var cli struct {
Flag string `help:"A required flag." required`
}
w := &strings.Builder{}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Description("Some description."),
kong.Exit(func(int) {}),
kong.UsageOnError(),
)
_, err := p.Parse([]string{})
p.FatalIfErrorf(err)
expected := `Usage: test --flag=STRING
Some description.
Flags:
-h, --help Show context-sensitive help.
--flag=STRING A required flag.
test: error: missing flags: --flag=STRING
`
require.Equal(t, expected, w.String())
}
func TestShortUsageOnError(t *testing.T) {
var cli struct {
Flag string `help:"A required flag." required`
}
w := &strings.Builder{}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Description("Some description."),
kong.Exit(func(int) {}),
kong.ShortUsageOnError(),
)
_, err := p.Parse([]string{})
require.Error(t, err)
p.FatalIfErrorf(err)
expected := `Usage: test --flag=STRING
Run "test --help" for more information.
test: error: missing flags: --flag=STRING
`
require.Equal(t, expected, w.String())
}
func TestCustomShortUsageOnError(t *testing.T) {
var cli struct {
Flag string `help:"A required flag." required`
}
w := &strings.Builder{}
shortHelp := func(_ kong.HelpOptions, ctx *kong.Context) error {
fmt.Fprintln(ctx.Stdout, "🤷 wish I could help")
return nil
}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Description("Some description."),
kong.Exit(func(int) {}),
kong.ShortHelp(shortHelp),
kong.ShortUsageOnError(),
)
_, err := p.Parse([]string{})
require.Error(t, err)
p.FatalIfErrorf(err)
expected := `🤷 wish I could help
test: error: missing flags: --flag=STRING
`
require.Equal(t, expected, w.String())
}
+22 -5
View File
@@ -31,6 +31,13 @@ func Must(ast interface{}, options ...Option) *Kong {
return k
}
type usageOnError int
const (
shortUsage usageOnError = iota + 1
fullUsage
)
// Kong is the main parser type.
type Kong struct {
// Grammar model.
@@ -48,8 +55,9 @@ type Kong struct {
registry *Registry
noDefaultHelp bool
usageOnError bool
usageOnError usageOnError
help HelpPrinter
shortHelp HelpPrinter
helpFormatter HelpValueFormatter
helpOptions HelpOptions
helpFlag *Flag
@@ -86,6 +94,10 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
k.help = DefaultHelpPrinter
}
if k.shortHelp == nil {
k.shortHelp = DefaultShortHelpPrinter
}
model, err := build(k, grammar)
if err != nil {
return k, err
@@ -331,10 +343,15 @@ func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error()
}
// Maybe display usage information.
if err, ok := err.(*ParseError); ok && k.usageOnError {
options := k.helpOptions
_ = k.help(options, err.Context)
fmt.Fprintln(k.Stdout)
if err, ok := err.(*ParseError); ok {
switch k.usageOnError {
case fullUsage:
_ = k.help(k.helpOptions, err.Context)
fmt.Fprintln(k.Stdout)
case shortUsage:
_ = k.shortHelp(k.helpOptions, err.Context)
fmt.Fprintln(k.Stdout)
}
}
k.Fatalf("%s", msg)
}
+22 -1
View File
@@ -192,6 +192,17 @@ func Help(help HelpPrinter) Option {
})
}
// ShortHelp configures the short usage message.
//
// It should be used together with kong.ShortUsageOnError() to display a
// custom short usage message on errors.
func ShortHelp(shortHelp HelpPrinter) Option {
return OptionFunc(func(k *Kong) error {
k.shortHelp = shortHelp
return nil
})
}
// HelpFormatter configures how the help text is formatted.
func HelpFormatter(helpFormatter HelpValueFormatter) Option {
return OptionFunc(func(k *Kong) error {
@@ -251,7 +262,17 @@ func ExplicitGroups(groups []Group) Option {
// UsageOnError configures Kong to display context-sensitive usage if FatalIfErrorf is called with an error.
func UsageOnError() Option {
return OptionFunc(func(k *Kong) error {
k.usageOnError = true
k.usageOnError = fullUsage
return nil
})
}
// ShortUsageOnError configures Kong to display context-sensitive short
// usage if FatalIfErrorf is called with an error. The default short
// usage message can be overridden with kong.ShortHelp(...).
func ShortUsageOnError() Option {
return OptionFunc(func(k *Kong) error {
k.usageOnError = shortUsage
return nil
})
}