feat(binding): add support for slices with BindUnmarshaler elements (#4312)

Fixes issue where slice/array elements implementing BindUnmarshaler
interface were not properly handled. The setArray function now checks
if elements implement custom unmarshaling before falling back to
default type conversion.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sidi Chang 2025-07-26 23:38:01 +09:00
parent dab5944a7b
commit 906c864012
2 changed files with 72 additions and 3 deletions

View File

@ -449,10 +449,17 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
for i, s := range vals {
err := setWithProperType(s, value.Index(i), field)
elementValue := value.Index(i)
if isSet, err := trySetCustom(s, elementValue); isSet {
if err != nil {
return err
}
} else {
err := setWithProperType(s, elementValue, field)
if err != nil {
return err
}
}
}
return nil
}

View File

@ -635,3 +635,65 @@ func TestMappingCustomArrayForm(t *testing.T) {
expected, _ := convertTo(val)
assert.Equal(t, expected, s.FileData)
}
func TestMappingSliceOfCustomBindUnmarshalerElementsUri(t *testing.T) {
var s struct {
Items []customUnmarshalParamType `uri:"items"`
}
err := mappingByPtr(&s, formSource{"items": {"http:path1:name1", "https:path2:name2"}}, "uri")
require.NoError(t, err)
assert.Len(t, s.Items, 2)
assert.Equal(t, "http", s.Items[0].Protocol)
assert.Equal(t, "path1", s.Items[0].Path)
assert.Equal(t, "name1", s.Items[0].Name)
assert.Equal(t, "https", s.Items[1].Protocol)
assert.Equal(t, "path2", s.Items[1].Path)
assert.Equal(t, "name2", s.Items[1].Name)
}
func TestMappingSliceOfCustomBindUnmarshalerElementsForm(t *testing.T) {
var s struct {
Items []customUnmarshalParamType `form:"items"`
}
err := mappingByPtr(&s, formSource{"items": {"tcp:socket1:server", "udp:socket2:client"}}, "form")
require.NoError(t, err)
assert.Len(t, s.Items, 2)
assert.Equal(t, "tcp", s.Items[0].Protocol)
assert.Equal(t, "socket1", s.Items[0].Path)
assert.Equal(t, "server", s.Items[0].Name)
assert.Equal(t, "udp", s.Items[1].Protocol)
assert.Equal(t, "socket2", s.Items[1].Path)
assert.Equal(t, "client", s.Items[1].Name)
}
func TestMappingArrayOfCustomBindUnmarshalerElementsUri(t *testing.T) {
var s struct {
Items [2]customUnmarshalParamType `uri:"items"`
}
err := mappingByPtr(&s, formSource{"items": {"grpc:service1:auth", "rest:service2:data"}}, "uri")
require.NoError(t, err)
assert.Equal(t, "grpc", s.Items[0].Protocol)
assert.Equal(t, "service1", s.Items[0].Path)
assert.Equal(t, "auth", s.Items[0].Name)
assert.Equal(t, "rest", s.Items[1].Protocol)
assert.Equal(t, "service2", s.Items[1].Path)
assert.Equal(t, "data", s.Items[1].Name)
}
func TestMappingArrayOfCustomBindUnmarshalerElementsForm(t *testing.T) {
var s struct {
Items [2]customUnmarshalParamType `form:"items"`
}
err := mappingByPtr(&s, formSource{"items": {"ws:chat:room1", "wss:chat:room2"}}, "form")
require.NoError(t, err)
assert.Equal(t, "ws", s.Items[0].Protocol)
assert.Equal(t, "chat", s.Items[0].Path)
assert.Equal(t, "room1", s.Items[0].Name)
assert.Equal(t, "wss", s.Items[1].Protocol)
assert.Equal(t, "chat", s.Items[1].Path)
assert.Equal(t, "room2", s.Items[1].Name)
}