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/.travis.yml18
-rw-r--r--vendor/github.com/gorilla/schema/LICENSE27
-rw-r--r--vendor/github.com/gorilla/schema/README.md90
-rw-r--r--vendor/github.com/gorilla/schema/cache.go264
-rw-r--r--vendor/github.com/gorilla/schema/converter.go145
-rw-r--r--vendor/github.com/gorilla/schema/decoder.go420
-rw-r--r--vendor/github.com/gorilla/schema/decoder_test.go1693
-rw-r--r--vendor/github.com/gorilla/schema/doc.go148
-rw-r--r--vendor/github.com/gorilla/schema/encoder.go195
-rw-r--r--vendor/github.com/gorilla/schema/encoder_test.go420
-rw-r--r--vendor/github.com/gorilla/websocket/conn.go15
11 files changed, 3429 insertions, 6 deletions
diff --git a/vendor/github.com/gorilla/schema/.travis.yml b/vendor/github.com/gorilla/schema/.travis.yml
new file mode 100644
index 000000000..5f51dce4e
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/.travis.yml
@@ -0,0 +1,18 @@
+language: go
+sudo: false
+
+matrix:
+ include:
+ - go: 1.5
+ - go: 1.6
+ - go: 1.7
+ - go: 1.8
+ - go: tip
+ allow_failures:
+ - go: tip
+
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go vet $(go list ./... | grep -v /vendor/)
+ - go test -v -race ./...
diff --git a/vendor/github.com/gorilla/schema/LICENSE b/vendor/github.com/gorilla/schema/LICENSE
new file mode 100644
index 000000000..0e5fb8728
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/schema/README.md b/vendor/github.com/gorilla/schema/README.md
new file mode 100644
index 000000000..2c3ecd8e3
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/README.md
@@ -0,0 +1,90 @@
+schema
+======
+[![GoDoc](https://godoc.org/github.com/gorilla/schema?status.svg)](https://godoc.org/github.com/gorilla/schema) [![Build Status](https://travis-ci.org/gorilla/schema.png?branch=master)](https://travis-ci.org/gorilla/schema)
+[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge)
+
+
+Package gorilla/schema converts structs to and from form values.
+
+## Example
+
+Here's a quick example: we parse POST form values and then decode them into a struct:
+
+```go
+// Set a Decoder instance as a package global, because it caches
+// meta-data about structs, and an instance can be shared safely.
+var decoder = schema.NewDecoder()
+
+type Person struct {
+ Name string
+ Phone string
+}
+
+func MyHandler(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseForm()
+ if err != nil {
+ // Handle error
+ }
+
+ var person Person
+
+ // r.PostForm is a map of our POST form values
+ err := decoder.Decode(&person, r.PostForm)
+ if err != nil {
+ // Handle error
+ }
+
+ // Do something with person.Name or person.Phone
+}
+```
+
+Conversely, contents of a struct can be encoded into form values. Here's a variant of the previous example using the Encoder:
+
+```go
+var encoder = schema.NewEncoder()
+
+func MyHttpRequest() {
+ person := Person{"Jane Doe", "555-5555"}
+ form := url.Values{}
+
+ err := encoder.Encode(person, form)
+
+ if err != nil {
+ // Handle error
+ }
+
+ // Use form values, for example, with an http client
+ client := new(http.Client)
+ res, err := client.PostForm("http://my-api.test", form)
+}
+
+```
+
+To define custom names for fields, use a struct tag "schema". To not populate certain fields, use a dash for the name and it will be ignored:
+
+```go
+type Person struct {
+ Name string `schema:"name"` // custom name
+ Phone string `schema:"phone"` // custom name
+ Admin bool `schema:"-"` // this field is never set
+}
+```
+
+The supported field types in the struct are:
+
+* bool
+* float variants (float32, float64)
+* int variants (int, int8, int16, int32, int64)
+* string
+* uint variants (uint, uint8, uint16, uint32, uint64)
+* struct
+* a pointer to one of the above types
+* a slice or a pointer to a slice of one of the above types
+
+Unsupported types are simply ignored, however custom types can be registered to be converted.
+
+More examples are available on the Gorilla website: http://www.gorillatoolkit.org/pkg/schema
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/vendor/github.com/gorilla/schema/cache.go b/vendor/github.com/gorilla/schema/cache.go
new file mode 100644
index 000000000..afa20a3a3
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/cache.go
@@ -0,0 +1,264 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package schema
+
+import (
+ "errors"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+var invalidPath = errors.New("schema: invalid path")
+
+// newCache returns a new cache.
+func newCache() *cache {
+ c := cache{
+ m: make(map[reflect.Type]*structInfo),
+ regconv: make(map[reflect.Type]Converter),
+ tag: "schema",
+ }
+ return &c
+}
+
+// cache caches meta-data about a struct.
+type cache struct {
+ l sync.RWMutex
+ m map[reflect.Type]*structInfo
+ 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.
+//
+// It returns "path parts" which contain indices to fields to be used by
+// reflect.Value.FieldByString(). Multiple parts are required for slices of
+// structs.
+func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
+ var struc *structInfo
+ var field *fieldInfo
+ var index64 int64
+ var err error
+ parts := make([]pathPart, 0)
+ path := make([]string, 0)
+ keys := strings.Split(p, ".")
+ for i := 0; i < len(keys); i++ {
+ if t.Kind() != reflect.Struct {
+ return nil, invalidPath
+ }
+ if struc = c.get(t); struc == nil {
+ return nil, invalidPath
+ }
+ if field = struc.get(keys[i]); field == nil {
+ return nil, invalidPath
+ }
+ // Valid field. Append index.
+ path = append(path, field.name)
+ if field.ss {
+ // Parse a special case: slices of structs.
+ // i+1 must be the slice index.
+ //
+ // Now that struct can implements TextUnmarshaler interface,
+ // we don't need to force the struct's fields to appear in the path.
+ // So checking i+2 is not necessary anymore.
+ i++
+ if i+1 > len(keys) {
+ return nil, invalidPath
+ }
+ if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil {
+ return nil, invalidPath
+ }
+ parts = append(parts, pathPart{
+ path: path,
+ field: field,
+ index: int(index64),
+ })
+ path = make([]string, 0)
+
+ // Get the next struct type, dropping ptrs.
+ if field.typ.Kind() == reflect.Ptr {
+ t = field.typ.Elem()
+ } else {
+ t = field.typ
+ }
+ if t.Kind() == reflect.Slice {
+ t = t.Elem()
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ }
+ } else if field.typ.Kind() == reflect.Ptr {
+ t = field.typ.Elem()
+ } else {
+ t = field.typ
+ }
+ }
+ // Add the remaining.
+ parts = append(parts, pathPart{
+ path: path,
+ field: field,
+ index: -1,
+ })
+ return parts, nil
+}
+
+// get returns a cached structInfo, creating it if necessary.
+func (c *cache) get(t reflect.Type) *structInfo {
+ c.l.RLock()
+ info := c.m[t]
+ c.l.RUnlock()
+ if info == nil {
+ info = c.create(t, nil)
+ c.l.Lock()
+ c.m[t] = info
+ c.l.Unlock()
+ }
+ return info
+}
+
+// create creates a structInfo with meta-data about a struct.
+func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
+ if info == nil {
+ info = &structInfo{fields: []*fieldInfo{}}
+ }
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ if field.Anonymous {
+ ft := field.Type
+ if ft.Kind() == reflect.Ptr {
+ ft = ft.Elem()
+ }
+ if ft.Kind() == reflect.Struct {
+ bef := len(info.fields)
+ c.create(ft, info)
+ for _, fi := range info.fields[bef:len(info.fields)] {
+ // exclude required check because duplicated to embedded field
+ fi.required = false
+ }
+ }
+ }
+ c.createField(field, info)
+ }
+ return info
+}
+
+// createField creates a fieldInfo for the given field.
+func (c *cache) createField(field reflect.StructField, info *structInfo) {
+ alias, options := fieldAlias(field, c.tag)
+ if alias == "-" {
+ // Ignore this field.
+ return
+ }
+ // Check if the type is supported and don't cache it if not.
+ // First let's get the basic type.
+ isSlice, isStruct := false, false
+ ft := field.Type
+ if ft.Kind() == reflect.Ptr {
+ ft = ft.Elem()
+ }
+ if isSlice = ft.Kind() == reflect.Slice; isSlice {
+ ft = ft.Elem()
+ if ft.Kind() == reflect.Ptr {
+ ft = ft.Elem()
+ }
+ }
+ if ft.Kind() == reflect.Array {
+ ft = ft.Elem()
+ if ft.Kind() == reflect.Ptr {
+ ft = ft.Elem()
+ }
+ }
+ if isStruct = ft.Kind() == reflect.Struct; !isStruct {
+ 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"),
+ })
+}
+
+// converter returns the converter for a type.
+func (c *cache) converter(t reflect.Type) Converter {
+ return c.regconv[t]
+}
+
+// ----------------------------------------------------------------------------
+
+type structInfo struct {
+ fields []*fieldInfo
+}
+
+func (i *structInfo) get(alias string) *fieldInfo {
+ for _, field := range i.fields {
+ if strings.EqualFold(field.alias, alias) {
+ return field
+ }
+ }
+ return nil
+}
+
+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
+}
+
+type pathPart struct {
+ field *fieldInfo
+ path []string // path to the field: walks structs using field names.
+ index int // struct index in slices of structs.
+}
+
+// ----------------------------------------------------------------------------
+
+// fieldAlias parses a field tag to get a field alias.
+func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
+ if tag := field.Tag.Get(tagName); tag != "" {
+ alias, options = parseTag(tag)
+ }
+ if alias == "" {
+ alias = field.Name
+ }
+ return alias, options
+}
+
+// tagOptions is the string following a comma in a struct field's tag, or
+// the empty string. It does not include the leading comma.
+type tagOptions []string
+
+// parseTag splits a struct field's url tag into its name and comma-separated
+// options.
+func parseTag(tag string) (string, tagOptions) {
+ s := strings.Split(tag, ",")
+ return s[0], s[1:]
+}
+
+// Contains checks whether the tagOptions contains the specified option.
+func (o tagOptions) Contains(option string) bool {
+ for _, s := range o {
+ if s == option {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/gorilla/schema/converter.go b/vendor/github.com/gorilla/schema/converter.go
new file mode 100644
index 000000000..4f2116a15
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/converter.go
@@ -0,0 +1,145 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package schema
+
+import (
+ "reflect"
+ "strconv"
+)
+
+type Converter func(string) reflect.Value
+
+var (
+ invalidValue = reflect.Value{}
+ boolType = reflect.Bool
+ float32Type = reflect.Float32
+ float64Type = reflect.Float64
+ intType = reflect.Int
+ int8Type = reflect.Int8
+ int16Type = reflect.Int16
+ int32Type = reflect.Int32
+ int64Type = reflect.Int64
+ stringType = reflect.String
+ uintType = reflect.Uint
+ uint8Type = reflect.Uint8
+ uint16Type = reflect.Uint16
+ uint32Type = reflect.Uint32
+ uint64Type = reflect.Uint64
+)
+
+// Default converters for basic types.
+var builtinConverters = map[reflect.Kind]Converter{
+ boolType: convertBool,
+ float32Type: convertFloat32,
+ float64Type: convertFloat64,
+ intType: convertInt,
+ int8Type: convertInt8,
+ int16Type: convertInt16,
+ int32Type: convertInt32,
+ int64Type: convertInt64,
+ stringType: convertString,
+ uintType: convertUint,
+ uint8Type: convertUint8,
+ uint16Type: convertUint16,
+ uint32Type: convertUint32,
+ uint64Type: convertUint64,
+}
+
+func convertBool(value string) reflect.Value {
+ if value == "on" {
+ return reflect.ValueOf(true)
+ } else if v, err := strconv.ParseBool(value); err == nil {
+ return reflect.ValueOf(v)
+ }
+ return invalidValue
+}
+
+func convertFloat32(value string) reflect.Value {
+ if v, err := strconv.ParseFloat(value, 32); err == nil {
+ return reflect.ValueOf(float32(v))
+ }
+ return invalidValue
+}
+
+func convertFloat64(value string) reflect.Value {
+ if v, err := strconv.ParseFloat(value, 64); err == nil {
+ return reflect.ValueOf(v)
+ }
+ return invalidValue
+}
+
+func convertInt(value string) reflect.Value {
+ if v, err := strconv.ParseInt(value, 10, 0); err == nil {
+ return reflect.ValueOf(int(v))
+ }
+ return invalidValue
+}
+
+func convertInt8(value string) reflect.Value {
+ if v, err := strconv.ParseInt(value, 10, 8); err == nil {
+ return reflect.ValueOf(int8(v))
+ }
+ return invalidValue
+}
+
+func convertInt16(value string) reflect.Value {
+ if v, err := strconv.ParseInt(value, 10, 16); err == nil {
+ return reflect.ValueOf(int16(v))
+ }
+ return invalidValue
+}
+
+func convertInt32(value string) reflect.Value {
+ if v, err := strconv.ParseInt(value, 10, 32); err == nil {
+ return reflect.ValueOf(int32(v))
+ }
+ return invalidValue
+}
+
+func convertInt64(value string) reflect.Value {
+ if v, err := strconv.ParseInt(value, 10, 64); err == nil {
+ return reflect.ValueOf(v)
+ }
+ return invalidValue
+}
+
+func convertString(value string) reflect.Value {
+ return reflect.ValueOf(value)
+}
+
+func convertUint(value string) reflect.Value {
+ if v, err := strconv.ParseUint(value, 10, 0); err == nil {
+ return reflect.ValueOf(uint(v))
+ }
+ return invalidValue
+}
+
+func convertUint8(value string) reflect.Value {
+ if v, err := strconv.ParseUint(value, 10, 8); err == nil {
+ return reflect.ValueOf(uint8(v))
+ }
+ return invalidValue
+}
+
+func convertUint16(value string) reflect.Value {
+ if v, err := strconv.ParseUint(value, 10, 16); err == nil {
+ return reflect.ValueOf(uint16(v))
+ }
+ return invalidValue
+}
+
+func convertUint32(value string) reflect.Value {
+ if v, err := strconv.ParseUint(value, 10, 32); err == nil {
+ return reflect.ValueOf(uint32(v))
+ }
+ return invalidValue
+}
+
+func convertUint64(value string) reflect.Value {
+ if v, err := strconv.ParseUint(value, 10, 64); err == nil {
+ return reflect.ValueOf(v)
+ }
+ return invalidValue
+}
diff --git a/vendor/github.com/gorilla/schema/decoder.go b/vendor/github.com/gorilla/schema/decoder.go
new file mode 100644
index 000000000..e49b53c87
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/decoder.go
@@ -0,0 +1,420 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package schema
+
+import (
+ "encoding"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// NewDecoder returns a new Decoder.
+func NewDecoder() *Decoder {
+ return &Decoder{cache: newCache()}
+}
+
+// Decoder decodes values from a map[string][]string to a struct.
+type Decoder struct {
+ cache *cache
+ zeroEmpty bool
+ ignoreUnknownKeys bool
+}
+
+// SetAliasTag changes the tag used to locate custom field aliases.
+// The default tag is "schema".
+func (d *Decoder) SetAliasTag(tag string) {
+ d.cache.tag = tag
+}
+
+// ZeroEmpty controls the behaviour when the decoder encounters empty values
+// in a map.
+// If z is true and a key in the map has the empty string as a value
+// then the corresponding struct field is set to the zero value.
+// If z is false then empty strings are ignored.
+//
+// The default value is false, that is empty values do not change
+// the value of the struct field.
+func (d *Decoder) ZeroEmpty(z bool) {
+ d.zeroEmpty = z
+}
+
+// IgnoreUnknownKeys controls the behaviour when the decoder encounters unknown
+// keys in the map.
+// If i is true and an unknown field is encountered, it is ignored. This is
+// similar to how unknown keys are handled by encoding/json.
+// If i is false then Decode will return an error. Note that any valid keys
+// will still be decoded in to the target struct.
+//
+// To preserve backwards compatibility, the default value is false.
+func (d *Decoder) IgnoreUnknownKeys(i bool) {
+ d.ignoreUnknownKeys = i
+}
+
+// RegisterConverter registers a converter function for a custom type.
+func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
+ d.cache.registerConverter(value, converterFunc)
+}
+
+// Decode decodes a map[string][]string to a struct.
+//
+// The first parameter must be a pointer to a struct.
+//
+// The second parameter is a map, typically url.Values from an HTTP request.
+// Keys are "paths" in dotted notation to the struct fields and nested structs.
+//
+// See the package documentation for a full explanation of the mechanics.
+func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
+ v := reflect.ValueOf(dst)
+ if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
+ return errors.New("schema: interface must be a pointer to struct")
+ }
+ v = v.Elem()
+ t := v.Type()
+ errors := MultiError{}
+ for path, values := range src {
+ if parts, err := d.cache.parsePath(path, t); err == nil {
+ if err = d.decode(v, path, parts, values); err != nil {
+ errors[path] = err
+ }
+ } else if !d.ignoreUnknownKeys {
+ errors[path] = fmt.Errorf("schema: invalid path %q", path)
+ }
+ }
+ if len(errors) > 0 {
+ return errors
+ }
+ return d.checkRequired(t, src, "")
+}
+
+// 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
+//
+// src is the source map for decoding, we use it here to see if those required fields are included in src
+func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix string) error {
+ struc := d.cache.get(t)
+ if struc == nil {
+ // unexpect, cache.get never return nil
+ return errors.New("cache fail")
+ }
+
+ for _, f := range struc.fields {
+ if f.typ.Kind() == reflect.Struct {
+ err := d.checkRequired(f.typ, src, prefix+f.alias+".")
+ if err != nil {
+ if !f.anon {
+ return err
+ }
+ // check embedded parent field.
+ err2 := d.checkRequired(f.typ, src, prefix)
+ if err2 != nil {
+ return err
+ }
+ }
+ }
+ if f.required {
+ key := f.alias
+ if prefix != "" {
+ key = prefix + key
+ }
+ if isEmpty(f.typ, src[key]) {
+ return fmt.Errorf("%v is empty", key)
+ }
+ }
+ }
+ return nil
+}
+
+// isEmpty returns true if value is empty for specific type
+func isEmpty(t reflect.Type, value []string) bool {
+ if len(value) == 0 {
+ return true
+ }
+ switch t.Kind() {
+ case boolType, float32Type, float64Type, intType, int8Type, int32Type, int64Type, stringType, uint8Type, uint16Type, uint32Type, uint64Type:
+ return len(value[0]) == 0
+ }
+ return false
+}
+
+// decode fills a struct field using a parsed path.
+func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error {
+ // Get the field walking the struct fields by index.
+ for _, name := range parts[0].path {
+ if v.Type().Kind() == reflect.Ptr {
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ v = v.FieldByName(name)
+ }
+ // Don't even bother for unexported fields.
+ if !v.CanSet() {
+ return nil
+ }
+
+ // Dereference if needed.
+ t := v.Type()
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ if v.IsNil() {
+ v.Set(reflect.New(t))
+ }
+ v = v.Elem()
+ }
+
+ // Slice of structs. Let's go recursive.
+ if len(parts) > 1 {
+ idx := parts[0].index
+ if v.IsNil() || v.Len() < idx+1 {
+ value := reflect.MakeSlice(t, idx+1, idx+1)
+ if v.Len() < idx+1 {
+ // Resize it.
+ reflect.Copy(value, v)
+ }
+ v.Set(value)
+ }
+ return d.decode(v.Index(idx), path, parts[1:], values)
+ }
+
+ // Get the converter early in case there is one for a slice type.
+ conv := d.cache.converter(t)
+ m := isTextUnmarshaler(v)
+ if conv == nil && t.Kind() == reflect.Slice && m.IsSlice {
+ var items []reflect.Value
+ elemT := t.Elem()
+ isPtrElem := elemT.Kind() == reflect.Ptr
+ if isPtrElem {
+ elemT = elemT.Elem()
+ }
+
+ // Try to get a converter for the element type.
+ conv := d.cache.converter(elemT)
+ if conv == nil {
+ 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 {
+ if value == "" {
+ if d.zeroEmpty {
+ items = append(items, reflect.Zero(elemT))
+ }
+ } else if m.IsValid {
+ u := reflect.New(elemT)
+ if m.IsPtr {
+ 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.IsPtr {
+ 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)
+ ptr.Elem().Set(item)
+ item = ptr
+ }
+ if item.Type() != elemT && !isPtrElem {
+ item = item.Convert(elemT)
+ }
+ items = append(items, item)
+ } else {
+ if strings.Contains(value, ",") {
+ values := strings.Split(value, ",")
+ for _, value := range values {
+ if value == "" {
+ if d.zeroEmpty {
+ items = append(items, reflect.Zero(elemT))
+ }
+ } else if item := conv(value); item.IsValid() {
+ if isPtrElem {
+ ptr := reflect.New(elemT)
+ ptr.Elem().Set(item)
+ item = ptr
+ }
+ if item.Type() != elemT && !isPtrElem {
+ item = item.Convert(elemT)
+ }
+ items = append(items, item)
+ } else {
+ return ConversionError{
+ Key: path,
+ Type: elemT,
+ Index: key,
+ }
+ }
+ }
+ } else {
+ return ConversionError{
+ Key: path,
+ Type: elemT,
+ Index: key,
+ }
+ }
+ }
+ }
+ value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...)
+ v.Set(value)
+ } else {
+ val := ""
+ // Use the last value provided if any values were provided
+ if len(values) > 0 {
+ val = values[len(values)-1]
+ }
+
+ if val == "" {
+ if d.zeroEmpty {
+ v.Set(reflect.Zero(t))
+ }
+ } else if conv != nil {
+ if value := conv(val); value.IsValid() {
+ v.Set(value.Convert(t))
+ } else {
+ return ConversionError{
+ Key: path,
+ Type: t,
+ Index: -1,
+ }
+ }
+ } else if m.IsValid {
+ // 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 conv := builtinConverters[t.Kind()]; conv != nil {
+ if value := conv(val); value.IsValid() {
+ v.Set(value.Convert(t))
+ } else {
+ 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{}
+
+ // 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 m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler); m.IsValid {
+ 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 {
+ // if t is a pointer slice, check if it implements encoding.TextUnmarshaler
+ m.IsSlice = true
+ if t = t.Elem(); t.Kind() == reflect.Ptr {
+ t = reflect.PtrTo(t.Elem())
+ v = reflect.Zero(t)
+ m.IsPtr = 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
+ IsSlice bool
+ IsPtr bool
+ IsValid bool
+}
+
+// Errors ---------------------------------------------------------------------
+
+// ConversionError stores information about a failed conversion.
+type ConversionError struct {
+ Key string // key from the source map.
+ Type reflect.Type // expected type of elem
+ Index int // index for multi-value fields; -1 for single-value fields.
+ Err error // low-level error (when it exists)
+}
+
+func (e ConversionError) Error() string {
+ var output string
+
+ if e.Index < 0 {
+ output = fmt.Sprintf("schema: error converting value for %q", e.Key)
+ } else {
+ output = fmt.Sprintf("schema: error converting value for index %d of %q",
+ e.Index, e.Key)
+ }
+
+ if e.Err != nil {
+ output = fmt.Sprintf("%s. Details: %s", output, e.Err)
+ }
+
+ return output
+}
+
+// MultiError stores multiple decoding errors.
+//
+// Borrowed from the App Engine SDK.
+type MultiError map[string]error
+
+func (e MultiError) Error() string {
+ s := ""
+ for _, err := range e {
+ s = err.Error()
+ break
+ }
+ switch len(e) {
+ case 0:
+ return "(0 errors)"
+ case 1:
+ return s
+ case 2:
+ return s + " (and 1 other error)"
+ }
+ return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1)
+}
diff --git a/vendor/github.com/gorilla/schema/decoder_test.go b/vendor/github.com/gorilla/schema/decoder_test.go
new file mode 100644
index 000000000..18b3b3270
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/decoder_test.go
@@ -0,0 +1,1693 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package schema
+
+import (
+ "encoding/hex"
+ "errors"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+type IntAlias int
+
+type rudeBool bool
+
+func (id *rudeBool) UnmarshalText(text []byte) error {
+ value := string(text)
+ switch {
+ case strings.EqualFold("Yup", value):
+ *id = true
+ case strings.EqualFold("Nope", value):
+ *id = false
+ default:
+ return errors.New("value must be yup or nope")
+ }
+ return nil
+}
+
+// All cases we want to cover, in a nutshell.
+type S1 struct {
+ F01 int `schema:"f1"`
+ F02 *int `schema:"f2"`
+ F03 []int `schema:"f3"`
+ F04 []*int `schema:"f4"`
+ F05 *[]int `schema:"f5"`
+ F06 *[]*int `schema:"f6"`
+ F07 S2 `schema:"f7"`
+ F08 *S1 `schema:"f8"`
+ F09 int `schema:"-"`
+ F10 []S1 `schema:"f10"`
+ F11 []*S1 `schema:"f11"`
+ F12 *[]S1 `schema:"f12"`
+ F13 *[]*S1 `schema:"f13"`
+ F14 int `schema:"f14"`
+ F15 IntAlias `schema:"f15"`
+ F16 []IntAlias `schema:"f16"`
+ F17 S19 `schema:"f17"`
+ F18 rudeBool `schema:"f18"`
+ F19 *rudeBool `schema:"f19"`
+ F20 []rudeBool `schema:"f20"`
+ F21 []*rudeBool `schema:"f21"`
+}
+
+type S2 struct {
+ F01 *[]*int `schema:"f1"`
+}
+
+type S19 [2]byte
+
+func (id *S19) UnmarshalText(text []byte) error {
+ buf, err := hex.DecodeString(string(text))
+ if err != nil {
+ return err
+ }
+ if len(buf) > len(*id) {
+ return errors.New("out of range")
+ }
+ for i := range buf {
+ (*id)[i] = buf[i]
+ }
+ return nil
+}
+
+func TestAll(t *testing.T) {
+ v := map[string][]string{
+ "f1": {"1"},
+ "f2": {"2"},
+ "f3": {"31", "32"},
+ "f4": {"41", "42"},
+ "f5": {"51", "52"},
+ "f6": {"61", "62"},
+ "f7.f1": {"71", "72"},
+ "f8.f8.f7.f1": {"81", "82"},
+ "f9": {"9"},
+ "f10.0.f10.0.f6": {"101", "102"},
+ "f10.0.f10.1.f6": {"103", "104"},
+ "f11.0.f11.0.f6": {"111", "112"},
+ "f11.0.f11.1.f6": {"113", "114"},
+ "f12.0.f12.0.f6": {"121", "122"},
+ "f12.0.f12.1.f6": {"123", "124"},
+ "f13.0.f13.0.f6": {"131", "132"},
+ "f13.0.f13.1.f6": {"133", "134"},
+ "f14": {},
+ "f15": {"151"},
+ "f16": {"161", "162"},
+ "f17": {"1a2b"},
+ "f18": {"yup"},
+ "f19": {"nope"},
+ "f20": {"nope", "yup"},
+ "f21": {"yup", "nope"},
+ }
+ f2 := 2
+ f41, f42 := 41, 42
+ f61, f62 := 61, 62
+ f71, f72 := 71, 72
+ f81, f82 := 81, 82
+ f101, f102, f103, f104 := 101, 102, 103, 104
+ f111, f112, f113, f114 := 111, 112, 113, 114
+ f121, f122, f123, f124 := 121, 122, 123, 124
+ f131, f132, f133, f134 := 131, 132, 133, 134
+ var f151 IntAlias = 151
+ var f161, f162 IntAlias = 161, 162
+ var f152, f153 rudeBool = true, false
+ e := S1{
+ F01: 1,
+ F02: &f2,
+ F03: []int{31, 32},
+ F04: []*int{&f41, &f42},
+ F05: &[]int{51, 52},
+ F06: &[]*int{&f61, &f62},
+ F07: S2{
+ F01: &[]*int{&f71, &f72},
+ },
+ F08: &S1{
+ F08: &S1{
+ F07: S2{
+ F01: &[]*int{&f81, &f82},
+ },
+ },
+ },
+ F09: 0,
+ F10: []S1{
+ S1{
+ F10: []S1{
+ S1{F06: &[]*int{&f101, &f102}},
+ S1{F06: &[]*int{&f103, &f104}},
+ },
+ },
+ },
+ F11: []*S1{
+ &S1{
+ F11: []*S1{
+ &S1{F06: &[]*int{&f111, &f112}},
+ &S1{F06: &[]*int{&f113, &f114}},
+ },
+ },
+ },
+ F12: &[]S1{
+ S1{
+ F12: &[]S1{
+ S1{F06: &[]*int{&f121, &f122}},
+ S1{F06: &[]*int{&f123, &f124}},
+ },
+ },
+ },
+ F13: &[]*S1{
+ &S1{
+ F13: &[]*S1{
+ &S1{F06: &[]*int{&f131, &f132}},
+ &S1{F06: &[]*int{&f133, &f134}},
+ },
+ },
+ },
+ F14: 0,
+ F15: f151,
+ F16: []IntAlias{f161, f162},
+ F17: S19{0x1a, 0x2b},
+ F18: f152,
+ F19: &f153,
+ F20: []rudeBool{f153, f152},
+ F21: []*rudeBool{&f152, &f153},
+ }
+
+ s := &S1{}
+ _ = NewDecoder().Decode(s, v)
+
+ vals := func(values []*int) []int {
+ r := make([]int, len(values))
+ for k, v := range values {
+ r[k] = *v
+ }
+ return r
+ }
+
+ if s.F01 != e.F01 {
+ t.Errorf("f1: expected %v, got %v", e.F01, s.F01)
+ }
+ if s.F02 == nil {
+ t.Errorf("f2: expected %v, got nil", *e.F02)
+ } else if *s.F02 != *e.F02 {
+ t.Errorf("f2: expected %v, got %v", *e.F02, *s.F02)
+ }
+ if s.F03 == nil {
+ t.Errorf("f3: expected %v, got nil", e.F03)
+ } else if len(s.F03) != 2 || s.F03[0] != e.F03[0] || s.F03[1] != e.F03[1] {
+ t.Errorf("f3: expected %v, got %v", e.F03, s.F03)
+ }
+ if s.F04 == nil {
+ t.Errorf("f4: expected %v, got nil", e.F04)
+ } else {
+ if len(s.F04) != 2 || *(s.F04)[0] != *(e.F04)[0] || *(s.F04)[1] != *(e.F04)[1] {
+ t.Errorf("f4: expected %v, got %v", vals(e.F04), vals(s.F04))
+ }
+ }
+ if s.F05 == nil {
+ t.Errorf("f5: expected %v, got nil", e.F05)
+ } else {
+ sF05, eF05 := *s.F05, *e.F05
+ if len(sF05) != 2 || sF05[0] != eF05[0] || sF05[1] != eF05[1] {
+ t.Errorf("f5: expected %v, got %v", eF05, sF05)
+ }
+ }
+ if s.F06 == nil {
+ t.Errorf("f6: expected %v, got nil", vals(*e.F06))
+ } else {
+ sF06, eF06 := *s.F06, *e.F06
+ if len(sF06) != 2 || *(sF06)[0] != *(eF06)[0] || *(sF06)[1] != *(eF06)[1] {
+ t.Errorf("f6: expected %v, got %v", vals(eF06), vals(sF06))
+ }
+ }
+ if s.F07.F01 == nil {
+ t.Errorf("f7.f1: expected %v, got nil", vals(*e.F07.F01))
+ } else {
+ sF07, eF07 := *s.F07.F01, *e.F07.F01
+ if len(sF07) != 2 || *(sF07)[0] != *(eF07)[0] || *(sF07)[1] != *(eF07)[1] {
+ t.Errorf("f7.f1: expected %v, got %v", vals(eF07), vals(sF07))
+ }
+ }
+ if s.F08 == nil {
+ t.Errorf("f8: got nil")
+ } else if s.F08.F08 == nil {
+ t.Errorf("f8.f8: got nil")
+ } else if s.F08.F08.F07.F01 == nil {
+ t.Errorf("f8.f8.f7.f1: expected %v, got nil", vals(*e.F08.F08.F07.F01))
+ } else {
+ sF08, eF08 := *s.F08.F08.F07.F01, *e.F08.F08.F07.F01
+ if len(sF08) != 2 || *(sF08)[0] != *(eF08)[0] || *(sF08)[1] != *(eF08)[1] {
+ t.Errorf("f8.f8.f7.f1: expected %v, got %v", vals(eF08), vals(sF08))
+ }
+ }
+ if s.F09 != e.F09 {
+ t.Errorf("f9: expected %v, got %v", e.F09, s.F09)
+ }
+ if s.F10 == nil {
+ t.Errorf("f10: got nil")
+ } else if len(s.F10) != 1 {
+ t.Errorf("f10: expected 1 element, got %v", s.F10)
+ } else {
+ if len(s.F10[0].F10) != 2 {
+ t.Errorf("f10.0.f10: expected 1 element, got %v", s.F10[0].F10)
+ } else {
+ sF10, eF10 := *s.F10[0].F10[0].F06, *e.F10[0].F10[0].F06
+ if sF10 == nil {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
+ } else {
+ if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
+ }
+ }
+ sF10, eF10 = *s.F10[0].F10[1].F06, *e.F10[0].F10[1].F06
+ if sF10 == nil {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
+ } else {
+ if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
+ }
+ }
+ }
+ }
+ if s.F11 == nil {
+ t.Errorf("f11: got nil")
+ } else if len(s.F11) != 1 {
+ t.Errorf("f11: expected 1 element, got %v", s.F11)
+ } else {
+ if len(s.F11[0].F11) != 2 {
+ t.Errorf("f11.0.f11: expected 1 element, got %v", s.F11[0].F11)
+ } else {
+ sF11, eF11 := *s.F11[0].F11[0].F06, *e.F11[0].F11[0].F06
+ if sF11 == nil {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
+ } else {
+ if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
+ }
+ }
+ sF11, eF11 = *s.F11[0].F11[1].F06, *e.F11[0].F11[1].F06
+ if sF11 == nil {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
+ } else {
+ if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
+ }
+ }
+ }
+ }
+ if s.F12 == nil {
+ t.Errorf("f12: got nil")
+ } else if len(*s.F12) != 1 {
+ t.Errorf("f12: expected 1 element, got %v", *s.F12)
+ } else {
+ sF12, eF12 := *(s.F12), *(e.F12)
+ if len(*sF12[0].F12) != 2 {
+ t.Errorf("f12.0.f12: expected 1 element, got %v", *sF12[0].F12)
+ } else {
+ sF122, eF122 := *(*sF12[0].F12)[0].F06, *(*eF12[0].F12)[0].F06
+ if sF122 == nil {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
+ } else {
+ if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
+ }
+ }
+ sF122, eF122 = *(*sF12[0].F12)[1].F06, *(*eF12[0].F12)[1].F06
+ if sF122 == nil {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
+ } else {
+ if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
+ }
+ }
+ }
+ }
+ if s.F13 == nil {
+ t.Errorf("f13: got nil")
+ } else if len(*s.F13) != 1 {
+ t.Errorf("f13: expected 1 element, got %v", *s.F13)
+ } else {
+ sF13, eF13 := *(s.F13), *(e.F13)
+ if len(*sF13[0].F13) != 2 {
+ t.Errorf("f13.0.f13: expected 1 element, got %v", *sF13[0].F13)
+ } else {
+ sF132, eF132 := *(*sF13[0].F13)[0].F06, *(*eF13[0].F13)[0].F06
+ if sF132 == nil {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
+ } else {
+ if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
+ }
+ }
+ sF132, eF132 = *(*sF13[0].F13)[1].F06, *(*eF13[0].F13)[1].F06
+ if sF132 == nil {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
+ } else {
+ if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
+ }
+ }
+ }
+ }
+ if s.F14 != e.F14 {
+ t.Errorf("f14: expected %v, got %v", e.F14, s.F14)
+ }
+ if s.F15 != e.F15 {
+ t.Errorf("f15: expected %v, got %v", e.F15, s.F15)
+ }
+ if s.F16 == nil {
+ t.Errorf("f16: nil")
+ } else if len(s.F16) != len(e.F16) {
+ t.Errorf("f16: expected len %d, got %d", len(e.F16), len(s.F16))
+ } else if !reflect.DeepEqual(s.F16, e.F16) {
+ t.Errorf("f16: expected %v, got %v", e.F16, s.F16)
+ }
+ if s.F17 != e.F17 {
+ t.Errorf("f17: expected %v, got %v", e.F17, s.F17)
+ }
+ if s.F18 != e.F18 {
+ t.Errorf("f18: expected %v, got %v", e.F18, s.F18)
+ }
+ if *s.F19 != *e.F19 {
+ t.Errorf("f19: expected %v, got %v", *e.F19, *s.F19)
+ }
+ if s.F20 == nil {
+ t.Errorf("f20: nil")
+ } else if len(s.F20) != len(e.F20) {
+ t.Errorf("f20: expected %v, got %v", e.F20, s.F20)
+ } else if !reflect.DeepEqual(s.F20, e.F20) {
+ t.Errorf("f20: expected %v, got %v", e.F20, s.F20)
+ }
+ if s.F21 == nil {
+ t.Errorf("f21: nil")
+ } else if len(s.F21) != len(e.F21) {
+ t.Errorf("f21: expected length %d, got %d", len(e.F21), len(s.F21))
+ } else if !reflect.DeepEqual(s.F21, e.F21) {
+ t.Errorf("f21: expected %v, got %v", e.F21, s.F21)
+ }
+}
+
+func BenchmarkAll(b *testing.B) {
+ v := map[string][]string{
+ "f1": {"1"},
+ "f2": {"2"},
+ "f3": {"31", "32"},
+ "f4": {"41", "42"},
+ "f5": {"51", "52"},
+ "f6": {"61", "62"},
+ "f7.f1": {"71", "72"},
+ "f8.f8.f7.f1": {"81", "82"},
+ "f9": {"9"},
+ "f10.0.f10.0.f6": {"101", "102"},
+ "f10.0.f10.1.f6": {"103", "104"},
+ "f11.0.f11.0.f6": {"111", "112"},
+ "f11.0.f11.1.f6": {"113", "114"},
+ "f12.0.f12.0.f6": {"121", "122"},
+ "f12.0.f12.1.f6": {"123", "124"},
+ "f13.0.f13.0.f6": {"131", "132"},
+ "f13.0.f13.1.f6": {"133", "134"},
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ s := &S1{}
+ _ = NewDecoder().Decode(s, v)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S3 struct {
+ F01 bool
+ F02 float32
+ F03 float64
+ F04 int
+ F05 int8
+ F06 int16
+ F07 int32
+ F08 int64
+ F09 string
+ F10 uint
+ F11 uint8
+ F12 uint16
+ F13 uint32
+ F14 uint64
+}
+
+func TestDefaultConverters(t *testing.T) {
+ v := map[string][]string{
+ "F01": {"true"},
+ "F02": {"4.2"},
+ "F03": {"4.3"},
+ "F04": {"-42"},
+ "F05": {"-43"},
+ "F06": {"-44"},
+ "F07": {"-45"},
+ "F08": {"-46"},
+ "F09": {"foo"},
+ "F10": {"42"},
+ "F11": {"43"},
+ "F12": {"44"},
+ "F13": {"45"},
+ "F14": {"46"},
+ }
+ e := S3{
+ F01: true,
+ F02: 4.2,
+ F03: 4.3,
+ F04: -42,
+ F05: -43,
+ F06: -44,
+ F07: -45,
+ F08: -46,
+ F09: "foo",
+ F10: 42,
+ F11: 43,
+ F12: 44,
+ F13: 45,
+ F14: 46,
+ }
+ s := &S3{}
+ _ = NewDecoder().Decode(s, v)
+ if s.F01 != e.F01 {
+ t.Errorf("F01: expected %v, got %v", e.F01, s.F01)
+ }
+ if s.F02 != e.F02 {
+ t.Errorf("F02: expected %v, got %v", e.F02, s.F02)
+ }
+ if s.F03 != e.F03 {
+ t.Errorf("F03: expected %v, got %v", e.F03, s.F03)
+ }
+ if s.F04 != e.F04 {
+ t.Errorf("F04: expected %v, got %v", e.F04, s.F04)
+ }
+ if s.F05 != e.F05 {
+ t.Errorf("F05: expected %v, got %v", e.F05, s.F05)
+ }
+ if s.F06 != e.F06 {
+ t.Errorf("F06: expected %v, got %v", e.F06, s.F06)
+ }
+ if s.F07 != e.F07 {
+ t.Errorf("F07: expected %v, got %v", e.F07, s.F07)
+ }
+ if s.F08 != e.F08 {
+ t.Errorf("F08: expected %v, got %v", e.F08, s.F08)
+ }
+ if s.F09 != e.F09 {
+ t.Errorf("F09: expected %v, got %v", e.F09, s.F09)
+ }
+ if s.F10 != e.F10 {
+ t.Errorf("F10: expected %v, got %v", e.F10, s.F10)
+ }
+ if s.F11 != e.F11 {
+ t.Errorf("F11: expected %v, got %v", e.F11, s.F11)
+ }
+ if s.F12 != e.F12 {
+ t.Errorf("F12: expected %v, got %v", e.F12, s.F12)
+ }
+ if s.F13 != e.F13 {
+ t.Errorf("F13: expected %v, got %v", e.F13, s.F13)
+ }
+ if s.F14 != e.F14 {
+ t.Errorf("F14: expected %v, got %v", e.F14, s.F14)
+ }
+}
+
+func TestOn(t *testing.T) {
+ v := map[string][]string{
+ "F01": {"on"},
+ }
+ s := S3{}
+ err := NewDecoder().Decode(&s, v)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !s.F01 {
+ t.Fatal("Value was not set to true")
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+func TestInlineStruct(t *testing.T) {
+ s1 := &struct {
+ F01 bool
+ }{}
+ s2 := &struct {
+ F01 int
+ }{}
+ v1 := map[string][]string{
+ "F01": {"true"},
+ }
+ v2 := map[string][]string{
+ "F01": {"42"},
+ }
+ decoder := NewDecoder()
+ _ = decoder.Decode(s1, v1)
+ if s1.F01 != true {
+ t.Errorf("s1: expected %v, got %v", true, s1.F01)
+ }
+ _ = decoder.Decode(s2, v2)
+ if s2.F01 != 42 {
+ t.Errorf("s2: expected %v, got %v", 42, s2.F01)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type Foo struct {
+ F01 int
+ F02 Bar
+ Bif []Baz
+}
+
+type Bar struct {
+ F01 string
+ F02 string
+ F03 string
+ F14 string
+ S05 string
+ Str string
+}
+
+type Baz struct {
+ F99 []string
+}
+
+func TestSimpleExample(t *testing.T) {
+ data := map[string][]string{
+ "F01": {"1"},
+ "F02.F01": {"S1"},
+ "F02.F02": {"S2"},
+ "F02.F03": {"S3"},
+ "F02.F14": {"S4"},
+ "F02.S05": {"S5"},
+ "F02.Str": {"Str"},
+ "Bif.0.F99": {"A", "B", "C"},
+ }
+
+ e := &Foo{
+ F01: 1,
+ F02: Bar{
+ F01: "S1",
+ F02: "S2",
+ F03: "S3",
+ F14: "S4",
+ S05: "S5",
+ Str: "Str",
+ },
+ Bif: []Baz{{
+ F99: []string{"A", "B", "C"}},
+ },
+ }
+
+ s := &Foo{}
+ _ = NewDecoder().Decode(s, data)
+
+ if s.F01 != e.F01 {
+ t.Errorf("F01: expected %v, got %v", e.F01, s.F01)
+ }
+ if s.F02.F01 != e.F02.F01 {
+ t.Errorf("F02.F01: expected %v, got %v", e.F02.F01, s.F02.F01)
+ }
+ if s.F02.F02 != e.F02.F02 {
+ t.Errorf("F02.F02: expected %v, got %v", e.F02.F02, s.F02.F02)
+ }
+ if s.F02.F03 != e.F02.F03 {
+ t.Errorf("F02.F03: expected %v, got %v", e.F02.F03, s.F02.F03)
+ }
+ if s.F02.F14 != e.F02.F14 {
+ t.Errorf("F02.F14: expected %v, got %v", e.F02.F14, s.F02.F14)
+ }
+ if s.F02.S05 != e.F02.S05 {
+ t.Errorf("F02.S05: expected %v, got %v", e.F02.S05, s.F02.S05)
+ }
+ if s.F02.Str != e.F02.Str {
+ t.Errorf("F02.Str: expected %v, got %v", e.F02.Str, s.F02.Str)
+ }
+ if len(s.Bif) != len(e.Bif) {
+ t.Errorf("Bif len: expected %d, got %d", len(e.Bif), len(s.Bif))
+ } else {
+ if len(s.Bif[0].F99) != len(e.Bif[0].F99) {
+ t.Errorf("Bif[0].F99 len: expected %d, got %d", len(e.Bif[0].F99), len(s.Bif[0].F99))
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S4 struct {
+ F01 int64
+ F02 float64
+ F03 bool
+ F04 rudeBool
+}
+
+func TestConversionError(t *testing.T) {
+ data := map[string][]string{
+ "F01": {"foo"},
+ "F02": {"bar"},
+ "F03": {"baz"},
+ "F04": {"not-a-yes-or-nope"},
+ }
+ s := &S4{}
+ e := NewDecoder().Decode(s, data)
+
+ m := e.(MultiError)
+ if len(m) != 4 {
+ t.Errorf("Expected 3 errors, got %v", m)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S5 struct {
+ F01 []string
+}
+
+func TestEmptyValue(t *testing.T) {
+ data := map[string][]string{
+ "F01": {"", "foo"},
+ }
+ s := &S5{}
+ NewDecoder().Decode(s, data)
+ if len(s.F01) != 1 {
+ t.Errorf("Expected 1 values in F01")
+ }
+}
+
+func TestEmptyValueZeroEmpty(t *testing.T) {
+ data := map[string][]string{
+ "F01": {"", "foo"},
+ }
+ s := S5{}
+ d := NewDecoder()
+ d.ZeroEmpty(true)
+ err := d.Decode(&s, data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(s.F01) != 2 {
+ t.Errorf("Expected 1 values in F01")
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S6 struct {
+ id string
+}
+
+func TestUnexportedField(t *testing.T) {
+ data := map[string][]string{
+ "id": {"identifier"},
+ }
+ s := &S6{}
+ NewDecoder().Decode(s, data)
+ if s.id != "" {
+ t.Errorf("Unexported field expected to be ignored")
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S7 struct {
+ ID string
+}
+
+func TestMultipleValues(t *testing.T) {
+ data := map[string][]string{
+ "ID": {"0", "1"},
+ }
+
+ s := S7{}
+ NewDecoder().Decode(&s, data)
+ if s.ID != "1" {
+ t.Errorf("Last defined value must be used when multiple values for same field are provided")
+ }
+}
+
+type S8 struct {
+ ID string `json:"id"`
+}
+
+func TestSetAliasTag(t *testing.T) {
+ data := map[string][]string{
+ "id": {"foo"},
+ }
+
+ s := S8{}
+ dec := NewDecoder()
+ dec.SetAliasTag("json")
+ dec.Decode(&s, data)
+ if s.ID != "foo" {
+ t.Fatalf("Bad value: got %q, want %q", s.ID, "foo")
+ }
+}
+
+func TestZeroEmpty(t *testing.T) {
+ data := map[string][]string{
+ "F01": {""},
+ "F03": {"true"},
+ }
+ s := S4{1, 1, false, false}
+ d := NewDecoder()
+ d.ZeroEmpty(true)
+
+ err := d.Decode(&s, data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if s.F01 != 0 {
+ t.Errorf("F01: got %v, want %v", s.F01, 0)
+ }
+ if s.F02 != 1 {
+ t.Errorf("F02: got %v, want %v", s.F02, 1)
+ }
+ if s.F03 != true {
+ t.Errorf("F03: got %v, want %v", s.F03, true)
+ }
+}
+
+func TestNoZeroEmpty(t *testing.T) {
+ data := map[string][]string{
+ "F01": {""},
+ "F03": {"true"},
+ }
+ s := S4{1, 1, false, false}
+ d := NewDecoder()
+ d.ZeroEmpty(false)
+ err := d.Decode(&s, data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if s.F01 != 1 {
+ t.Errorf("F01: got %v, want %v", s.F01, 1)
+ }
+ if s.F02 != 1 {
+ t.Errorf("F02: got %v, want %v", s.F02, 1)
+ }
+ if s.F03 != true {
+ t.Errorf("F03: got %v, want %v", s.F03, true)
+ }
+ if s.F04 != false {
+ t.Errorf("F04: got %v, want %v", s.F04, false)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S9 struct {
+ Id string
+}
+
+type S10 struct {
+ S9
+}
+
+func TestEmbeddedField(t *testing.T) {
+ data := map[string][]string{
+ "Id": {"identifier"},
+ }
+ s := &S10{}
+ NewDecoder().Decode(s, data)
+ if s.Id != "identifier" {
+ t.Errorf("Missing support for embedded fields")
+ }
+}
+
+type S11 struct {
+ S10
+}
+
+func TestMultipleLevelEmbeddedField(t *testing.T) {
+ data := map[string][]string{
+ "Id": {"identifier"},
+ }
+ s := &S11{}
+ err := NewDecoder().Decode(s, data)
+ if s.Id != "identifier" {
+ t.Errorf("Missing support for multiple-level embedded fields (%v)", err)
+ }
+}
+
+func TestInvalidPath(t *testing.T) {
+ data := map[string][]string{
+ "Foo.Bar": {"baz"},
+ }
+ s := S9{}
+ err := NewDecoder().Decode(&s, data)
+ expectedErr := `schema: invalid path "Foo.Bar"`
+ if err.Error() != expectedErr {
+ t.Fatalf("got %q, want %q", err, expectedErr)
+ }
+}
+
+func TestInvalidPathIgnoreUnknownKeys(t *testing.T) {
+ data := map[string][]string{
+ "Foo.Bar": {"baz"},
+ }
+ s := S9{}
+ dec := NewDecoder()
+ dec.IgnoreUnknownKeys(true)
+ err := dec.Decode(&s, data)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S1NT struct {
+ F1 int
+ F2 *int
+ F3 []int
+ F4 []*int
+ F5 *[]int
+ F6 *[]*int
+ F7 S2
+ F8 *S1
+ F9 int `schema:"-"`
+ F10 []S1
+ F11 []*S1
+ F12 *[]S1
+ F13 *[]*S1
+}
+
+func TestAllNT(t *testing.T) {
+ v := map[string][]string{
+ "f1": {"1"},
+ "f2": {"2"},
+ "f3": {"31", "32"},
+ "f4": {"41", "42"},
+ "f5": {"51", "52"},
+ "f6": {"61", "62"},
+ "f7.f1": {"71", "72"},
+ "f8.f8.f7.f1": {"81", "82"},
+ "f9": {"9"},
+ "f10.0.f10.0.f6": {"101", "102"},
+ "f10.0.f10.1.f6": {"103", "104"},
+ "f11.0.f11.0.f6": {"111", "112"},
+ "f11.0.f11.1.f6": {"113", "114"},
+ "f12.0.f12.0.f6": {"121", "122"},
+ "f12.0.f12.1.f6": {"123", "124"},
+ "f13.0.f13.0.f6": {"131", "132"},
+ "f13.0.f13.1.f6": {"133", "134"},
+ }
+ f2 := 2
+ f41, f42 := 41, 42
+ f61, f62 := 61, 62
+ f71, f72 := 71, 72
+ f81, f82 := 81, 82
+ f101, f102, f103, f104 := 101, 102, 103, 104
+ f111, f112, f113, f114 := 111, 112, 113, 114
+ f121, f122, f123, f124 := 121, 122, 123, 124
+ f131, f132, f133, f134 := 131, 132, 133, 134
+ e := S1NT{
+ F1: 1,
+ F2: &f2,
+ F3: []int{31, 32},
+ F4: []*int{&f41, &f42},
+ F5: &[]int{51, 52},
+ F6: &[]*int{&f61, &f62},
+ F7: S2{
+ F01: &[]*int{&f71, &f72},
+ },
+ F8: &S1{
+ F08: &S1{
+ F07: S2{
+ F01: &[]*int{&f81, &f82},
+ },
+ },
+ },
+ F9: 0,
+ F10: []S1{
+ S1{
+ F10: []S1{
+ S1{F06: &[]*int{&f101, &f102}},
+ S1{F06: &[]*int{&f103, &f104}},
+ },
+ },
+ },
+ F11: []*S1{
+ &S1{
+ F11: []*S1{
+ &S1{F06: &[]*int{&f111, &f112}},
+ &S1{F06: &[]*int{&f113, &f114}},
+ },
+ },
+ },
+ F12: &[]S1{
+ S1{
+ F12: &[]S1{
+ S1{F06: &[]*int{&f121, &f122}},
+ S1{F06: &[]*int{&f123, &f124}},
+ },
+ },
+ },
+ F13: &[]*S1{
+ &S1{
+ F13: &[]*S1{
+ &S1{F06: &[]*int{&f131, &f132}},
+ &S1{F06: &[]*int{&f133, &f134}},
+ },
+ },
+ },
+ }
+
+ s := &S1NT{}
+ _ = NewDecoder().Decode(s, v)
+
+ vals := func(values []*int) []int {
+ r := make([]int, len(values))
+ for k, v := range values {
+ r[k] = *v
+ }
+ return r
+ }
+
+ if s.F1 != e.F1 {
+ t.Errorf("f1: expected %v, got %v", e.F1, s.F1)
+ }
+ if s.F2 == nil {
+ t.Errorf("f2: expected %v, got nil", *e.F2)
+ } else if *s.F2 != *e.F2 {
+ t.Errorf("f2: expected %v, got %v", *e.F2, *s.F2)
+ }
+ if s.F3 == nil {
+ t.Errorf("f3: expected %v, got nil", e.F3)
+ } else if len(s.F3) != 2 || s.F3[0] != e.F3[0] || s.F3[1] != e.F3[1] {
+ t.Errorf("f3: expected %v, got %v", e.F3, s.F3)
+ }
+ if s.F4 == nil {
+ t.Errorf("f4: expected %v, got nil", e.F4)
+ } else {
+ if len(s.F4) != 2 || *(s.F4)[0] != *(e.F4)[0] || *(s.F4)[1] != *(e.F4)[1] {
+ t.Errorf("f4: expected %v, got %v", vals(e.F4), vals(s.F4))
+ }
+ }
+ if s.F5 == nil {
+ t.Errorf("f5: expected %v, got nil", e.F5)
+ } else {
+ sF5, eF5 := *s.F5, *e.F5
+ if len(sF5) != 2 || sF5[0] != eF5[0] || sF5[1] != eF5[1] {
+ t.Errorf("f5: expected %v, got %v", eF5, sF5)
+ }
+ }
+ if s.F6 == nil {
+ t.Errorf("f6: expected %v, got nil", vals(*e.F6))
+ } else {
+ sF6, eF6 := *s.F6, *e.F6
+ if len(sF6) != 2 || *(sF6)[0] != *(eF6)[0] || *(sF6)[1] != *(eF6)[1] {
+ t.Errorf("f6: expected %v, got %v", vals(eF6), vals(sF6))
+ }
+ }
+ if s.F7.F01 == nil {
+ t.Errorf("f7.f1: expected %v, got nil", vals(*e.F7.F01))
+ } else {
+ sF7, eF7 := *s.F7.F01, *e.F7.F01
+ if len(sF7) != 2 || *(sF7)[0] != *(eF7)[0] || *(sF7)[1] != *(eF7)[1] {
+ t.Errorf("f7.f1: expected %v, got %v", vals(eF7), vals(sF7))
+ }
+ }
+ if s.F8 == nil {
+ t.Errorf("f8: got nil")
+ } else if s.F8.F08 == nil {
+ t.Errorf("f8.f8: got nil")
+ } else if s.F8.F08.F07.F01 == nil {
+ t.Errorf("f8.f8.f7.f1: expected %v, got nil", vals(*e.F8.F08.F07.F01))
+ } else {
+ sF8, eF8 := *s.F8.F08.F07.F01, *e.F8.F08.F07.F01
+ if len(sF8) != 2 || *(sF8)[0] != *(eF8)[0] || *(sF8)[1] != *(eF8)[1] {
+ t.Errorf("f8.f8.f7.f1: expected %v, got %v", vals(eF8), vals(sF8))
+ }
+ }
+ if s.F9 != e.F9 {
+ t.Errorf("f9: expected %v, got %v", e.F9, s.F9)
+ }
+ if s.F10 == nil {
+ t.Errorf("f10: got nil")
+ } else if len(s.F10) != 1 {
+ t.Errorf("f10: expected 1 element, got %v", s.F10)
+ } else {
+ if len(s.F10[0].F10) != 2 {
+ t.Errorf("f10.0.f10: expected 1 element, got %v", s.F10[0].F10)
+ } else {
+ sF10, eF10 := *s.F10[0].F10[0].F06, *e.F10[0].F10[0].F06
+ if sF10 == nil {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
+ } else {
+ if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
+ }
+ }
+ sF10, eF10 = *s.F10[0].F10[1].F06, *e.F10[0].F10[1].F06
+ if sF10 == nil {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
+ } else {
+ if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
+ t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
+ }
+ }
+ }
+ }
+ if s.F11 == nil {
+ t.Errorf("f11: got nil")
+ } else if len(s.F11) != 1 {
+ t.Errorf("f11: expected 1 element, got %v", s.F11)
+ } else {
+ if len(s.F11[0].F11) != 2 {
+ t.Errorf("f11.0.f11: expected 1 element, got %v", s.F11[0].F11)
+ } else {
+ sF11, eF11 := *s.F11[0].F11[0].F06, *e.F11[0].F11[0].F06
+ if sF11 == nil {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
+ } else {
+ if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
+ }
+ }
+ sF11, eF11 = *s.F11[0].F11[1].F06, *e.F11[0].F11[1].F06
+ if sF11 == nil {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
+ } else {
+ if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
+ t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
+ }
+ }
+ }
+ }
+ if s.F12 == nil {
+ t.Errorf("f12: got nil")
+ } else if len(*s.F12) != 1 {
+ t.Errorf("f12: expected 1 element, got %v", *s.F12)
+ } else {
+ sF12, eF12 := *(s.F12), *(e.F12)
+ if len(*sF12[0].F12) != 2 {
+ t.Errorf("f12.0.f12: expected 1 element, got %v", *sF12[0].F12)
+ } else {
+ sF122, eF122 := *(*sF12[0].F12)[0].F06, *(*eF12[0].F12)[0].F06
+ if sF122 == nil {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
+ } else {
+ if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
+ }
+ }
+ sF122, eF122 = *(*sF12[0].F12)[1].F06, *(*eF12[0].F12)[1].F06
+ if sF122 == nil {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
+ } else {
+ if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
+ t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
+ }
+ }
+ }
+ }
+ if s.F13 == nil {
+ t.Errorf("f13: got nil")
+ } else if len(*s.F13) != 1 {
+ t.Errorf("f13: expected 1 element, got %v", *s.F13)
+ } else {
+ sF13, eF13 := *(s.F13), *(e.F13)
+ if len(*sF13[0].F13) != 2 {
+ t.Errorf("f13.0.f13: expected 1 element, got %v", *sF13[0].F13)
+ } else {
+ sF132, eF132 := *(*sF13[0].F13)[0].F06, *(*eF13[0].F13)[0].F06
+ if sF132 == nil {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
+ } else {
+ if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
+ }
+ }
+ sF132, eF132 = *(*sF13[0].F13)[1].F06, *(*eF13[0].F13)[1].F06
+ if sF132 == nil {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
+ } else {
+ if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
+ t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
+ }
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S12A struct {
+ ID []int
+}
+
+func TestCSVSlice(t *testing.T) {
+ data := map[string][]string{
+ "ID": {"0,1"},
+ }
+
+ s := S12A{}
+ NewDecoder().Decode(&s, data)
+ if len(s.ID) != 2 {
+ t.Errorf("Expected two values in the result list, got %+v", s.ID)
+ }
+ if s.ID[0] != 0 || s.ID[1] != 1 {
+ t.Errorf("Expected []{0, 1} got %+v", s)
+ }
+}
+
+type S12B struct {
+ ID []string
+}
+
+//Decode should not split on , into a slice for string only
+func TestCSVStringSlice(t *testing.T) {
+ data := map[string][]string{
+ "ID": {"0,1"},
+ }
+
+ s := S12B{}
+ NewDecoder().Decode(&s, data)
+ if len(s.ID) != 1 {
+ t.Errorf("Expected one value in the result list, got %+v", s.ID)
+ }
+ if s.ID[0] != "0,1" {
+ t.Errorf("Expected []{0, 1} got %+v", s)
+ }
+}
+
+//Invalid data provided by client should not panic (github issue 33)
+func TestInvalidDataProvidedByClient(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Errorf("Panicked calling decoder.Decode: %v", r)
+ }
+ }()
+
+ type S struct {
+ f string
+ }
+
+ data := map[string][]string{
+ "f.f": {"v"},
+ }
+
+ err := NewDecoder().Decode(new(S), data)
+ if err == nil {
+ t.Errorf("invalid path in decoder.Decode should return an error.")
+ }
+}
+
+// underlying cause of error in issue 33
+func TestInvalidPathInCacheParsePath(t *testing.T) {
+ type S struct {
+ f string
+ }
+
+ typ := reflect.ValueOf(new(S)).Elem().Type()
+ c := newCache()
+ _, err := c.parsePath("f.f", typ)
+ if err == nil {
+ t.Errorf("invalid path in cache.parsePath should return an error.")
+ }
+}
+
+// issue 32
+func TestDecodeToTypedField(t *testing.T) {
+ type Aa bool
+ s1 := &struct{ Aa }{}
+ v1 := map[string][]string{"Aa": {"true"}}
+ NewDecoder().Decode(s1, v1)
+ if s1.Aa != Aa(true) {
+ t.Errorf("s1: expected %v, got %v", true, s1.Aa)
+ }
+}
+
+// issue 37
+func TestRegisterConverter(t *testing.T) {
+ type Aa int
+ type Bb int
+ s1 := &struct {
+ Aa
+ Bb
+ }{}
+ decoder := NewDecoder()
+
+ decoder.RegisterConverter(s1.Aa, func(s string) reflect.Value { return reflect.ValueOf(1) })
+ decoder.RegisterConverter(s1.Bb, func(s string) reflect.Value { return reflect.ValueOf(2) })
+
+ v1 := map[string][]string{"Aa": {"4"}, "Bb": {"5"}}
+ decoder.Decode(s1, v1)
+
+ if s1.Aa != Aa(1) {
+ t.Errorf("s1.Aa: expected %v, got %v", 1, s1.Aa)
+ }
+ if s1.Bb != Bb(2) {
+ t.Errorf("s1.Bb: expected %v, got %v", 2, s1.Bb)
+ }
+}
+
+// Issue #40
+func TestRegisterConverterSlice(t *testing.T) {
+ decoder := NewDecoder()
+ decoder.RegisterConverter([]string{}, func(input string) reflect.Value {
+ return reflect.ValueOf(strings.Split(input, ","))
+ })
+
+ result := struct {
+ Multiple []string `schema:"multiple"`
+ }{}
+
+ expected := []string{"one", "two", "three"}
+ decoder.Decode(&result, map[string][]string{
+ "multiple": []string{"one,two,three"},
+ })
+ for i := range expected {
+ if got, want := expected[i], result.Multiple[i]; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }
+}
+
+func TestRegisterConverterMap(t *testing.T) {
+ decoder := NewDecoder()
+ decoder.IgnoreUnknownKeys(false)
+ decoder.RegisterConverter(map[string]string{}, func(input string) reflect.Value {
+ m := make(map[string]string)
+ for _, pair := range strings.Split(input, ",") {
+ parts := strings.Split(pair, ":")
+ switch len(parts) {
+ case 2:
+ m[parts[0]] = parts[1]
+ }
+ }
+ return reflect.ValueOf(m)
+ })
+
+ result := struct {
+ Multiple map[string]string `schema:"multiple"`
+ }{}
+
+ err := decoder.Decode(&result, map[string][]string{
+ "multiple": []string{"a:one,b:two"},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ expected := map[string]string{"a": "one", "b": "two"}
+ for k, v := range expected {
+ got, ok := result.Multiple[k]
+ if !ok {
+ t.Fatalf("got %v, want %v", result.Multiple, expected)
+ }
+ if got != v {
+ t.Errorf("got %s, want %s", got, v)
+ }
+ }
+}
+
+type S13 struct {
+ Value []S14
+}
+
+type S14 struct {
+ F1 string
+ F2 string
+}
+
+func (n *S14) UnmarshalText(text []byte) error {
+ textParts := strings.Split(string(text), " ")
+ if len(textParts) < 2 {
+ return errors.New("Not a valid name!")
+ }
+
+ n.F1, n.F2 = textParts[0], textParts[len(textParts)-1]
+ return nil
+}
+
+type S15 struct {
+ Value []S16
+}
+
+type S16 struct {
+ F1 string
+ F2 string
+}
+
+func TestCustomTypeSlice(t *testing.T) {
+ data := map[string][]string{
+ "Value.0": []string{"Louisa May Alcott"},
+ "Value.1": []string{"Florence Nightingale"},
+ "Value.2": []string{"Clara Barton"},
+ }
+
+ s := S13{}
+ decoder := NewDecoder()
+
+ if err := decoder.Decode(&s, data); err != nil {
+ t.Fatal(err)
+ }
+
+ if len(s.Value) != 3 {
+ t.Fatalf("Expected 3 values in the result list, got %+v", s.Value)
+ }
+ if s.Value[0].F1 != "Louisa" || s.Value[0].F2 != "Alcott" {
+ t.Errorf("Expected S14{'Louisa', 'Alcott'} got %+v", s.Value[0])
+ }
+ if s.Value[1].F1 != "Florence" || s.Value[1].F2 != "Nightingale" {
+ t.Errorf("Expected S14{'Florence', 'Nightingale'} got %+v", s.Value[1])
+ }
+ if s.Value[2].F1 != "Clara" || s.Value[2].F2 != "Barton" {
+ t.Errorf("Expected S14{'Clara', 'Barton'} got %+v", s.Value[2])
+ }
+}
+
+func TestCustomTypeSliceWithError(t *testing.T) {
+ data := map[string][]string{
+ "Value.0": []string{"Louisa May Alcott"},
+ "Value.1": []string{"Florence Nightingale"},
+ "Value.2": []string{"Clara"},
+ }
+
+ s := S13{}
+ decoder := NewDecoder()
+
+ if err := decoder.Decode(&s, data); err == nil {
+ t.Error("Not detecting error in conversion")
+ }
+}
+
+func TestNoTextUnmarshalerTypeSlice(t *testing.T) {
+ data := map[string][]string{
+ "Value.0": []string{"Louisa May Alcott"},
+ "Value.1": []string{"Florence Nightingale"},
+ "Value.2": []string{"Clara Barton"},
+ }
+
+ s := S15{}
+ decoder := NewDecoder()
+
+ if err := decoder.Decode(&s, data); err == nil {
+ t.Error("Not detecting when there's no converter")
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+type S17 struct {
+ Value S14
+}
+
+type S18 struct {
+ Value S16
+}
+
+func TestCustomType(t *testing.T) {
+ data := map[string][]string{
+ "Value": []string{"Louisa May Alcott"},
+ }
+
+ s := S17{}
+ decoder := NewDecoder()
+
+ if err := decoder.Decode(&s, data); err != nil {
+ t.Fatal(err)
+ }
+
+ if s.Value.F1 != "Louisa" || s.Value.F2 != "Alcott" {
+ t.Errorf("Expected S14{'Louisa', 'Alcott'} got %+v", s.Value)
+ }
+}
+
+func TestCustomTypeWithError(t *testing.T) {
+ data := map[string][]string{
+ "Value": []string{"Louisa"},
+ }
+
+ s := S17{}
+ decoder := NewDecoder()
+
+ if err := decoder.Decode(&s, data); err == nil {
+ t.Error("Not detecting error in conversion")
+ }
+}
+
+func TestNoTextUnmarshalerType(t *testing.T) {
+ data := map[string][]string{
+ "Value": []string{"Louisa May Alcott"},
+ }
+
+ s := S18{}
+ decoder := NewDecoder()
+
+ if err := decoder.Decode(&s, data); err == nil {
+ t.Error("Not detecting when there's no converter")
+ }
+}
+
+func TestExpectedType(t *testing.T) {
+ data := map[string][]string{
+ "bools": []string{"1", "a"},
+ "date": []string{"invalid"},
+ "Foo.Bar": []string{"a", "b"},
+ }
+
+ type B struct {
+ Bar *int
+ }
+ type A struct {
+ Bools []bool `schema:"bools"`
+ Date time.Time `schema:"date"`
+ Foo B
+ }
+
+ a := A{}
+
+ err := NewDecoder().Decode(&a, data)
+
+ e := err.(MultiError)["bools"].(ConversionError)
+ if e.Type != reflect.TypeOf(false) && e.Index == 1 {
+ t.Errorf("Expected bool, index: 1 got %+v, index: %d", e.Type, e.Index)
+ }
+ e = err.(MultiError)["date"].(ConversionError)
+ if e.Type != reflect.TypeOf(time.Time{}) {
+ t.Errorf("Expected time.Time got %+v", e.Type)
+ }
+ e = err.(MultiError)["Foo.Bar"].(ConversionError)
+ if e.Type != reflect.TypeOf(0) {
+ t.Errorf("Expected int got %+v", e.Type)
+ }
+}
+
+type R1 struct {
+ A string `schema:"a,required"`
+ B struct {
+ C int `schema:"c,required"`
+ D float64 `schema:"d"`
+ E string `schema:"e,required"`
+ } `schema:"b"`
+ F []string `schema:"f,required"`
+ G []int `schema:"g,othertag"`
+ H bool `schema:"h,required"`
+}
+
+func TestRequiredField(t *testing.T) {
+ var a R1
+ v := map[string][]string{
+ "a": []string{"bbb"},
+ "b.c": []string{"88"},
+ "b.d": []string{"9"},
+ "f": []string{""},
+ "h": []string{"true"},
+ }
+ err := NewDecoder().Decode(&a, v)
+ if err == nil {
+ t.Errorf("error nil, b.e is empty expect")
+ return
+ }
+ // b.e empty
+ v["b.e"] = []string{""} // empty string
+ err = NewDecoder().Decode(&a, v)
+ if err == nil {
+ t.Errorf("error nil, b.e is empty expect")
+ return
+ }
+
+ // all fields ok
+ v["b.e"] = []string{"nonempty"}
+ err = NewDecoder().Decode(&a, v)
+ if err != nil {
+ t.Errorf("error: %v", err)
+ return
+ }
+
+ // set f empty
+ v["f"] = []string{}
+ err = NewDecoder().Decode(&a, v)
+ if err == nil {
+ t.Errorf("error nil, f is empty expect")
+ return
+ }
+ v["f"] = []string{"nonempty"}
+
+ // b.c type int with empty string
+ v["b.c"] = []string{""}
+ err = NewDecoder().Decode(&a, v)
+ if err == nil {
+ t.Errorf("error nil, b.c is empty expect")
+ return
+ }
+ v["b.c"] = []string{"3"}
+
+ // h type bool with empty string
+ v["h"] = []string{""}
+ err = NewDecoder().Decode(&a, v)
+ if err == nil {
+ t.Errorf("error nil, h is empty expect")
+ return
+ }
+}
+
+type AS1 struct {
+ A int32 `schema:"a,required"`
+ E int32 `schema:"e,required"`
+}
+type AS2 struct {
+ AS1
+ B string `schema:"b,required"`
+}
+type AS3 struct {
+ C int32 `schema:"c"`
+}
+
+type AS4 struct {
+ AS3
+ D string `schema:"d"`
+}
+
+func TestAnonymousStructField(t *testing.T) {
+ patterns := []map[string][]string{
+ {
+ "a": {"1"},
+ "e": {"2"},
+ "b": {"abc"},
+ },
+ {
+ "AS1.a": {"1"},
+ "AS1.e": {"2"},
+ "b": {"abc"},
+ },
+ }
+ for _, v := range patterns {
+ a := AS2{}
+ err := NewDecoder().Decode(&a, v)
+ if err != nil {
+ t.Errorf("Decode failed %s, %#v", err, v)
+ continue
+ }
+ if a.A != 1 {
+ t.Errorf("A: expected %v, got %v", 1, a.A)
+ }
+ if a.E != 2 {
+ t.Errorf("E: expected %v, got %v", 2, a.E)
+ }
+ if a.B != "abc" {
+ t.Errorf("B: expected %v, got %v", "abc", a.B)
+ }
+ if a.AS1.A != 1 {
+ t.Errorf("AS1.A: expected %v, got %v", 1, a.AS1.A)
+ }
+ if a.AS1.E != 2 {
+ t.Errorf("AS1.E: expected %v, got %v", 2, a.AS1.E)
+ }
+ }
+ a := AS2{}
+ err := NewDecoder().Decode(&a, map[string][]string{
+ "e": {"2"},
+ "b": {"abc"},
+ })
+ if err == nil {
+ t.Errorf("error nil, a is empty expect")
+ }
+ patterns = []map[string][]string{
+ {
+ "c": {"1"},
+ "d": {"abc"},
+ },
+ {
+ "AS3.c": {"1"},
+ "d": {"abc"},
+ },
+ }
+ for _, v := range patterns {
+ a := AS4{}
+ err := NewDecoder().Decode(&a, v)
+ if err != nil {
+ t.Errorf("Decode failed %s, %#v", err, v)
+ continue
+ }
+ if a.C != 1 {
+ t.Errorf("C: expected %v, got %v", 1, a.C)
+ }
+ if a.D != "abc" {
+ t.Errorf("D: expected %v, got %v", "abc", a.D)
+ }
+ if a.AS3.C != 1 {
+ t.Errorf("AS3.C: expected %v, got %v", 1, a.AS3.C)
+ }
+ }
+}
+
+// Test to ensure that a registered converter overrides the default text unmarshaler.
+func TestRegisterConverterOverridesTextUnmarshaler(t *testing.T) {
+ type MyTime time.Time
+ s1 := &struct {
+ MyTime
+ }{}
+ decoder := NewDecoder()
+
+ ts := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
+ decoder.RegisterConverter(s1.MyTime, func(s string) reflect.Value { return reflect.ValueOf(ts) })
+
+ v1 := map[string][]string{"MyTime": {"4"}, "Bb": {"5"}}
+ decoder.Decode(s1, v1)
+
+ if s1.MyTime != MyTime(ts) {
+ t.Errorf("s1.Aa: expected %v, got %v", ts, s1.MyTime)
+ }
+}
+
+type S20E string
+
+func (e *S20E) UnmarshalText(text []byte) error {
+ *e = S20E("x")
+ return nil
+}
+
+type S20 []S20E
+
+func (s *S20) UnmarshalText(text []byte) error {
+ *s = S20{"a", "b", "c"}
+ return nil
+}
+
+// Test to ensure that when a custom type based on a slice implements an
+// encoding.TextUnmarshaler interface that it takes precedence over any
+// implementations by its elements.
+func TestTextUnmarshalerTypeSlice(t *testing.T) {
+ data := map[string][]string{
+ "Value": []string{"a,b,c"},
+ }
+ s := struct {
+ Value S20
+ }{}
+ decoder := NewDecoder()
+ if err := decoder.Decode(&s, data); err != nil {
+ t.Error("Error while decoding:", err)
+ }
+ expected := S20{"a", "b", "c"}
+ if !reflect.DeepEqual(expected, s.Value) {
+ t.Errorf("Expected %v errors, got %v", expected, s.Value)
+ }
+}
diff --git a/vendor/github.com/gorilla/schema/doc.go b/vendor/github.com/gorilla/schema/doc.go
new file mode 100644
index 000000000..aae9f33f9
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/doc.go
@@ -0,0 +1,148 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package gorilla/schema fills a struct with form values.
+
+The basic usage is really simple. Given this struct:
+
+ type Person struct {
+ Name string
+ Phone string
+ }
+
+...we can fill it passing a map to the Decode() function:
+
+ values := map[string][]string{
+ "Name": {"John"},
+ "Phone": {"999-999-999"},
+ }
+ person := new(Person)
+ decoder := schema.NewDecoder()
+ decoder.Decode(person, values)
+
+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:
+
+ func MyHandler(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseForm()
+
+ if err != nil {
+ // Handle error
+ }
+
+ decoder := schema.NewDecoder()
+ // r.PostForm is a map of our POST form values
+ err := decoder.Decode(person, r.PostForm)
+
+ if err != nil {
+ // Handle error
+ }
+
+ // Do something with person.Name or person.Phone
+ }
+
+Note: it is a good idea to set a Decoder instance as a package global,
+because it caches meta-data about structs, and an instance can be shared safely:
+
+ var decoder = schema.NewDecoder()
+
+To define custom names for fields, use a struct tag "schema". To not populate
+certain fields, use a dash for the name and it will be ignored:
+
+ type Person struct {
+ Name string `schema:"name"` // custom name
+ Phone string `schema:"phone"` // custom name
+ Admin bool `schema:"-"` // this field is never set
+ }
+
+The supported field types in the destination struct are:
+
+ * bool
+ * float variants (float32, float64)
+ * int variants (int, int8, int16, int32, int64)
+ * string
+ * uint variants (uint, uint8, uint16, uint32, uint64)
+ * struct
+ * a pointer to one of the above types
+ * a slice or a pointer to a slice of one of the above types
+
+Non-supported types are simply ignored, however custom types can be registered
+to be converted.
+
+To fill nested structs, keys must use a dotted notation as the "path" for the
+field. So for example, to fill the struct Person below:
+
+ type Phone struct {
+ Label string
+ Number string
+ }
+
+ type Person struct {
+ Name string
+ Phone Phone
+ }
+
+...the source map must have the keys "Name", "Phone.Label" and "Phone.Number".
+This means that an HTML form to fill a Person struct must look like this:
+
+ <form>
+ <input type="text" name="Name">
+ <input type="text" name="Phone.Label">
+ <input type="text" name="Phone.Number">
+ </form>
+
+Single values are filled using the first value for a key from the source map.
+Slices are filled using all values for a key from the source map. So to fill
+a Person with multiple Phone values, like:
+
+ type Person struct {
+ Name string
+ Phones []Phone
+ }
+
+...an HTML form that accepts three Phone values would look like this:
+
+ <form>
+ <input type="text" name="Name">
+ <input type="text" name="Phones.0.Label">
+ <input type="text" name="Phones.0.Number">
+ <input type="text" name="Phones.1.Label">
+ <input type="text" name="Phones.1.Number">
+ <input type="text" name="Phones.2.Label">
+ <input type="text" name="Phones.2.Number">
+ </form>
+
+Notice that only for slices of structs the slice index is required.
+This is needed for disambiguation: if the nested struct also had a slice
+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 register
+a converter, like:
+
+ type Person struct {
+ Emails []Email
+ }
+
+ type Email struct {
+ *mail.Address
+ }
+
+ func (e *Email) UnmarshalText(text []byte) (err error) {
+ e.Address, err = mail.ParseAddress(string(text))
+ return
+ }
+
+...an HTML form that accepts three Email values would look like this:
+
+ <form>
+ <input type="email" name="Emails.0">
+ <input type="email" name="Emails.1">
+ <input type="email" name="Emails.2">
+ </form>
+*/
+package schema
diff --git a/vendor/github.com/gorilla/schema/encoder.go b/vendor/github.com/gorilla/schema/encoder.go
new file mode 100644
index 000000000..bf1d511e6
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/encoder.go
@@ -0,0 +1,195 @@
+package schema
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+type encoderFunc func(reflect.Value) string
+
+// Encoder encodes values from a struct into url.Values.
+type Encoder struct {
+ cache *cache
+ regenc map[reflect.Type]encoderFunc
+}
+
+// NewEncoder returns a new Encoder with defaults.
+func NewEncoder() *Encoder {
+ return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
+}
+
+// Encode encodes a struct into map[string][]string.
+//
+// Intended for use with url.Values.
+func (e *Encoder) Encode(src interface{}, dst map[string][]string) error {
+ v := reflect.ValueOf(src)
+
+ return e.encode(v, dst)
+}
+
+// RegisterEncoder registers a converter for encoding a custom type.
+func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) {
+ e.regenc[reflect.TypeOf(value)] = encoder
+}
+
+// SetAliasTag changes the tag used to locate custom field aliases.
+// The default tag is "schema".
+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()
+ }
+ if v.Kind() != reflect.Struct {
+ return errors.New("schema: interface must be a struct")
+ }
+ t := v.Type()
+
+ errors := MultiError{}
+
+ for i := 0; i < v.NumField(); i++ {
+ name, opts := fieldAlias(t.Field(i), e.cache.tag)
+ if name == "-" {
+ continue
+ }
+
+ // 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
+ }
+
+ encFunc := typeEncoder(v.Field(i).Type(), e.regenc)
+
+ // Encode non-slice types and custom implementations immediately.
+ if encFunc != nil {
+ value := encFunc(v.Field(i))
+ if opts.Contains("omitempty") && isZero(v.Field(i)) {
+ continue
+ }
+
+ dst[name] = append(dst[name], value)
+ 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)
+ }
+
+ if encFunc == nil {
+ errors[v.Field(i).Type().String()] = fmt.Errorf("schema: encoder not found for %v", v.Field(i))
+ continue
+ }
+
+ // Encode a slice.
+ if v.Field(i).Len() == 0 && opts.Contains("omitempty") {
+ continue
+ }
+
+ dst[name] = []string{}
+ for j := 0; j < v.Field(i).Len(); j++ {
+ dst[name] = append(dst[name], encFunc(v.Field(i).Index(j)))
+ }
+ }
+
+ if len(errors) > 0 {
+ return errors
+ }
+ return nil
+}
+
+func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
+ if f, ok := reg[t]; ok {
+ return f
+ }
+
+ switch t.Kind() {
+ case reflect.Bool:
+ return encodeBool
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return encodeInt
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return encodeUint
+ case reflect.Float32:
+ return encodeFloat32
+ case reflect.Float64:
+ return encodeFloat64
+ case reflect.Ptr:
+ f := typeEncoder(t.Elem(), reg)
+ return func(v reflect.Value) string {
+ if v.IsNil() {
+ return "null"
+ }
+ return f(v.Elem())
+ }
+ case reflect.String:
+ return encodeString
+ default:
+ return nil
+ }
+}
+
+func encodeBool(v reflect.Value) string {
+ return strconv.FormatBool(v.Bool())
+}
+
+func encodeInt(v reflect.Value) string {
+ return strconv.FormatInt(int64(v.Int()), 10)
+}
+
+func encodeUint(v reflect.Value) string {
+ return strconv.FormatUint(uint64(v.Uint()), 10)
+}
+
+func encodeFloat(v reflect.Value, bits int) string {
+ return strconv.FormatFloat(v.Float(), 'f', 6, bits)
+}
+
+func encodeFloat32(v reflect.Value) string {
+ return encodeFloat(v, 32)
+}
+
+func encodeFloat64(v reflect.Value) string {
+ return encodeFloat(v, 64)
+}
+
+func encodeString(v reflect.Value) string {
+ return v.String()
+}
diff --git a/vendor/github.com/gorilla/schema/encoder_test.go b/vendor/github.com/gorilla/schema/encoder_test.go
new file mode 100644
index 000000000..ac4cd6137
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/encoder_test.go
@@ -0,0 +1,420 @@
+package schema
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+type E1 struct {
+ F01 int `schema:"f01"`
+ F02 int `schema:"-"`
+ F03 string `schema:"f03"`
+ F04 string `schema:"f04,omitempty"`
+ F05 bool `schema:"f05"`
+ F06 bool `schema:"f06"`
+ F07 *string `schema:"f07"`
+ F08 *int8 `schema:"f08"`
+ F09 float64 `schema:"f09"`
+ F10 func() `schema:"f10"`
+ F11 inner
+}
+type inner struct {
+ F12 int
+}
+
+func TestFilled(t *testing.T) {
+ f07 := "seven"
+ var f08 int8 = 8
+ s := &E1{
+ F01: 1,
+ F02: 2,
+ F03: "three",
+ F04: "four",
+ F05: true,
+ F06: false,
+ F07: &f07,
+ F08: &f08,
+ F09: 1.618,
+ F10: func() {},
+ F11: inner{12},
+ }
+
+ vals := make(map[string][]string)
+ errs := NewEncoder().Encode(s, vals)
+
+ valExists(t, "f01", "1", vals)
+ valNotExists(t, "f02", vals)
+ valExists(t, "f03", "three", vals)
+ valExists(t, "f05", "true", vals)
+ valExists(t, "f06", "false", vals)
+ valExists(t, "f07", "seven", vals)
+ valExists(t, "f08", "8", vals)
+ valExists(t, "f09", "1.618000", vals)
+ valExists(t, "F12", "12", vals)
+
+ emptyErr := MultiError{}
+ if errs.Error() == emptyErr.Error() {
+ t.Errorf("Expected error got %v", errs)
+ }
+}
+
+type Aa int
+
+type E3 struct {
+ F01 bool `schema:"f01"`
+ F02 float32 `schema:"f02"`
+ F03 float64 `schema:"f03"`
+ F04 int `schema:"f04"`
+ F05 int8 `schema:"f05"`
+ F06 int16 `schema:"f06"`
+ F07 int32 `schema:"f07"`
+ F08 int64 `schema:"f08"`
+ F09 string `schema:"f09"`
+ F10 uint `schema:"f10"`
+ F11 uint8 `schema:"f11"`
+ F12 uint16 `schema:"f12"`
+ F13 uint32 `schema:"f13"`
+ F14 uint64 `schema:"f14"`
+ F15 Aa `schema:"f15"`
+}
+
+// Test compatibility with default decoder types.
+func TestCompat(t *testing.T) {
+ src := &E3{
+ F01: true,
+ F02: 4.2,
+ F03: 4.3,
+ F04: -42,
+ F05: -43,
+ F06: -44,
+ F07: -45,
+ F08: -46,
+ F09: "foo",
+ F10: 42,
+ F11: 43,
+ F12: 44,
+ F13: 45,
+ F14: 46,
+ F15: 1,
+ }
+ dst := &E3{}
+
+ vals := make(map[string][]string)
+ encoder := NewEncoder()
+ decoder := NewDecoder()
+
+ encoder.RegisterEncoder(src.F15, func(reflect.Value) string { return "1" })
+ decoder.RegisterConverter(src.F15, func(string) reflect.Value { return reflect.ValueOf(1) })
+
+ err := encoder.Encode(src, vals)
+ if err != nil {
+ t.Errorf("Encoder has non-nil error: %v", err)
+ }
+ err = decoder.Decode(dst, vals)
+ if err != nil {
+ t.Errorf("Decoder has non-nil error: %v", err)
+ }
+
+ if *src != *dst {
+ t.Errorf("Decoder-Encoder compatibility: expected %v, got %v\n", src, dst)
+ }
+}
+
+func TestEmpty(t *testing.T) {
+ s := &E1{
+ F01: 1,
+ F02: 2,
+ F03: "three",
+ }
+
+ estr := "schema: encoder not found for <nil>"
+ vals := make(map[string][]string)
+ err := NewEncoder().Encode(s, vals)
+ if err.Error() != estr {
+ t.Errorf("Expected: %s, got %v", estr, err)
+ }
+
+ valExists(t, "f03", "three", vals)
+ valNotExists(t, "f04", vals)
+}
+
+func TestStruct(t *testing.T) {
+ estr := "schema: interface must be a struct"
+ vals := make(map[string][]string)
+ err := NewEncoder().Encode("hello world", vals)
+
+ if err.Error() != estr {
+ t.Errorf("Expected: %s, got %v", estr, err)
+ }
+}
+
+func TestSlices(t *testing.T) {
+ type oneAsWord int
+ ones := []oneAsWord{1, 2}
+ s1 := &struct {
+ ones []oneAsWord `schema:"ones"`
+ ints []int `schema:"ints"`
+ nonempty []int `schema:"nonempty"`
+ empty []int `schema:"empty,omitempty"`
+ }{ones, []int{1, 1}, []int{}, []int{}}
+ vals := make(map[string][]string)
+
+ encoder := NewEncoder()
+ encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" })
+ err := encoder.Encode(s1, vals)
+ if err != nil {
+ t.Errorf("Encoder has non-nil error: %v", err)
+ }
+
+ valsExist(t, "ones", []string{"one", "one"}, vals)
+ valsExist(t, "ints", []string{"1", "1"}, vals)
+ valsExist(t, "nonempty", []string{}, vals)
+ valNotExists(t, "empty", vals)
+}
+
+func TestCompatSlices(t *testing.T) {
+ type oneAsWord int
+ type s1 struct {
+ Ones []oneAsWord `schema:"ones"`
+ Ints []int `schema:"ints"`
+ }
+ ones := []oneAsWord{1, 1}
+ src := &s1{ones, []int{1, 1}}
+ vals := make(map[string][]string)
+ dst := &s1{}
+
+ encoder := NewEncoder()
+ encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" })
+
+ decoder := NewDecoder()
+ decoder.RegisterConverter(ones[0], func(s string) reflect.Value {
+ if s == "one" {
+ return reflect.ValueOf(1)
+ }
+ return reflect.ValueOf(2)
+ })
+
+ err := encoder.Encode(src, vals)
+ if err != nil {
+ t.Errorf("Encoder has non-nil error: %v", err)
+ }
+ err = decoder.Decode(dst, vals)
+ if err != nil {
+ t.Errorf("Dncoder has non-nil error: %v", err)
+ }
+
+ if len(src.Ints) != len(dst.Ints) || len(src.Ones) != len(src.Ones) {
+ t.Fatalf("Expected %v, got %v", src, dst)
+ }
+
+ for i, v := range src.Ones {
+ if dst.Ones[i] != v {
+ t.Fatalf("Expected %v, got %v", v, dst.Ones[i])
+ }
+ }
+
+ for i, v := range src.Ints {
+ if dst.Ints[i] != v {
+ t.Fatalf("Expected %v, got %v", v, dst.Ints[i])
+ }
+ }
+}
+
+func TestRegisterEncoder(t *testing.T) {
+ type oneAsWord int
+ type twoAsWord int
+ type oneSliceAsWord []int
+
+ s1 := &struct {
+ oneAsWord
+ twoAsWord
+ oneSliceAsWord
+ }{1, 2, []int{1, 1}}
+ v1 := make(map[string][]string)
+
+ encoder := NewEncoder()
+ encoder.RegisterEncoder(s1.oneAsWord, func(v reflect.Value) string { return "one" })
+ encoder.RegisterEncoder(s1.twoAsWord, func(v reflect.Value) string { return "two" })
+ encoder.RegisterEncoder(s1.oneSliceAsWord, func(v reflect.Value) string { return "one" })
+
+ err := encoder.Encode(s1, v1)
+ if err != nil {
+ t.Errorf("Encoder has non-nil error: %v", err)
+ }
+
+ valExists(t, "oneAsWord", "one", v1)
+ valExists(t, "twoAsWord", "two", v1)
+ valExists(t, "oneSliceAsWord", "one", v1)
+}
+
+func TestEncoderOrder(t *testing.T) {
+ type builtinEncoderSimple int
+ type builtinEncoderSimpleOverridden int
+ type builtinEncoderSlice []int
+ type builtinEncoderSliceOverridden []int
+ type builtinEncoderStruct struct{ nr int }
+ type builtinEncoderStructOverridden struct{ nr int }
+
+ s1 := &struct {
+ builtinEncoderSimple `schema:"simple"`
+ builtinEncoderSimpleOverridden `schema:"simple_overridden"`
+ builtinEncoderSlice `schema:"slice"`
+ builtinEncoderSliceOverridden `schema:"slice_overridden"`
+ builtinEncoderStruct `schema:"struct"`
+ builtinEncoderStructOverridden `schema:"struct_overridden"`
+ }{
+ 1,
+ 1,
+ []int{2},
+ []int{2},
+ builtinEncoderStruct{3},
+ builtinEncoderStructOverridden{3},
+ }
+ v1 := make(map[string][]string)
+
+ encoder := NewEncoder()
+ encoder.RegisterEncoder(s1.builtinEncoderSimpleOverridden, func(v reflect.Value) string { return "one" })
+ encoder.RegisterEncoder(s1.builtinEncoderSliceOverridden, func(v reflect.Value) string { return "two" })
+ encoder.RegisterEncoder(s1.builtinEncoderStructOverridden, func(v reflect.Value) string { return "three" })
+
+ err := encoder.Encode(s1, v1)
+ if err != nil {
+ t.Errorf("Encoder has non-nil error: %v", err)
+ }
+
+ valExists(t, "simple", "1", v1)
+ valExists(t, "simple_overridden", "one", v1)
+ valExists(t, "slice", "2", v1)
+ valExists(t, "slice_overridden", "two", v1)
+ valExists(t, "nr", "3", v1)
+ valExists(t, "struct_overridden", "three", v1)
+}
+
+func valExists(t *testing.T, key string, expect string, result map[string][]string) {
+ valsExist(t, key, []string{expect}, result)
+}
+
+func valsExist(t *testing.T, key string, expect []string, result map[string][]string) {
+ vals, ok := result[key]
+ if !ok {
+ t.Fatalf("Key not found. Expected: %s", key)
+ }
+
+ if len(expect) != len(vals) {
+ t.Fatalf("Expected: %v, got: %v", expect, vals)
+ }
+
+ for i, v := range expect {
+ if vals[i] != v {
+ t.Fatalf("Unexpected value. Expected: %v, got %v", v, vals[i])
+ }
+ }
+}
+
+func valNotExists(t *testing.T, key string, result map[string][]string) {
+ if val, ok := result[key]; ok {
+ t.Error("Key not ommited. Expected: empty; got: " + val[0] + ".")
+ }
+}
+
+type E4 struct {
+ ID string `json:"id"`
+}
+
+func TestEncoderSetAliasTag(t *testing.T) {
+ data := map[string][]string{}
+
+ s := E4{
+ ID: "foo",
+ }
+ encoder := NewEncoder()
+ encoder.SetAliasTag("json")
+ encoder.Encode(&s, data)
+ valExists(t, "id", "foo", data)
+}
+
+type E5 struct {
+ F01 int `schema:"f01,omitempty"`
+ F02 string `schema:"f02,omitempty"`
+ F03 *string `schema:"f03,omitempty"`
+ F04 *int8 `schema:"f04,omitempty"`
+ F05 float64 `schema:"f05,omitempty"`
+ F06 E5F06 `schema:"f06,omitempty"`
+ F07 E5F06 `schema:"f07,omitempty"`
+ F08 []string `schema:"f08,omitempty"`
+ F09 []string `schema:"f09,omitempty"`
+}
+
+type E5F06 struct {
+ F0601 string `schema:"f0601,omitempty"`
+}
+
+func TestEncoderWithOmitempty(t *testing.T) {
+ vals := map[string][]string{}
+
+ s := E5{
+ F02: "test",
+ F07: E5F06{
+ F0601: "test",
+ },
+ F09: []string{"test"},
+ }
+
+ encoder := NewEncoder()
+ encoder.Encode(&s, vals)
+
+ valNotExists(t, "f01", vals)
+ valExists(t, "f02", "test", vals)
+ valNotExists(t, "f03", vals)
+ valNotExists(t, "f04", vals)
+ valNotExists(t, "f05", vals)
+ valNotExists(t, "f06", vals)
+ valExists(t, "f0601", "test", vals)
+ valNotExists(t, "f08", vals)
+ valsExist(t, "f09", []string{"test"}, vals)
+}
+
+type E6 struct {
+ F01 *inner
+ F02 *inner
+ F03 *inner `schema:",omitempty"`
+}
+
+func TestStructPointer(t *testing.T) {
+ vals := map[string][]string{}
+ s := E6{
+ F01: &inner{2},
+ }
+
+ encoder := NewEncoder()
+ encoder.Encode(&s, vals)
+ valExists(t, "F12", "2", vals)
+ valExists(t, "F02", "null", vals)
+ valNotExists(t, "F03", vals)
+}
+
+func TestRegisterEncoderCustomArrayType(t *testing.T) {
+ type CustomInt []int
+ type S1 struct {
+ SomeInts CustomInt `schema:",omitempty"`
+ }
+
+ ss := []S1{
+ {},
+ {CustomInt{}},
+ {CustomInt{1, 2, 3}},
+ }
+
+ for s := range ss {
+ vals := map[string][]string{}
+
+ encoder := NewEncoder()
+ encoder.RegisterEncoder(CustomInt{}, func(value reflect.Value) string {
+ return fmt.Sprint(value.Interface())
+ })
+
+ encoder.Encode(s, vals)
+ t.Log(vals)
+ }
+}
diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go
index cd3569d53..b051e9591 100644
--- a/vendor/github.com/gorilla/websocket/conn.go
+++ b/vendor/github.com/gorilla/websocket/conn.go
@@ -1051,8 +1051,9 @@ func (c *Conn) CloseHandler() func(code int, text string) error {
// if the close message is empty. The default close handler sends a close
// message back to the peer.
//
-// The application must read the connection to process close messages as
-// described in the section on Control Messages above.
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// close messages as described in the section on Control Messages above.
//
// The connection read methods return a CloseError when a close message is
// received. Most applications should handle close messages as part of their
@@ -1079,8 +1080,9 @@ func (c *Conn) PingHandler() func(appData string) error {
// The appData argument to h is the PING message application data. The default
// ping handler sends a pong to the peer.
//
-// The application must read the connection to process ping messages as
-// described in the section on Control Messages above.
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// ping messages as described in the section on Control Messages above.
func (c *Conn) SetPingHandler(h func(appData string) error) {
if h == nil {
h = func(message string) error {
@@ -1105,8 +1107,9 @@ func (c *Conn) PongHandler() func(appData string) error {
// The appData argument to h is the PONG message application data. The default
// pong handler does nothing.
//
-// The application must read the connection to process ping messages as
-// described in the section on Control Messages above.
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// pong messages as described in the section on Control Messages above.
func (c *Conn) SetPongHandler(h func(appData string) error) {
if h == nil {
h = func(string) error { return nil }