Skip to content

Commit

Permalink
Form reader support for encoding.TextUnmarshaler
Browse files Browse the repository at this point in the history
This allows Context.Read to read components that support the text unmarshaler interface, like UUID libraries
  • Loading branch information
RangelReale committed Dec 20, 2017
1 parent 4d40027 commit aa09cd1
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 0 deletions.
29 changes: 29 additions & 0 deletions reader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package routing

import (
"encoding"
"encoding/json"
"encoding/xml"
"errors"
Expand All @@ -19,6 +20,10 @@ const (
MIME_MULTIPART_FORM = "multipart/form-data"
)

var (
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)

// DataReader is used by Context.Read() to read data from an HTTP request.
type DataReader interface {
// Read reads from the given HTTP request and populate the specified data.
Expand Down Expand Up @@ -107,6 +112,13 @@ func readForm(form map[string][]string, prefix string, rv reflect.Value) error {
name = prefix + "." + name
}

// check if type implements a known type, like encoding.TextUnmarshaler
if ok, err := readFormFieldKnownType(form, name, rv.Field(i)); err != nil {
return err
} else if ok {
continue
}

if ft.Kind() != reflect.Struct {
if err := readFormField(form, name, rv.Field(i)); err != nil {
return err
Expand All @@ -124,6 +136,23 @@ func readForm(form map[string][]string, prefix string, rv reflect.Value) error {
return nil
}

func readFormFieldKnownType(form map[string][]string, name string, rv reflect.Value) (bool, error) {
value, ok := form[name]
if !ok {
return false, nil
}
rv = indirect(rv)
rt := rv.Type()

// check if type implements encoding.TextUnmarshaler
if rt.Implements(textUnmarshalerType) {
return true, rv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value[0]))
} else if reflect.PtrTo(rt).Implements(textUnmarshalerType) {
return true, rv.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value[0]))
}
return false, nil
}

func readFormField(form map[string][]string, name string, rv reflect.Value) error {
value, ok := form[name]
if !ok {
Expand Down
24 changes: 24 additions & 0 deletions reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,27 @@ func TestDefaultDataReader(t *testing.T) {
assert.Equal(t, expected, data, test.tag)
}
}

type TU struct {
UValue string
}

func (tu *TU) UnmarshalText(text []byte) error {
tu.UValue = "TU_" + string(text[:])
return nil
}

func TestTextUnmarshaler(t *testing.T) {
var a struct {
ATU TU `form:"atu"`
NTU string `form:"ntu"`
}
values := map[string][]string{
"atu": []string{"ORIGINAL"},
"ntu": []string{"ORIGINAL"},
}
err := ReadFormData(values, &a)
assert.Nil(t, err)
assert.Equal(t, "TU_ORIGINAL", a.ATU.UValue)
assert.Equal(t, "ORIGINAL", a.NTU)
}

0 comments on commit aa09cd1

Please sign in to comment.