Skip to content

Commit

Permalink
fix(binding): form binding error on fields of type any
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaotushaoxia committed Jun 21, 2024
1 parent 9c081de commit 7f4c8c3
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 28 deletions.
48 changes: 20 additions & 28 deletions binding/form_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,45 +187,34 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
if !ok && !opt.isDefaultExists {
return false, nil
}
if !ok || (ok && len(vs) == 0) { // (ok && len(vs) == 0) should not happen, add for sense of security
vs = []string{opt.defaultValue}
}

if setCustomOk, setCustomErr := trySetCustom(vs[0], value); setCustomOk {
return true, setCustomErr
}

switch value.Kind() {
case reflect.Slice:
if !ok {
vs = []string{opt.defaultValue}
}

if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
}

return true, setSlice(vs, value, field)
case reflect.Array:
if !ok {
vs = []string{opt.defaultValue}
}

if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
}

if len(vs) != value.Len() {
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
}

return true, setArray(vs, value, field)
default:
var val string
if !ok {
val = opt.defaultValue
}

if len(vs) > 0 {
val = vs[0]
}
if ok, err := trySetCustom(val, value); ok {
return ok, err
switch value.Kind() {
case reflect.Interface: // if field is any, we can set vs into it directly
if len(vs) == 1 {
value.Set(reflect.ValueOf(vs[0]))
} else {
value.Set(reflect.ValueOf(vs))
}
return true, nil
default:
return true, setWithProperType(vs[0], value, field)
}
return true, setWithProperType(val, value, field)
}
}

Expand Down Expand Up @@ -278,6 +267,9 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
value.Set(reflect.New(value.Type().Elem()))
}
return setWithProperType(val, value.Elem(), field)
case reflect.Interface:
value.Set(reflect.ValueOf(val))
return nil
default:
return errUnknownType
}
Expand Down
85 changes: 85 additions & 0 deletions binding/form_mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,91 @@ func TestMappingTimeDuration(t *testing.T) {
assert.Error(t, err)
}

func TestMappingAny(t *testing.T) {
type sT struct {
Value any
PValue *any
PPValue **any
DV any `form:"dv,default=aa"`
DV2 any `form:"dv2,default=aa2"`
DV3 *any `form:"dv3,default=aaa3"`
}

noNil := func(st *sT) bool {
if st.PValue == nil || st.PPValue == nil || *(st.PPValue) == nil || st.DV3 == nil {
return false
}
return true
}

var s sT
// ok
err := mappingByPtr(&s, formSource{"Value": {"1"}, "PValue": {"p1"}, "PPValue": {"pp1"}, "dv2": {"aaa2"}}, "form")
assert.NoError(t, err)
assert.True(t, noNil(&s))
assert.Equal(t, "1", s.Value)
assert.Equal(t, "p1", *(s.PValue))
assert.Equal(t, "pp1", *(*(s.PPValue)))
assert.Equal(t, "aa", s.DV)
assert.Equal(t, "aaa2", s.DV2)
assert.Equal(t, "aaa3", *(s.DV3))

var s2 sT
// ok
err = mappingByPtr(&s2, formSource{"Value": {"1", "a2"}, "PValue": {"p1", "2.0"}, "PPValue": {"pp1", "2.00"}, "dv3": {"3", "33"}}, "form")
assert.NoError(t, err)
assert.True(t, noNil(&s2))
assert.Equal(t, []string{"1", "a2"}, s2.Value)
assert.Equal(t, []string{"p1", "2.0"}, *(s2.PValue))
assert.Equal(t, []string{"pp1", "2.00"}, *(*(s2.PPValue)))
assert.Equal(t, []string{"3", "33"}, *(s2.DV3))
}

func TestMappingSliceArrayAny(t *testing.T) {
var s struct {
Values []any
PValues *[]any
PPValues **[]any
AValues [2]any
PAValues *[2]any
PPAValues **[2]any
}

noNil := func() bool {
if s.PValues == nil || s.PPValues == nil || *(s.PPValues) == nil {
return false
}
if s.PAValues == nil || s.PPAValues == nil || *(s.PPAValues) == nil {
return false
}
return true
}

// ok
err := mappingByPtr(&s,
formSource{
"Values": {"1"}, "PValues": {"p1"}, "PPValues": {"pp1"},
"AValues": {"a1", "a2"}, "PAValues": {"pa1", "pa2"}, "PPAValues": {"ppa1", "ppa2"},
}, "form")
assert.NoError(t, err)
assert.True(t, noNil())
assert.Equal(t, []any{"1"}, s.Values)
assert.Equal(t, []any{"p1"}, *(s.PValues))
assert.Equal(t, []any{"pp1"}, *(*(s.PPValues)))

assert.Equal(t, [2]any{"a1", "a2"}, s.AValues)
assert.Equal(t, [2]any{"pa1", "pa2"}, *(s.PAValues))
assert.Equal(t, [2]any{"ppa1", "ppa2"}, *(*(s.PPAValues)))

// error - not enough vals
err = mappingByPtr(&s,
formSource{
"Values": {"1"}, "PValues": {"p1"}, "PPValues": {"pp1"},
"AValues": {"a1", "a2"}, "PAValues": {"pa1"}, "PPAValues": {"ppa1", "ppa2"},
}, "form")
assert.Error(t, err)
}

func TestMappingSlice(t *testing.T) {
var s struct {
Slice []int `form:"slice,default=9"`
Expand Down

0 comments on commit 7f4c8c3

Please sign in to comment.