Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions binding/form_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ type BindUnmarshaler interface {
func trySetCustom(val string, value reflect.Value) (isSet bool, err error) {
switch v := value.Addr().Interface().(type) {
case BindUnmarshaler:
return true, v.UnmarshalParam(val)
if err := v.UnmarshalParam(val); err != nil {
return true, fmt.Errorf("invalid value %q: %w", val, err)
}
return true, nil
}
return false, nil
}
Expand Down Expand Up @@ -245,7 +248,10 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
}

if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
if err != nil {
return true, fmt.Errorf("field %q: %w", field.Name, err)
}
return true, nil
}

if vs, err = trySplit(vs, field); err != nil {
Expand All @@ -268,7 +274,10 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
}

if ok, err = trySetCustom(vs[0], value); ok {
return ok, err
if err != nil {
return true, fmt.Errorf("field %q: %w", field.Name, err)
}
return true, nil
}

if vs, err = trySplit(vs, field); err != nil {
Expand All @@ -293,7 +302,10 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
}
}
if ok, err := trySetCustom(val, value); ok {
return ok, err
if err != nil {
return true, fmt.Errorf("field %q: %w", field.Name, err)
}
return true, nil
}
return true, setWithProperType(val, value, field)
}
Expand Down Expand Up @@ -456,6 +468,12 @@ 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 {
if ok, err := trySetCustom(s, value.Index(i)); ok {
if err != nil {
return fmt.Errorf("field %q: %w", field.Name, err)
}
continue
}
err := setWithProperType(s, value.Index(i), field)
if err != nil {
return err
Expand Down
101 changes: 101 additions & 0 deletions binding/form_mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -715,3 +715,104 @@ func TestMappingEmptyValues(t *testing.T) {
assert.Equal(t, []int{1, 2, 3}, s.SliceCsv)
})
}

type testCustom struct {
Value string
}

func (t *testCustom) UnmarshalParam(param string) error {
t.Value = "prefix_" + param
return nil
}

func TestTrySetCustomIntegration(t *testing.T) {
var s struct {
F testCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"hello"}}, "form")
require.NoError(t, err)
assert.Equal(t, "prefix_hello", s.F.Value)
}

type badCustom struct{}

func (b *badCustom) UnmarshalParam(s string) error {
return errors.New("boom")
}

func TestTrySetCustom_SingleValues(t *testing.T) {
t.Run("Error case", func(t *testing.T) {
var s struct {
F badCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"hello"}}, "form")
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid value")
})

t.Run("Integration success", func(t *testing.T) {
var s struct {
F testCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"hello"}}, "form")
require.NoError(t, err)
assert.Equal(t, "prefix_hello", s.F.Value)
})

t.Run("Not applicable type", func(t *testing.T) {
var s struct {
N int `form:"n"`
}

err := mappingByPtr(&s, formSource{"n": {"42"}}, "form")
require.NoError(t, err)
assert.Equal(t, 42, s.N)
})
}

func TestTrySetCustom_Collections(t *testing.T) {
t.Run("Slice success", func(t *testing.T) {
var s struct {
F []testCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"one", "two"}}, "form")
require.NoError(t, err)
assert.Equal(t, "prefix_one", s.F[0].Value)
assert.Equal(t, "prefix_two", s.F[1].Value)
})

t.Run("Array success", func(t *testing.T) {
var s struct {
F [2]testCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"hello", "world"}}, "form")
require.NoError(t, err)
assert.Equal(t, "prefix_hello", s.F[0].Value)
assert.Equal(t, "prefix_world", s.F[1].Value)
})

t.Run("Slice error", func(t *testing.T) {
var s struct {
F []badCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"oops1", "oops2"}}, "form")
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid value")
})

t.Run("Array error", func(t *testing.T) {
var s struct {
F [2]badCustom `form:"f"`
}

err := mappingByPtr(&s, formSource{"f": {"fail1", "fail2"}}, "form")
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid value")
})
}