diff --git a/README.md b/README.md index 1f4ed1e..f39285c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ 1. [Slices](#slices) 1. [Maps](#maps) 1. [Custom named decoders](#custom-named-decoders) -1. [Custom decoders](#custom-decoders) +1. [Custom decoders \(mappers\)](#custom-decoders-mappers) 1. [Supported tags](#supported-tags) 1. [Variable interpolation](#variable-interpolation) 1. [Modifying Kong's behaviour](#modifying-kongs-behaviour) @@ -162,11 +162,13 @@ This has the advantage that it is convenient, but the downside that if you modif A more robust approach is to break each command out into their own structs: 1. Break leaf commands out into separate structs. -2. Attach a `Run(...) error` method to all leaf commands (`Run()` signatures must match). +2. Attach a `Run(...) error` method to all leaf commands. 3. Call `kong.Kong.Parse()` to obtain a `kong.Context`. -4. Call `kong.Context.Run(params...)` to call the selected parsed command. +4. Call `kong.Context.Run(bindings...)` to call the selected parsed command. -Note that `Run()` method arguments may also be provided by the `Bind(...)` option (see below). +In addition to values bound with the `kong.Bind(...)` option, any values +passed through to `kong.Context.Run(...)` are also bindable to the target's +`Run()` arguments. There's a full example emulating part of the Docker CLI [here](https://github.com/alecthomas/kong/tree/master/_examples/docker). @@ -362,7 +364,7 @@ specifies the element type. For maps, the tag has the format `tag:"[]:[]"` where either may be omitted. -## Custom decoders +## Custom decoders (mappers) If a field implements the [MapperValue](https://godoc.org/github.com/alecthomas/kong#MapperValue) interface it will be used to decode arguments into the field. diff --git a/_examples/docker/main.go b/_examples/docker/main.go index e678bf4..0fd01f3 100644 --- a/_examples/docker/main.go +++ b/_examples/docker/main.go @@ -2,24 +2,36 @@ package main import ( + "fmt" + "github.com/alecthomas/kong" ) type Globals struct { - Config string `help:"Location of client config files" default:"~/.docker" type:"path"` - Debug bool `short:"D" help:"Enable debug mode"` - Host []string `short:"H" help:"Daemon socket(s) to connect to"` - LogLevel string `short:"l" help:"Set the logging level (debug|info|warn|error|fatal)" default:"info"` - TLS bool `help:"Use TLS; implied by --tls-verify"` - TLSCACert string `name:"tls-ca-cert" help:"Trust certs signed only by this CA" default:"~/.docker/ca.pem" type:"path"` - TLSCert string `help:"Path to TLS certificate file" default:"~/.docker/cert.pem" type:"path"` - TLSKey string `help:"Path to TLS key file" default:"~/.docker/key.pem" type:"path"` - TLSVerify bool `help:"Use TLS and verify the remote"` + Config string `help:"Location of client config files" default:"~/.docker" type:"path"` + Debug bool `short:"D" help:"Enable debug mode"` + Host []string `short:"H" help:"Daemon socket(s) to connect to"` + LogLevel string `short:"l" help:"Set the logging level (debug|info|warn|error|fatal)" default:"info"` + TLS bool `help:"Use TLS; implied by --tls-verify"` + TLSCACert string `name:"tls-ca-cert" help:"Trust certs signed only by this CA" default:"~/.docker/ca.pem" type:"path"` + TLSCert string `help:"Path to TLS certificate file" default:"~/.docker/cert.pem" type:"path"` + TLSKey string `help:"Path to TLS key file" default:"~/.docker/key.pem" type:"path"` + TLSVerify bool `help:"Use TLS and verify the remote"` + Version VersionFlag `name:"version" help:"Print version information and quit"` +} + +type VersionFlag string + +func (v VersionFlag) Decode(ctx *kong.DecodeContext) error { return nil } +func (v VersionFlag) IsBool() bool { return true } +func (v VersionFlag) BeforeHook(app *kong.Kong, vars kong.Vars) error { + fmt.Println(vars["version"]) + app.Exit(0) + return nil } type CLI struct { Globals - VersionFlag bool `name:"version" help:"Print version information and quit"` Attach AttachCmd `cmd help:"Attach local standard input, output, and error streams to a running container"` Build BuildCmd `cmd help:"Build an image from a Dockerfile"` @@ -65,7 +77,12 @@ type CLI struct { } func main() { - cli := CLI{} + cli := CLI{ + Globals: Globals{ + Version: VersionFlag("0.1.1"), + }, + } + ctx := kong.Parse(&cli, kong.Name("docker"), kong.Description("A self-sufficient runtime for containers"), @@ -73,11 +90,9 @@ func main() { kong.ConfigureHelp(kong.HelpOptions{ Compact: true, }), - // - kong.Hook(&cli.VersionFlag, func(ctx *kong.Context, path *kong.Path) error { - ctx.Printf("1.0.0").Exit(0) - return nil - })) + kong.Vars{ + "version": "0.0.1", + }) err := ctx.Run(&cli.Globals) ctx.FatalIfErrorf(err) } diff --git a/context.go b/context.go index 76e2c64..0a43daf 100644 --- a/context.go +++ b/context.go @@ -470,10 +470,10 @@ func (c *Context) parseFlag(flags []*Flag, match string) (err error) { return findPotentialCandidates(match, candidates, "unknown flag %s", match) } -// Run executes the corresponding Run(params...) method on the target command selected by the parsed args. +// Run executes the Run() method on the selected command, which must exist. // -// The target Run() method must exist and have the type signature "Run(params...) error". -func (c *Context) Run(params ...interface{}) (err error) { +// Any passed values will be bindable to arguments of the target Run() method. +func (c *Context) Run(bindings ...interface{}) (err error) { defer catch(&err) node := c.Selected() if node == nil { @@ -487,7 +487,7 @@ func (c *Context) Run(params ...interface{}) (err error) { if err != nil { return err } - binds := c.Kong.bindings.clone().add(params...).add(c) + binds := c.Kong.bindings.clone().add(bindings...).add(c) return callMethod("Run", node.Target, method, binds) } diff --git a/kong.go b/kong.go index 8e3fdce..fb0bbf6 100644 --- a/kong.go +++ b/kong.go @@ -51,7 +51,7 @@ type Kong struct { help HelpPrinter helpOptions HelpOptions helpFlag *Flag - vars map[string]string + vars Vars // Set temporarily by Options. These are applied after build(). postBuildOptions []Option @@ -67,7 +67,7 @@ func New(grammar interface{}, options ...Option) (*Kong, error) { Stderr: os.Stderr, registry: NewRegistry().RegisterDefaults(), resolvers: []ResolverFunc{Envars()}, - vars: map[string]string{}, + vars: Vars{}, bindings: bindings{}, } @@ -102,6 +102,8 @@ func New(grammar interface{}, options ...Option) (*Kong, error) { return nil, err } + k.bindings.add(k.vars) + return k, nil }