6408010083
Previously, there was a confusing mix of functionality shared between
the two wherein you would need to use the Kong type for printing errors,
etc. but it did not have access to the context in order to print
context-sensitive usage information. This has been fixed.
Additionally, there are now fuzzy correction suggestions for flags and
commands
Also added a server example which shows how Kong can be used for parsing
in interactive shells. Run with:
$ go run ./_examples/server/*.go
Then interact with:
$ ssh -p 6740 127.0.0.1
201 lines
4.7 KiB
Go
201 lines
4.7 KiB
Go
package kong
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// An Option applies optional changes to the Kong application.
|
|
type Option func(k *Kong) error
|
|
|
|
// Exit overrides the function used to terminate. This is useful for testing or interactive use.
|
|
func Exit(exit func(int)) Option {
|
|
return func(k *Kong) error {
|
|
k.Exit = exit
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// NoDefaultHelp disables the default help flags.
|
|
func NoDefaultHelp() Option {
|
|
return func(k *Kong) error {
|
|
k.noDefaultHelp = true
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Name overrides the application name.
|
|
func Name(name string) Option {
|
|
return func(k *Kong) error {
|
|
k.postBuildOptions = append(k.postBuildOptions, func(k *Kong) error {
|
|
k.Model.Name = name
|
|
return nil
|
|
})
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Description sets the application description.
|
|
func Description(description string) Option {
|
|
return func(k *Kong) error {
|
|
k.postBuildOptions = append(k.postBuildOptions, func(k *Kong) error {
|
|
k.Model.Help = description
|
|
return nil
|
|
})
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// TypeMapper registers a mapper to a type.
|
|
func TypeMapper(typ reflect.Type, mapper Mapper) Option {
|
|
return func(k *Kong) error {
|
|
k.registry.RegisterType(typ, mapper)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// KindMapper registers a mapper to a kind.
|
|
func KindMapper(kind reflect.Kind, mapper Mapper) Option {
|
|
return func(k *Kong) error {
|
|
k.registry.RegisterKind(kind, mapper)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ValueMapper registers a mapper to a field value.
|
|
func ValueMapper(ptr interface{}, mapper Mapper) Option {
|
|
return func(k *Kong) error {
|
|
k.registry.RegisterValue(ptr, mapper)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// NamedMapper registers a mapper to a name.
|
|
func NamedMapper(name string, mapper Mapper) Option {
|
|
return func(k *Kong) error {
|
|
k.registry.RegisterName(name, mapper)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Writers overrides the default writers. Useful for testing or interactive use.
|
|
func Writers(stdout, stderr io.Writer) Option {
|
|
return func(k *Kong) error {
|
|
k.Stdout = stdout
|
|
k.Stderr = stderr
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// HookFunc is a callback tied to a field of the grammar, called before a value is applied.
|
|
//
|
|
// "ctx" is the current parse Context, "path" is the Path entry corresponding to the hooked value.
|
|
type HookFunc func(ctx *Context, path *Path) error
|
|
|
|
// Hook to apply before a command, flag or positional argument is encountered.
|
|
//
|
|
// "ptr" is a pointer to a field of the grammar.
|
|
//
|
|
// Note that the hook will be called once for each time the corresponding node is encountered. This means that if a flag
|
|
// is passed twice, its hook will be called twice.
|
|
func Hook(ptr interface{}, hook HookFunc) Option {
|
|
key := reflect.ValueOf(ptr)
|
|
if key.Kind() != reflect.Ptr {
|
|
panic("expected a pointer")
|
|
}
|
|
return func(k *Kong) error {
|
|
k.before[key] = hook
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Help printer to use.
|
|
func Help(help HelpPrinter) Option {
|
|
return func(k *Kong) error {
|
|
k.help = help
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ConfigureHelp sets the HelpOptions to use for printing help.
|
|
func ConfigureHelp(options HelpOptions) Option {
|
|
return func(k *Kong) error {
|
|
k.helpOptions = options
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// UsageOnError configures Kong to display context-sensitive usage if FatalIfErrorf is called with an error.
|
|
func UsageOnError() Option {
|
|
return func(k *Kong) error {
|
|
k.usageOnError = true
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ClearResolvers clears all existing resolvers.
|
|
func ClearResolvers() Option {
|
|
return func(k *Kong) error {
|
|
k.resolvers = nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Resolver registers flag resolvers.
|
|
func Resolver(resolvers ...ResolverFunc) Option {
|
|
return func(k *Kong) error {
|
|
k.resolvers = append(k.resolvers, resolvers...)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ConfigurationFunc is a function that builds a resolver from a file.
|
|
type ConfigurationFunc func(r io.Reader) (ResolverFunc, error)
|
|
|
|
// Configuration provides Kong with support for loading defaults from a set of configuration files.
|
|
//
|
|
// Paths will be opened in order, and "loader" will be used to provide a ResolverFunc which is registered with Kong.
|
|
//
|
|
// Note: The JSON function is a ConfigurationFunc.
|
|
//
|
|
// ~ expansion will occur on the provided paths.
|
|
func Configuration(loader ConfigurationFunc, paths ...string) Option {
|
|
return func(k *Kong) error {
|
|
for _, path := range paths {
|
|
path = expandPath(path)
|
|
r, err := os.Open(path) // nolint: gas
|
|
if err != nil {
|
|
continue
|
|
}
|
|
resolver, err := loader(r)
|
|
if err == nil {
|
|
k.resolvers = append(k.resolvers, resolver)
|
|
}
|
|
_ = r.Close()
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func expandPath(path string) string {
|
|
if filepath.IsAbs(path) {
|
|
return path
|
|
}
|
|
if strings.HasPrefix(path, "~/") {
|
|
user, err := user.Current()
|
|
if err != nil {
|
|
return path
|
|
}
|
|
return filepath.Join(user.HomeDir, path[2:])
|
|
}
|
|
abspath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return path
|
|
}
|
|
return abspath
|
|
}
|