summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gorilla
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gorilla')
-rw-r--r--vendor/github.com/gorilla/schema/cache.go54
-rw-r--r--vendor/github.com/gorilla/schema/converter.go2
-rw-r--r--vendor/github.com/gorilla/schema/decoder.go149
-rw-r--r--vendor/github.com/gorilla/schema/doc.go6
-rw-r--r--vendor/github.com/gorilla/schema/encoder.go40
5 files changed, 193 insertions, 58 deletions
diff --git a/vendor/github.com/gorilla/schema/cache.go b/vendor/github.com/gorilla/schema/cache.go
index b2c5995d..73b75f48 100644
--- a/vendor/github.com/gorilla/schema/cache.go
+++ b/vendor/github.com/gorilla/schema/cache.go
@@ -18,13 +18,9 @@ var invalidPath = errors.New("schema: invalid path")
func newCache() *cache {
c := cache{
m: make(map[reflect.Type]*structInfo),
- conv: make(map[reflect.Kind]Converter),
regconv: make(map[reflect.Type]Converter),
tag: "schema",
}
- for k, v := range converters {
- c.conv[k] = v
- }
return &c
}
@@ -32,11 +28,15 @@ func newCache() *cache {
type cache struct {
l sync.RWMutex
m map[reflect.Type]*structInfo
- conv map[reflect.Kind]Converter
regconv map[reflect.Type]Converter
tag string
}
+// registerConverter registers a converter function for a custom type.
+func (c *cache) registerConverter(value interface{}, converterFunc Converter) {
+ c.regconv[reflect.TypeOf(value)] = converterFunc
+}
+
// parsePath parses a path in dotted notation verifying that it is a valid
// path to a struct field.
//
@@ -63,7 +63,7 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
}
// Valid field. Append index.
path = append(path, field.name)
- if field.ss {
+ if field.isSliceOfStructs && (!field.unmarshalerInfo.IsValid || (field.unmarshalerInfo.IsValid && field.unmarshalerInfo.IsSliceElement)) {
// Parse a special case: slices of structs.
// i+1 must be the slice index.
//
@@ -142,7 +142,7 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
c.create(ft, info)
for _, fi := range info.fields[bef:len(info.fields)] {
// exclude required check because duplicated to embedded field
- fi.required = false
+ fi.isRequired = false
}
}
}
@@ -162,6 +162,7 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
// First let's get the basic type.
isSlice, isStruct := false, false
ft := field.Type
+ m := isTextUnmarshaler(reflect.Zero(ft))
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
@@ -178,29 +179,26 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
}
}
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
- if conv := c.converter(ft); conv == nil {
+ if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
// Type is not supported.
return
}
}
info.fields = append(info.fields, &fieldInfo{
- typ: field.Type,
- name: field.Name,
- ss: isSlice && isStruct,
- alias: alias,
- anon: field.Anonymous,
- required: options.Contains("required"),
+ typ: field.Type,
+ name: field.Name,
+ alias: alias,
+ unmarshalerInfo: m,
+ isSliceOfStructs: isSlice && isStruct,
+ isAnonymous: field.Anonymous,
+ isRequired: options.Contains("required"),
})
}
// converter returns the converter for a type.
func (c *cache) converter(t reflect.Type) Converter {
- conv := c.regconv[t]
- if conv == nil {
- conv = c.conv[t.Kind()]
- }
- return conv
+ return c.regconv[t]
}
// ----------------------------------------------------------------------------
@@ -219,12 +217,18 @@ func (i *structInfo) get(alias string) *fieldInfo {
}
type fieldInfo struct {
- typ reflect.Type
- name string // field name in the struct.
- ss bool // true if this is a slice of structs.
- alias string
- anon bool // is an embedded field
- required bool // tag option
+ typ reflect.Type
+ // name is the field name in the struct.
+ name string
+ alias string
+ // unmarshalerInfo contains information regarding the
+ // encoding.TextUnmarshaler implementation of the field type.
+ unmarshalerInfo unmarshaler
+ // isSliceOfStructs indicates if the field type is a slice of structs.
+ isSliceOfStructs bool
+ // isAnonymous indicates whether the field is embedded in the struct.
+ isAnonymous bool
+ isRequired bool
}
type pathPart struct {
diff --git a/vendor/github.com/gorilla/schema/converter.go b/vendor/github.com/gorilla/schema/converter.go
index b33e9423..4f2116a1 100644
--- a/vendor/github.com/gorilla/schema/converter.go
+++ b/vendor/github.com/gorilla/schema/converter.go
@@ -30,7 +30,7 @@ var (
)
// Default converters for basic types.
-var converters = map[reflect.Kind]Converter{
+var builtinConverters = map[reflect.Kind]Converter{
boolType: convertBool,
float32Type: convertFloat32,
float64Type: convertFloat64,
diff --git a/vendor/github.com/gorilla/schema/decoder.go b/vendor/github.com/gorilla/schema/decoder.go
index b0f8cb00..5352a908 100644
--- a/vendor/github.com/gorilla/schema/decoder.go
+++ b/vendor/github.com/gorilla/schema/decoder.go
@@ -56,7 +56,7 @@ func (d *Decoder) IgnoreUnknownKeys(i bool) {
// RegisterConverter registers a converter function for a custom type.
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
- d.cache.regconv[reflect.TypeOf(value)] = converterFunc
+ d.cache.registerConverter(value, converterFunc)
}
// Decode decodes a map[string][]string to a struct.
@@ -90,7 +90,7 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
return d.checkRequired(t, src, "")
}
-// checkRequired checks whether requred field empty
+// checkRequired checks whether required fields are empty
//
// check type t recursively if t has struct fields, and prefix is same as parsePath: in dotted notation
//
@@ -106,7 +106,7 @@ func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix
if f.typ.Kind() == reflect.Struct {
err := d.checkRequired(f.typ, src, prefix+f.alias+".")
if err != nil {
- if !f.anon {
+ if !f.isAnonymous {
return err
}
// check embedded parent field.
@@ -116,7 +116,7 @@ func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix
}
}
}
- if f.required {
+ if f.isRequired {
key := f.alias
if prefix != "" {
key = prefix + key
@@ -153,7 +153,6 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
}
v = v.FieldByName(name)
}
-
// Don't even bother for unexported fields.
if !v.CanSet() {
return nil
@@ -185,7 +184,8 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
// Get the converter early in case there is one for a slice type.
conv := d.cache.converter(t)
- if conv == nil && t.Kind() == reflect.Slice {
+ m := isTextUnmarshaler(v)
+ if conv == nil && t.Kind() == reflect.Slice && m.IsSliceElement {
var items []reflect.Value
elemT := t.Elem()
isPtrElem := elemT.Kind() == reflect.Ptr
@@ -196,9 +196,12 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
// Try to get a converter for the element type.
conv := d.cache.converter(elemT)
if conv == nil {
- // As we are not dealing with slice of structs here, we don't need to check if the type
- // implements TextUnmarshaler interface
- return fmt.Errorf("schema: converter not found for %v", elemT)
+ conv = builtinConverters[elemT.Kind()]
+ if conv == nil {
+ // As we are not dealing with slice of structs here, we don't need to check if the type
+ // implements TextUnmarshaler interface
+ return fmt.Errorf("schema: converter not found for %v", elemT)
+ }
}
for key, value := range values {
@@ -206,6 +209,26 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
if d.zeroEmpty {
items = append(items, reflect.Zero(elemT))
}
+ } else if m.IsValid {
+ u := reflect.New(elemT)
+ if m.IsSliceElementPtr {
+ u = reflect.New(reflect.PtrTo(elemT).Elem())
+ }
+ if err := u.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)); err != nil {
+ return ConversionError{
+ Key: path,
+ Type: t,
+ Index: key,
+ Err: err,
+ }
+ }
+ if m.IsSliceElementPtr {
+ items = append(items, u.Elem().Addr())
+ } else if u.Kind() == reflect.Ptr {
+ items = append(items, u.Elem())
+ } else {
+ items = append(items, u)
+ }
} else if item := conv(value); item.IsValid() {
if isPtrElem {
ptr := reflect.New(elemT)
@@ -260,11 +283,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
val = values[len(values)-1]
}
- if val == "" {
- if d.zeroEmpty {
- v.Set(reflect.Zero(t))
- }
- } else if conv != nil {
+ if conv != nil {
if value := conv(val); value.IsValid() {
v.Set(value.Convert(t))
} else {
@@ -274,16 +293,10 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
Index: -1,
}
}
- } else {
- // When there's no registered conversion for the custom type, we will check if the type
- // implements the TextUnmarshaler interface. As the UnmarshalText function should be applied
- // to the pointer of the type, we convert the value to pointer.
- if v.CanAddr() {
- v = v.Addr()
- }
-
- if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
- if err := u.UnmarshalText([]byte(val)); err != nil {
+ } else if m.IsValid {
+ if m.IsPtr {
+ u := reflect.New(v.Type())
+ if err := u.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(val)); err != nil {
return ConversionError{
Key: path,
Type: t,
@@ -291,15 +304,99 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
Err: err,
}
}
-
+ v.Set(reflect.Indirect(u))
+ } else {
+ // If the value implements the encoding.TextUnmarshaler interface
+ // apply UnmarshalText as the converter
+ if err := m.Unmarshaler.UnmarshalText([]byte(val)); err != nil {
+ return ConversionError{
+ Key: path,
+ Type: t,
+ Index: -1,
+ Err: err,
+ }
+ }
+ }
+ } else if val == "" {
+ if d.zeroEmpty {
+ v.Set(reflect.Zero(t))
+ }
+ } else if conv := builtinConverters[t.Kind()]; conv != nil {
+ if value := conv(val); value.IsValid() {
+ v.Set(value.Convert(t))
} else {
- return fmt.Errorf("schema: converter not found for %v", t)
+ return ConversionError{
+ Key: path,
+ Type: t,
+ Index: -1,
+ }
}
+ } else {
+ return fmt.Errorf("schema: converter not found for %v", t)
}
}
return nil
}
+func isTextUnmarshaler(v reflect.Value) unmarshaler {
+ // Create a new unmarshaller instance
+ m := unmarshaler{}
+ if m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler); m.IsValid {
+ return m
+ }
+ // As the UnmarshalText function should be applied to the pointer of the
+ // type, we check that type to see if it implements the necessary
+ // method.
+ if m.Unmarshaler, m.IsValid = reflect.New(v.Type()).Interface().(encoding.TextUnmarshaler); m.IsValid {
+ m.IsPtr = true
+ return m
+ }
+
+ // if v is []T or *[]T create new T
+ t := v.Type()
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() == reflect.Slice {
+ // Check if the slice implements encoding.TextUnmarshaller
+ if m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler); m.IsValid {
+ return m
+ }
+ // If t is a pointer slice, check if its elements implement
+ // encoding.TextUnmarshaler
+ m.IsSliceElement = true
+ if t = t.Elem(); t.Kind() == reflect.Ptr {
+ t = reflect.PtrTo(t.Elem())
+ v = reflect.Zero(t)
+ m.IsSliceElementPtr = true
+ m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler)
+ return m
+ }
+ }
+
+ v = reflect.New(t)
+ m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler)
+ return m
+}
+
+// TextUnmarshaler helpers ----------------------------------------------------
+// unmarshaller contains information about a TextUnmarshaler type
+type unmarshaler struct {
+ Unmarshaler encoding.TextUnmarshaler
+ // IsValid indicates whether the resolved type indicated by the other
+ // flags implements the encoding.TextUnmarshaler interface.
+ IsValid bool
+ // IsPtr indicates that the resolved type is the pointer of the original
+ // type.
+ IsPtr bool
+ // IsSliceElement indicates that the resolved type is a slice element of
+ // the original type.
+ IsSliceElement bool
+ // IsSliceElementPtr indicates that the resolved type is a pointer to a
+ // slice element of the original type.
+ IsSliceElementPtr bool
+}
+
// Errors ---------------------------------------------------------------------
// ConversionError stores information about a failed conversion.
diff --git a/vendor/github.com/gorilla/schema/doc.go b/vendor/github.com/gorilla/schema/doc.go
index a95e87bd..aae9f33f 100644
--- a/vendor/github.com/gorilla/schema/doc.go
+++ b/vendor/github.com/gorilla/schema/doc.go
@@ -24,7 +24,7 @@ The basic usage is really simple. Given this struct:
This is just a simple example and it doesn't make a lot of sense to create
the map manually. Typically it will come from a http.Request object and
-will be of type url.Values: http.Request.Form or http.Request.MultipartForm:
+will be of type url.Values, http.Request.Form, or http.Request.MultipartForm:
func MyHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
@@ -45,7 +45,7 @@ will be of type url.Values: http.Request.Form or http.Request.MultipartForm:
}
Note: it is a good idea to set a Decoder instance as a package global,
-because it caches meta-data about structs, and a instance can be shared safely:
+because it caches meta-data about structs, and an instance can be shared safely:
var decoder = schema.NewDecoder()
@@ -121,7 +121,7 @@ field, we could not translate multiple values to it if we did not use an
index for the parent struct.
There's also the possibility to create a custom type that implements the
-TextUnmarshaler interface, and in this case there's no need to registry
+TextUnmarshaler interface, and in this case there's no need to register
a converter, like:
type Person struct {
diff --git a/vendor/github.com/gorilla/schema/encoder.go b/vendor/github.com/gorilla/schema/encoder.go
index aa43a5c9..bf1d511e 100644
--- a/vendor/github.com/gorilla/schema/encoder.go
+++ b/vendor/github.com/gorilla/schema/encoder.go
@@ -40,6 +40,34 @@ func (e *Encoder) SetAliasTag(tag string) {
e.cache.tag = tag
}
+// isValidStructPointer test if input value is a valid struct pointer.
+func isValidStructPointer(v reflect.Value) bool {
+ return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct
+}
+
+func isZero(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Func:
+ case reflect.Map, reflect.Slice:
+ return v.IsNil() || v.Len() == 0
+ case reflect.Array:
+ z := true
+ for i := 0; i < v.Len(); i++ {
+ z = z && isZero(v.Index(i))
+ }
+ return z
+ case reflect.Struct:
+ z := true
+ for i := 0; i < v.NumField(); i++ {
+ z = z && isZero(v.Field(i))
+ }
+ return z
+ }
+ // Compare other types directly:
+ z := reflect.Zero(v.Type())
+ return v.Interface() == z.Interface()
+}
+
func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
if v.Kind() == reflect.Ptr {
v = v.Elem()
@@ -57,8 +85,9 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
continue
}
- if v.Field(i).Type().Kind() == reflect.Struct {
- e.encode(v.Field(i), dst)
+ // Encode struct pointer types if the field is a valid pointer and a struct.
+ if isValidStructPointer(v.Field(i)) {
+ e.encode(v.Field(i).Elem(), dst)
continue
}
@@ -67,7 +96,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
// Encode non-slice types and custom implementations immediately.
if encFunc != nil {
value := encFunc(v.Field(i))
- if value == "" && opts.Contains("omitempty") {
+ if opts.Contains("omitempty") && isZero(v.Field(i)) {
continue
}
@@ -75,6 +104,11 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
continue
}
+ if v.Field(i).Type().Kind() == reflect.Struct {
+ e.encode(v.Field(i), dst)
+ continue
+ }
+
if v.Field(i).Type().Kind() == reflect.Slice {
encFunc = typeEncoder(v.Field(i).Type().Elem(), e.regenc)
}