feat: Embed() option and Context.Call()

The former allows arbitrary structs to be embedded in the root of the
CLI, with optional tags.

The latter allows an arbitrary function to be called using Kong's
binding functionality.
This commit is contained in:
Alec Thomas
2022-11-22 23:30:10 +11:00
parent d974d7270a
commit bf0cbf5d7c
7 changed files with 126 additions and 29 deletions
+40 -3
View File
@@ -74,11 +74,14 @@ func getMethod(value reflect.Value, name string) reflect.Value {
return method
}
func callMethod(name string, v, f reflect.Value, bindings bindings) error {
func callFunction(f reflect.Value, bindings bindings) error {
if f.Kind() != reflect.Func {
return fmt.Errorf("expected function, got %s", f.Type())
}
in := []reflect.Value{}
t := f.Type()
if t.NumOut() != 1 || !t.Out(0).Implements(callbackReturnSignature) {
return fmt.Errorf("return value of %T.%s() must implement \"error\"", v.Type(), name)
return fmt.Errorf("return value of %s must implement \"error\"", t)
}
for i := 0; i < t.NumIn(); i++ {
pt := t.In(i)
@@ -89,7 +92,7 @@ func callMethod(name string, v, f reflect.Value, bindings bindings) error {
}
in = append(in, argv)
} else {
return fmt.Errorf("couldn't find binding of type %s for parameter %d of %s.%s(), use kong.Bind(%s)", pt, i, v.Type(), name, pt)
return fmt.Errorf("couldn't find binding of type %s for parameter %d of %s(), use kong.Bind(%s)", pt, i, t, pt)
}
}
out := f.Call(in)
@@ -98,3 +101,37 @@ func callMethod(name string, v, f reflect.Value, bindings bindings) error {
}
return out[0].Interface().(error) // nolint
}
func callAnyFunction(f reflect.Value, bindings bindings) (out []any, err error) {
if f.Kind() != reflect.Func {
return nil, fmt.Errorf("expected function, got %s", f.Type())
}
in := []reflect.Value{}
t := f.Type()
for i := 0; i < t.NumIn(); i++ {
pt := t.In(i)
if argf, ok := bindings[pt]; ok {
argv, err := argf()
if err != nil {
return nil, err
}
in = append(in, argv)
} else {
return nil, fmt.Errorf("couldn't find binding of type %s for parameter %d of %s(), use kong.Bind(%s)", pt, i, t, pt)
}
}
outv := f.Call(in)
out = make([]any, len(outv))
for i, v := range outv {
out[i] = v.Interface()
}
return out, nil
}
func callMethod(name string, v, f reflect.Value, bindings bindings) error {
err := callFunction(f, bindings)
if err != nil {
return fmt.Errorf("%s.%s(): %w", v.Type(), name, err)
}
return nil
}