From 09875fe1603307080f3a4172985c5dca3bd9912d Mon Sep 17 00:00:00 2001 From: Duco van Amstel Date: Sun, 18 Nov 2018 17:55:05 +0000 Subject: Update direct dependencies where possible --- vendor/github.com/gorilla/schema/cache.go | 54 +++++----- vendor/github.com/gorilla/schema/converter.go | 2 +- vendor/github.com/gorilla/schema/decoder.go | 149 +++++++++++++++++++++----- vendor/github.com/gorilla/schema/doc.go | 6 +- vendor/github.com/gorilla/schema/encoder.go | 40 ++++++- 5 files changed, 193 insertions(+), 58 deletions(-) (limited to 'vendor/github.com/gorilla/schema') 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) } -- cgit v1.2.3