package wand import ( "io" "reflect" ) 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) } } u := bindFields(receiver, v) return len(v) > 0 && len(u) < len(v) } func bindOptions(receiver reflect.Value, shortForms []string, o []option) bool { ms := shortFormsToLong(shortForms) v := make(map[string][]any) for _, oi := range o { n := oi.name if oi.shortForm { n = ms[n] } var val any if oi.value.isBool { val = oi.value.boolean } else { val = oi.value.str } v[n] = append(v[n], val) } u := bindFields(receiver, v) return len(v) > 0 && len(u) < len(v) } func createStructArg(t reflect.Type, shortForms []string, c config, e env, o []option) (reflect.Value, bool) { r := allocate(reflect.PointerTo(t)) hasConfigMatches := bindKeyVals(r, c.values) hasEnvMatches := bindKeyVals(r, e.values) hasOptionMatches := bindOptions(r, shortForms, o) return r.Elem(), hasConfigMatches || hasEnvMatches || hasOptionMatches } func createPositional(t reflect.Type, v string) reflect.Value { r := allocate(reflect.PointerTo(t)) bindScalar(r, v) return r.Elem() } func createArgs(stdin io.Reader, stdout io.Writer, t reflect.Type, shortForms []string, c config, e env, cl commandLine) []reflect.Value { 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: args = append(args, reflect.ValueOf(stdin)) case iow: args = append(args, reflect.ValueOf(stdout)) case structure && variadic: if arg, ok := createStructArg(ti.Elem(), shortForms, c, e, cl.options); ok { args = append(args, arg) } case structure: arg, _ := createStructArg(ti, shortForms, c, e, cl.options) 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 isErrorType := t.Out(last) == reflect.TypeFor[error]() 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 } func apply(stdin io.Reader, stdout io.Writer, cmd Cmd, c config, e env, cl commandLine) ([]any, error) { v := reflect.ValueOf(cmd.impl) v = unpackValue(v) t := v.Type() args := createArgs(stdin, stdout, t, cmd.shortForms, c, e, cl) out := v.Call(args) return processResults(t, out) }