From a6ce719390d3ddafc7c3697367f7599f07cdd653 Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Sun, 13 Jan 2019 21:32:44 +1100 Subject: [PATCH] Call Run(...) for all parent nodes. Instead of just the leaf command. Fixes #30. --- README.md | 4 ++++ context.go | 30 +++++++++++++++++++++++++----- kong_test.go | 23 +++++++++++++++++++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b5ba8a9..68a8afb 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,10 @@ A more robust approach is to break each command out into their own structs: 3. Call `kong.Kong.Parse()` to obtain a `kong.Context`. 4. Call `kong.Context.Run(bindings...)` to call the selected parsed command. +Once a command node is selected by Kong it will search from that node back to the root. Each +encountered command node with a `Run(...) error` will be called in reverse order. This allows +sub-trees to be re-used fairly conveniently. + 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. diff --git a/context.go b/context.go index 2b6dbbb..ab7b397 100644 --- a/context.go +++ b/context.go @@ -88,6 +88,10 @@ func (c *Context) Bind(args ...interface{}) { } // BindTo adds a binding to the Context. +// +// This will typically have to be called like so: +// +// BindTo(impl, (*MyInterface)(nil)) func (c *Context) BindTo(impl, iface interface{}) { c.bindings[reflect.TypeOf(iface).Elem()] = reflect.ValueOf(impl) } @@ -493,16 +497,32 @@ func (c *Context) Run(bindings ...interface{}) (err error) { if node == nil { return fmt.Errorf("no command selected") } - method := getMethod(node.Target, "Run") - if !method.IsValid() { - return fmt.Errorf("no Run() method on %s", node.Target) + type targetMethod struct { + node *Node + method reflect.Value + } + methods := []targetMethod{} + for i := 0; node != nil; i, node = i+1, node.Parent { + method := getMethod(node.Target, "Run") + if method.IsValid() { + methods = append(methods, targetMethod{node, method}) + } + } + if len(methods) == 0 { + return fmt.Errorf("no Run() method found in hierarchy of %s", c.Selected().Summary()) } _, err = c.Apply() if err != nil { return err } - binds := c.Kong.bindings.clone().add(bindings...).add(c).merge(c.bindings) - return callMethod("Run", node.Target, method, binds) + + for _, method := range methods { + binds := c.Kong.bindings.clone().add(bindings...).add(c).merge(c.bindings) + if err = callMethod("Run", method.node.Target, method.method, binds); err != nil { + return err + } + } + return nil } // PrintUsage to Kong's stdout. diff --git a/kong_test.go b/kong_test.go index 36bb11c..a4f5a25 100644 --- a/kong_test.go +++ b/kong_test.go @@ -513,9 +513,22 @@ func (c *cmdWithRun) Run(key string) error { return nil } +type parentCmdWithRun struct { + Flag string + SubCommand struct { + Arg string `arg:""` + } `cmd:""` +} + +func (p *parentCmdWithRun) Run(key string) error { + p.SubCommand.Arg += key + return nil +} + type grammarWithRun struct { - One cmdWithRun `cmd:""` - Two cmdWithRun `cmd:""` + One cmdWithRun `cmd:""` + Two cmdWithRun `cmd:""` + Three parentCmdWithRun `cmd:""` } func TestRun(t *testing.T) { @@ -532,6 +545,12 @@ func TestRun(t *testing.T) { require.NoError(t, err) err = ctx.Run("ERROR") require.Error(t, err) + + ctx, err = p.Parse([]string{"three", "sub-command", "arg"}) + require.NoError(t, err) + err = ctx.Run("ping") + require.NoError(t, err) + require.Equal(t, "argping", cli.Three.SubCommand.Arg) } func TestInterpolationIntoModel(t *testing.T) {