Call Run(...) for all parent nodes.

Instead of just the leaf command. Fixes #30.
This commit is contained in:
Alec Thomas
2019-01-13 21:32:44 +11:00
parent 443709c0bb
commit a6ce719390
3 changed files with 50 additions and 7 deletions
+4
View File
@@ -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`. 3. Call `kong.Kong.Parse()` to obtain a `kong.Context`.
4. Call `kong.Context.Run(bindings...)` to call the selected parsed command. 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 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 passed through to `kong.Context.Run(...)` are also bindable to the target's
`Run()` arguments. `Run()` arguments.
+25 -5
View File
@@ -88,6 +88,10 @@ func (c *Context) Bind(args ...interface{}) {
} }
// BindTo adds a binding to the Context. // 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{}) { func (c *Context) BindTo(impl, iface interface{}) {
c.bindings[reflect.TypeOf(iface).Elem()] = reflect.ValueOf(impl) c.bindings[reflect.TypeOf(iface).Elem()] = reflect.ValueOf(impl)
} }
@@ -493,16 +497,32 @@ func (c *Context) Run(bindings ...interface{}) (err error) {
if node == nil { if node == nil {
return fmt.Errorf("no command selected") return fmt.Errorf("no command selected")
} }
method := getMethod(node.Target, "Run") type targetMethod struct {
if !method.IsValid() { node *Node
return fmt.Errorf("no Run() method on %s", node.Target) 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() _, err = c.Apply()
if err != nil { if err != nil {
return err 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. // PrintUsage to Kong's stdout.
+21 -2
View File
@@ -513,9 +513,22 @@ func (c *cmdWithRun) Run(key string) error {
return nil 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 { type grammarWithRun struct {
One cmdWithRun `cmd:""` One cmdWithRun `cmd:""`
Two cmdWithRun `cmd:""` Two cmdWithRun `cmd:""`
Three parentCmdWithRun `cmd:""`
} }
func TestRun(t *testing.T) { func TestRun(t *testing.T) {
@@ -532,6 +545,12 @@ func TestRun(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
err = ctx.Run("ERROR") err = ctx.Run("ERROR")
require.Error(t, err) 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) { func TestInterpolationIntoModel(t *testing.T) {