Display usage information on error.
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
1. [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
|
1. [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
|
||||||
1. [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
|
1. [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
|
||||||
1. [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
|
1. [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
|
||||||
1. [`Help(HelpFunc)` - customising help](#helphelpfunc---customising-help)
|
1. [`HelpOptions(...HelpOption)` and `Help(HelpFunc)` - customising help](#helpoptionshelpoption-and-helphelpfunc---customising-help)
|
||||||
1. [`Hook(&field, HookFunc)` - callback hooks to execute when the command-line is parsed](#hookfield-hookfunc---callback-hooks-to-execute-when-the-command-line-is-parsed)
|
1. [`Hook(&field, HookFunc)` - callback hooks to execute when the command-line is parsed](#hookfield-hookfunc---callback-hooks-to-execute-when-the-command-line-is-parsed)
|
||||||
1. [Other options](#other-options)
|
1. [Other options](#other-options)
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ All builtin Go types (as well as a bunch of useful stdlib types like `time.Time`
|
|||||||
3. `TypeMapper(reflect.Type, Mapper)`.
|
3. `TypeMapper(reflect.Type, Mapper)`.
|
||||||
4. `ValueMapper(interface{}, Mapper)`, passing in a pointer to a field of the grammar.
|
4. `ValueMapper(interface{}, Mapper)`, passing in a pointer to a field of the grammar.
|
||||||
|
|
||||||
### `Help(HelpFunc)` - customising help
|
### `HelpOptions(...HelpOption)` and `Help(HelpFunc)` - customising help
|
||||||
|
|
||||||
The default help output is usually sufficient, but if it's not, there are two solutions.
|
The default help output is usually sufficient, but if it's not, there are two solutions.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ var cli struct {
|
|||||||
Force bool `help:"Force removal." short:"f"`
|
Force bool `help:"Force removal." short:"f"`
|
||||||
Recursive bool `help:"Recursively remove files." short:"r"`
|
Recursive bool `help:"Recursively remove files." short:"r"`
|
||||||
|
|
||||||
Paths []string `arg:"" help:"Paths to remove." type:"path"`
|
Paths []string `arg:"" help:"Paths to remove." type:"path" name:"path"`
|
||||||
} `cmd:"" help:"Remove files."`
|
} `cmd:"" help:"Remove files."`
|
||||||
|
|
||||||
Ls struct {
|
Ls struct {
|
||||||
|
|||||||
+7
-4
@@ -43,11 +43,11 @@ func (p *Path) Node() *Node {
|
|||||||
// Context contains the current parse context.
|
// Context contains the current parse context.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
App *Kong
|
App *Kong
|
||||||
Path []*Path // A trace through parsed nodes.
|
Path []*Path // A trace through parsed nodes.
|
||||||
Error error // Error that occurred during trace, if any.
|
Args []string // Original command-line arguments.
|
||||||
|
Error error // Error that occurred during trace, if any.
|
||||||
|
|
||||||
values map[*Value]reflect.Value // Temporary values during tracing.
|
values map[*Value]reflect.Value // Temporary values during tracing.
|
||||||
args []string
|
|
||||||
scan *Scanner
|
scan *Scanner
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func (c *Context) Selected() *Node {
|
|||||||
func Trace(k *Kong, args []string) (*Context, error) {
|
func Trace(k *Kong, args []string) (*Context, error) {
|
||||||
c := &Context{
|
c := &Context{
|
||||||
App: k,
|
App: k,
|
||||||
args: args,
|
Args: args,
|
||||||
Path: []*Path{
|
Path: []*Path{
|
||||||
{App: k.Model, Flags: k.Model.Flags},
|
{App: k.Model, Flags: k.Model.Flags},
|
||||||
},
|
},
|
||||||
@@ -461,6 +461,9 @@ func checkMissingChildren(node *Node) error {
|
|||||||
if len(missing) == 1 {
|
if len(missing) == 1 {
|
||||||
return fmt.Errorf("%q should be followed by %s", node.Path(), missing[0])
|
return fmt.Errorf("%q should be followed by %s", node.Path(), missing[0])
|
||||||
}
|
}
|
||||||
|
if len(missing) > 5 {
|
||||||
|
missing = append(missing[:5], "...")
|
||||||
|
}
|
||||||
return fmt.Errorf("%q should be followed by one of %s", node.Path(), strings.Join(missing, ", "))
|
return fmt.Errorf("%q should be followed by one of %s", node.Path(), strings.Join(missing, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
// ParseError is the error type returned by Kong.Parse().
|
||||||
|
//
|
||||||
|
// It contains the parse Context that triggered the error.
|
||||||
|
type ParseError struct {
|
||||||
|
error
|
||||||
|
Context *Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause returns the original cause of the error.
|
||||||
|
func (p *ParseError) Cause() error { return p.error }
|
||||||
@@ -38,12 +38,13 @@ type Kong struct {
|
|||||||
Stdout io.Writer
|
Stdout io.Writer
|
||||||
Stderr io.Writer
|
Stderr io.Writer
|
||||||
|
|
||||||
before map[reflect.Value]HookFunc
|
before map[reflect.Value]HookFunc
|
||||||
resolvers []ResolverFunc
|
resolvers []ResolverFunc
|
||||||
registry *Registry
|
registry *Registry
|
||||||
noDefaultHelp bool
|
noDefaultHelp bool
|
||||||
help func(*Context) error
|
noUsageOnError bool
|
||||||
helpOptions []HelpOption
|
help func(*Context) error
|
||||||
|
helpOptions []HelpOption
|
||||||
|
|
||||||
// Set temporarily by Options. These are applied after build().
|
// Set temporarily by Options. These are applied after build().
|
||||||
postBuildOptions []Option
|
postBuildOptions []Option
|
||||||
@@ -133,6 +134,9 @@ func (k *Kong) Help(args []string) error {
|
|||||||
//
|
//
|
||||||
// The returned "command" is a space separated path to the final selected command, if any. Commands appear as
|
// The returned "command" is a space separated path to the final selected command, if any. Commands appear as
|
||||||
// the command name while positional arguments are the argument name surrounded by "<argument>".
|
// the command name while positional arguments are the argument name surrounded by "<argument>".
|
||||||
|
//
|
||||||
|
// Will return a ParseError if a *semantically* invalid command-line is encountered (as opposed to a syntactically
|
||||||
|
// invalid one, which will report a normal error).
|
||||||
func (k *Kong) Parse(args []string) (command string, err error) {
|
func (k *Kong) Parse(args []string) (command string, err error) {
|
||||||
defer catch(&err)
|
defer catch(&err)
|
||||||
ctx, err := Trace(k, args)
|
ctx, err := Trace(k, args)
|
||||||
@@ -140,13 +144,13 @@ func (k *Kong) Parse(args []string) (command string, err error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if err = k.applyHooks(ctx); err != nil {
|
if err = k.applyHooks(ctx); err != nil {
|
||||||
return "", err
|
return "", &ParseError{error: err, Context: ctx}
|
||||||
}
|
}
|
||||||
if ctx.Error != nil {
|
if ctx.Error != nil {
|
||||||
return "", ctx.Error
|
return "", &ParseError{error: ctx.Error, Context: ctx}
|
||||||
}
|
}
|
||||||
if err = ctx.Validate(); err != nil {
|
if err = ctx.Validate(); err != nil {
|
||||||
return "", err
|
return "", &ParseError{error: err, Context: ctx}
|
||||||
}
|
}
|
||||||
return ctx.Apply()
|
return ctx.Apply()
|
||||||
}
|
}
|
||||||
@@ -210,6 +214,11 @@ func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
|
|||||||
msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error()
|
msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error()
|
||||||
}
|
}
|
||||||
k.Errorf("%s", msg)
|
k.Errorf("%s", msg)
|
||||||
|
// Maybe display usage information.
|
||||||
|
if err, ok := err.(*ParseError); ok && !k.noUsageOnError {
|
||||||
|
fmt.Fprintln(k.Stdout)
|
||||||
|
_ = k.help(err.Context)
|
||||||
|
}
|
||||||
k.Exit(1)
|
k.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,14 @@ func HelpOptions(options ...HelpOption) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoUsageOnError configures Kong to NOT display context-sensitive usage if FatalIfErrorf is called with an error.
|
||||||
|
func NoUsageOnError() Option {
|
||||||
|
return func(k *Kong) error {
|
||||||
|
k.noUsageOnError = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ClearResolvers clears all existing resolvers.
|
// ClearResolvers clears all existing resolvers.
|
||||||
func ClearResolvers() Option {
|
func ClearResolvers() Option {
|
||||||
return func(k *Kong) error {
|
return func(k *Kong) error {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Tag struct {
|
|||||||
Short rune
|
Short rune
|
||||||
Hidden bool
|
Hidden bool
|
||||||
Sep rune
|
Sep rune
|
||||||
|
Enum map[string]bool
|
||||||
|
|
||||||
// Storage for all tag keys for arbitrary lookups.
|
// Storage for all tag keys for arbitrary lookups.
|
||||||
items map[string]string
|
items map[string]string
|
||||||
@@ -112,6 +113,7 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
|
|||||||
s, chars := getTagInfo(ft)
|
s, chars := getTagInfo(ft)
|
||||||
t := &Tag{
|
t := &Tag{
|
||||||
items: parseTagItems(s, chars),
|
items: parseTagItems(s, chars),
|
||||||
|
Enum: map[string]bool{},
|
||||||
}
|
}
|
||||||
t.Cmd = t.Has("cmd")
|
t.Cmd = t.Has("cmd")
|
||||||
t.Arg = t.Has("arg")
|
t.Arg = t.Has("arg")
|
||||||
@@ -141,6 +143,9 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
|
|||||||
if t.PlaceHolder == "" {
|
if t.PlaceHolder == "" {
|
||||||
t.PlaceHolder = strings.ToUpper(dashedString(fv.Type().Name()))
|
t.PlaceHolder = strings.ToUpper(dashedString(fv.Type().Name()))
|
||||||
}
|
}
|
||||||
|
for _, part := range strings.Split(t.Get("enum"), ",") {
|
||||||
|
t.Enum[part] = true
|
||||||
|
}
|
||||||
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user