diff --git a/apply.go b/apply.go index ad6390b..9e3b037 100644 --- a/apply.go +++ b/apply.go @@ -45,17 +45,17 @@ func bindOptions(receiver reflect.Value, shortForms []string, o []option) bool { } func createStructArg(t reflect.Type, shortForms []string, c config, e env, o []option) (reflect.Value, bool) { - r := allocate(t) + r := allocate(reflect.PointerTo(t)) hasConfigMatches := bindKeyVals(r, c.values) hasEnvMatches := bindKeyVals(r, e.values) hasOptionMatches := bindOptions(r, shortForms, o) - return r, hasConfigMatches || hasEnvMatches || hasOptionMatches + return r.Elem(), hasConfigMatches || hasEnvMatches || hasOptionMatches } func createPositional(t reflect.Type, v string) reflect.Value { - r := allocate(t) + r := allocate(reflect.PointerTo(t)) bindScalar(r, v) - return r + return r.Elem() } func createArgs(stdin io.Reader, stdout io.Writer, t reflect.Type, shortForms []string, c config, e env, cl commandLine) []reflect.Value { diff --git a/go.mod b/go.mod index 9a144a8..f42ca04 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,11 @@ module code.squareroundforest.org/arpio/wand go 1.25.0 require ( + code.squareroundforest.org/arpio/bind v0.0.0-20250901011104-bcadfd8b71fc code.squareroundforest.org/arpio/docreflect v0.0.0-20250831183400-d26ecc663a30 code.squareroundforest.org/arpio/notation v0.0.0-20250826181910-5140794b16b2 code.squareroundforest.org/arpio/treerack v0.0.0-20250820014405-1d956dcc6610 github.com/iancoleman/strcase v0.3.0 ) -require ( - code.squareroundforest.org/arpio/bind v0.0.0-20250831235903-9a6db08a25d0 // indirect - golang.org/x/mod v0.27.0 // indirect -) +require golang.org/x/mod v0.27.0 // indirect diff --git a/go.sum b/go.sum index 9465a5c..b206c9f 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,11 @@ -code.squareroundforest.org/arpio/bind v0.0.0-20250831151900-af0bbca22e99 h1:1p3wtLY/USO+niU9d7yNqk/sbRluZ3/Xoo7L7gEF+ew= -code.squareroundforest.org/arpio/bind v0.0.0-20250831151900-af0bbca22e99/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI= -code.squareroundforest.org/arpio/bind v0.0.0-20250831235903-9a6db08a25d0 h1:dpekVQNpmH39MDNig+hA2IFF6J7TXPQNc8hTuENAn7A= -code.squareroundforest.org/arpio/bind v0.0.0-20250831235903-9a6db08a25d0/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI= -code.squareroundforest.org/arpio/docreflect v0.0.0-20250823192303-755a103f3788 h1:jJoq0FdasFFDX1uJowXD8iyX/2G3gjwxtVEDyXtfeuw= -code.squareroundforest.org/arpio/docreflect v0.0.0-20250823192303-755a103f3788/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA= -code.squareroundforest.org/arpio/docreflect v0.0.0-20250826190210-b092c9cb4c2e h1:FJ9rP44KGmiDZb+gGRH0ty209R05x2vR3wNPUG3HB4I= -code.squareroundforest.org/arpio/docreflect v0.0.0-20250826190210-b092c9cb4c2e/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA= -code.squareroundforest.org/arpio/docreflect v0.0.0-20250826190339-b00034d8ca42 h1:w9JPDwsnPvDC70helP9RNL2lnRj+ab2eVgv9fK56kIg= -code.squareroundforest.org/arpio/docreflect v0.0.0-20250826190339-b00034d8ca42/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA= +code.squareroundforest.org/arpio/bind v0.0.0-20250901011104-bcadfd8b71fc h1:nu5YXVLDrRzN9Ea5agXmhxFILyVAPyoED25ksTYC9ws= +code.squareroundforest.org/arpio/bind v0.0.0-20250901011104-bcadfd8b71fc/go.mod h1:tTCmCwFABKNm3PO0Dclsp4zWhNQFTfg9+uSrgoarZFI= code.squareroundforest.org/arpio/docreflect v0.0.0-20250831183400-d26ecc663a30 h1:QUCgxUEA5/ng7GwRnzb/WezmFQXSHXl48GdLJc0KC5k= code.squareroundforest.org/arpio/docreflect v0.0.0-20250831183400-d26ecc663a30/go.mod h1:/3xQI36oJG8qLBxT2fSS61P5/+i1T64fTX9GHRh8XhA= -code.squareroundforest.org/arpio/notation v0.0.0-20241225183158-af3bd591a174 h1:DKMSagVY3uyRhJ4ohiwQzNnR6CWdVKLkg97A8eQGxQU= -code.squareroundforest.org/arpio/notation v0.0.0-20241225183158-af3bd591a174/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY= code.squareroundforest.org/arpio/notation v0.0.0-20250826181910-5140794b16b2 h1:S4mjQHL70CuzFg1AGkr0o0d+4M+ZWM0sbnlYq6f0b3I= code.squareroundforest.org/arpio/notation v0.0.0-20250826181910-5140794b16b2/go.mod h1:ait4Fvg9o0+bq5hlxi9dAcPL5a+/sr33qsZPNpToMLY= code.squareroundforest.org/arpio/treerack v0.0.0-20250820014405-1d956dcc6610 h1:I0jebdyQQfqJcwq2lT/TkUPBU8secHa5xZ+VzOdYVsw= code.squareroundforest.org/arpio/treerack v0.0.0-20250820014405-1d956dcc6610/go.mod h1:9XhPcVt1Y1M609z02lHvEcp00dwPD9NUCoVxS2TpcH8= -github.com/aryszka/notation v0.0.0-20230129164653-172017dde5e4 h1:JzqT9RArcw2sD4QPAyTss/sHaCZvCv+91DDJPZOrShw= -github.com/aryszka/notation v0.0.0-20230129164653-172017dde5e4/go.mod h1:myJFmFAZ/75y5xdA1jjpc4ItNJwdRqaL+TQhIvDU8Vk= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= diff --git a/reflect.go b/reflect.go index d4a4e77..44c0612 100644 --- a/reflect.go +++ b/reflect.go @@ -186,13 +186,13 @@ func structParameters(f any) []reflect.Type { func structFields(s reflect.Type) []bind.Field { s = unpackType(s) - v := reflect.Zero(s) - return bind.FieldValues(v) + v := allocate(reflect.PointerTo(s)) + return bind.FieldValues(v.Interface()) } func fields(f any) []bind.Field { var fields []bind.Field - s := structParameters(fields) + s := structParameters(f) for _, si := range s { fields = append(fields, structFields(si)...) } @@ -201,7 +201,7 @@ func fields(f any) []bind.Field { } func mapFields(f any) map[string][]bind.Field { - fields := fields(fields) + fields := fields(f) m := make(map[string][]bind.Field) for _, fi := range fields { m[fi.Name()] = append(m[fi.Name()], fi) @@ -260,6 +260,7 @@ func bindable(t reflect.Type) bool { return false } + t = unpackType(t) if isTime(t) { return true } @@ -336,7 +337,7 @@ func canScanType(t reflect.Type, v any) bool { return false } - r := reflect.Zero(t) + r := allocate(reflect.PointerTo(t)) return bind.BindScalar(r.Interface(), v) } @@ -354,7 +355,8 @@ func allocate(t reflect.Type) reflect.Value { s.Index(0).Set(v) return s default: - return reflect.Zero(t) + p := reflect.New(t) + return p.Elem() } } @@ -364,11 +366,13 @@ func bindScalar(receiver reflect.Value, value any) { func bindFields(receiver reflect.Value, values map[string][]any) []string { var f []bind.Field - for name, value := range values { - f = append(f, bind.NamedValue(name, value)) + for name, vals := range values { + for _, v := range vals { + f = append(f, bind.NamedValue(name, v)) + } } - unmapped := bind.BindFields(receiver, f...) + unmapped := bind.BindFields(receiver.Interface(), f...) var names []string for _, um := range unmapped { diff --git a/reflect_test.go b/reflect_test.go index febbef3..5690822 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -78,7 +78,7 @@ func TestReflect(t *testing.T) { t.Run("any", testExec(testCase{impl: f, command: "foo bar"}, "", "bar")) type i interface{ Foo() } g := func(a i) any { return a } - t.Run("unscannable", testExec(testCase{impl: g, command: "foo bar"}, "non-empty interface", "")) + t.Run("unscannable", testExec(testCase{impl: g, command: "foo bar"}, "unsupported parameter type", "")) }) }) }