2025-08-18 14:24:31 +02:00
|
|
|
package wand
|
|
|
|
|
|
|
|
|
|
import (
|
2025-08-24 01:45:25 +02:00
|
|
|
"io"
|
2025-08-18 14:24:31 +02:00
|
|
|
"reflect"
|
|
|
|
|
)
|
|
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
func bindKeyVals(receiver reflect.Value, keyVals map[string][]string) bool {
|
|
|
|
|
v := make(map[string][]any)
|
|
|
|
|
for name, values := range keyVals {
|
|
|
|
|
for _, vi := range values {
|
|
|
|
|
v[name] = append(v[name], vi)
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
u := bindFields(receiver, v)
|
|
|
|
|
return len(v) > 0 && len(u) < len(v)
|
|
|
|
|
}
|
2025-08-18 14:24:31 +02:00
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
func bindOptions(receiver reflect.Value, shortForms []string, o []option) bool {
|
2025-08-18 14:24:31 +02:00
|
|
|
ms := make(map[string]string)
|
|
|
|
|
for i := 0; i < len(shortForms); i += 2 {
|
2025-09-01 02:07:48 +02:00
|
|
|
ms[shortForms[i]] = shortForms[i + 1]
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
v := make(map[string][]any)
|
2025-08-18 14:24:31 +02:00
|
|
|
for _, oi := range o {
|
|
|
|
|
n := oi.name
|
2025-09-01 02:07:48 +02:00
|
|
|
if oi.shortForm {
|
|
|
|
|
n = ms[n]
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
2025-08-24 01:45:25 +02:00
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
var val any
|
|
|
|
|
if oi.value.isBool {
|
|
|
|
|
val = oi.value.boolean
|
|
|
|
|
} else {
|
|
|
|
|
val = oi.value.str
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
v[n] = append(v[n], val)
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
u := bindFields(receiver, v)
|
|
|
|
|
return len(v) > 0 && len(u) < len(v)
|
|
|
|
|
}
|
2025-08-18 14:24:31 +02:00
|
|
|
|
2025-09-01 02:07:48 +02:00
|
|
|
func createStructArg(t reflect.Type, shortForms []string, c config, e env, o []option) (reflect.Value, bool) {
|
2025-09-01 03:25:18 +02:00
|
|
|
r := allocate(reflect.PointerTo(t))
|
2025-09-01 02:07:48 +02:00
|
|
|
hasConfigMatches := bindKeyVals(r, c.values)
|
|
|
|
|
hasEnvMatches := bindKeyVals(r, e.values)
|
|
|
|
|
hasOptionMatches := bindOptions(r, shortForms, o)
|
2025-09-01 03:25:18 +02:00
|
|
|
return r.Elem(), hasConfigMatches || hasEnvMatches || hasOptionMatches
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func createPositional(t reflect.Type, v string) reflect.Value {
|
2025-09-01 03:25:18 +02:00
|
|
|
r := allocate(reflect.PointerTo(t))
|
2025-09-01 02:07:48 +02:00
|
|
|
bindScalar(r, v)
|
2025-09-01 03:25:18 +02:00
|
|
|
return r.Elem()
|
2025-08-18 14:24:31 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-24 01:45:25 +02:00
|
|
|
func createArgs(stdin io.Reader, stdout io.Writer, t reflect.Type, shortForms []string, c config, e env, cl commandLine) []reflect.Value {
|
2025-08-18 14:24:31 +02:00
|
|
|
var args []reflect.Value
|
|
|
|
|
positional := cl.positional
|
|
|
|
|
for i := 0; i < t.NumIn(); i++ {
|
|
|
|
|
ti := t.In(i)
|
|
|
|
|
structure := isStruct(ti)
|
|
|
|
|
variadic := t.IsVariadic() && i == t.NumIn()-1
|
|
|
|
|
ior := isReader(ti)
|
|
|
|
|
iow := isWriter(ti)
|
|
|
|
|
switch {
|
|
|
|
|
case ior:
|
2025-08-24 01:45:25 +02:00
|
|
|
args = append(args, reflect.ValueOf(stdin))
|
2025-08-18 14:24:31 +02:00
|
|
|
case iow:
|
2025-08-24 01:45:25 +02:00
|
|
|
args = append(args, reflect.ValueOf(stdout))
|
2025-08-18 14:24:31 +02:00
|
|
|
case structure && variadic:
|
2025-08-24 01:45:25 +02:00
|
|
|
if arg, ok := createStructArg(ti, shortForms, c, e, cl.options); ok {
|
2025-08-18 14:24:31 +02:00
|
|
|
args = append(args, arg)
|
|
|
|
|
}
|
|
|
|
|
case structure:
|
2025-08-24 01:45:25 +02:00
|
|
|
arg, _ := createStructArg(ti, shortForms, c, e, cl.options)
|
2025-08-18 14:24:31 +02:00
|
|
|
args = append(args, arg)
|
|
|
|
|
case variadic:
|
|
|
|
|
for _, p := range positional {
|
|
|
|
|
args = append(args, createPositional(ti.Elem(), p))
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
var p string
|
|
|
|
|
p, positional = positional[0], positional[1:]
|
|
|
|
|
args = append(args, createPositional(ti, p))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return args
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func processResults(t reflect.Type, out []reflect.Value) ([]any, error) {
|
|
|
|
|
if len(out) == 0 {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
last := len(out) - 1
|
2025-08-26 03:21:35 +02:00
|
|
|
isErrorType := t.Out(last) == reflect.TypeFor[error]()
|
2025-08-18 14:24:31 +02:00
|
|
|
if isErrorType && !out[last].IsZero() {
|
|
|
|
|
err = out[last].Interface().(error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isErrorType {
|
|
|
|
|
out = out[:last]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var values []any
|
|
|
|
|
for _, o := range out {
|
|
|
|
|
values = append(values, o.Interface())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 01:45:25 +02:00
|
|
|
func apply(stdin io.Reader, stdout io.Writer, cmd Cmd, c config, e env, cl commandLine) ([]any, error) {
|
2025-08-18 14:24:31 +02:00
|
|
|
v := reflect.ValueOf(cmd.impl)
|
2025-09-01 02:07:48 +02:00
|
|
|
v = unpackValue(v)
|
2025-08-18 14:24:31 +02:00
|
|
|
t := v.Type()
|
2025-08-24 01:45:25 +02:00
|
|
|
args := createArgs(stdin, stdout, t, cmd.shortForms, c, e, cl)
|
2025-08-18 14:24:31 +02:00
|
|
|
out := v.Call(args)
|
|
|
|
|
return processResults(t, out)
|
|
|
|
|
}
|