diff options
Diffstat (limited to 'vendor/github.com/gorilla')
-rw-r--r-- | vendor/github.com/gorilla/schema/README.md | 16 | ||||
-rw-r--r-- | vendor/github.com/gorilla/schema/cache.go | 79 | ||||
-rw-r--r-- | vendor/github.com/gorilla/schema/decoder.go | 99 |
3 files changed, 142 insertions, 52 deletions
diff --git a/vendor/github.com/gorilla/schema/README.md b/vendor/github.com/gorilla/schema/README.md index 2c3ecd8e..aefdd669 100644 --- a/vendor/github.com/gorilla/schema/README.md +++ b/vendor/github.com/gorilla/schema/README.md @@ -11,7 +11,7 @@ Package gorilla/schema converts structs to and from form values. 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 +// 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() @@ -27,9 +27,9 @@ func MyHandler(w http.ResponseWriter, r *http.Request) { } var person Person - + // r.PostForm is a map of our POST form values - err := decoder.Decode(&person, r.PostForm) + err = decoder.Decode(&person, r.PostForm) if err != nil { // Handle error } @@ -64,9 +64,9 @@ To define custom names for fields, use a struct tag "schema". To not populate ce ```go type Person struct { - Name string `schema:"name"` // custom name - Phone string `schema:"phone"` // custom name - Admin bool `schema:"-"` // this field is never set + Name string `schema:"name,required"` // custom name, must be supplied + Phone string `schema:"phone"` // custom name + Admin bool `schema:"-"` // this field is never set } ``` @@ -83,8 +83,8 @@ The supported field types in the struct are: 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 +More examples are available on the Gorilla website: https://www.gorillatoolkit.org/pkg/schema -## License +## 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 index 73b75f48..0746c120 100644 --- a/vendor/github.com/gorilla/schema/cache.go +++ b/vendor/github.com/gorilla/schema/cache.go @@ -117,7 +117,7 @@ func (c *cache) get(t reflect.Type) *structInfo { info := c.m[t] c.l.RUnlock() if info == nil { - info = c.create(t, nil) + info = c.create(t, "") c.l.Lock() c.m[t] = info c.l.Unlock() @@ -126,37 +126,40 @@ func (c *cache) get(t reflect.Type) *structInfo { } // 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{}} - } +func (c *cache) create(t reflect.Type, parentAlias string) *structInfo { + info := &structInfo{} + var anonymousInfos []*structInfo 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 f := c.createField(t.Field(i), parentAlias); f != nil { + info.fields = append(info.fields, f) + if ft := indirectType(f.typ); ft.Kind() == reflect.Struct && f.isAnonymous { + anonymousInfos = append(anonymousInfos, c.create(ft, f.canonicalAlias)) } - 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.isRequired = false - } + } + } + for i, a := range anonymousInfos { + others := []*structInfo{info} + others = append(others, anonymousInfos[:i]...) + others = append(others, anonymousInfos[i+1:]...) + for _, f := range a.fields { + if !containsAlias(others, f.alias) { + info.fields = append(info.fields, f) } } - c.createField(field, info) } return info } // createField creates a fieldInfo for the given field. -func (c *cache) createField(field reflect.StructField, info *structInfo) { +func (c *cache) createField(field reflect.StructField, parentAlias string) *fieldInfo { alias, options := fieldAlias(field, c.tag) if alias == "-" { // Ignore this field. - return + return nil + } + canonicalAlias := alias + if parentAlias != "" { + canonicalAlias = parentAlias + "." + alias } // Check if the type is supported and don't cache it if not. // First let's get the basic type. @@ -181,19 +184,20 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) { if isStruct = ft.Kind() == reflect.Struct; !isStruct { if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil { // Type is not supported. - return + return nil } } - info.fields = append(info.fields, &fieldInfo{ + return &fieldInfo{ typ: field.Type, name: field.Name, alias: alias, + canonicalAlias: canonicalAlias, unmarshalerInfo: m, isSliceOfStructs: isSlice && isStruct, isAnonymous: field.Anonymous, isRequired: options.Contains("required"), - }) + } } // converter returns the converter for a type. @@ -216,11 +220,26 @@ func (i *structInfo) get(alias string) *fieldInfo { return nil } +func containsAlias(infos []*structInfo, alias string) bool { + for _, info := range infos { + if info.get(alias) != nil { + return true + } + } + return false +} + type fieldInfo struct { typ reflect.Type // name is the field name in the struct. name string alias string + // canonicalAlias is almost the same as the alias, but is prefixed with + // an embedded struct field alias in dotted notation if this field is + // promoted from the struct. + // For instance, if the alias is "N" and this field is an embedded field + // in a struct "X", canonicalAlias will be "X.N". + canonicalAlias string // unmarshalerInfo contains information regarding the // encoding.TextUnmarshaler implementation of the field type. unmarshalerInfo unmarshaler @@ -231,6 +250,13 @@ type fieldInfo struct { isRequired bool } +func (f *fieldInfo) paths(prefix string) []string { + if f.alias == f.canonicalAlias { + return []string{prefix + f.alias} + } + return []string{prefix + f.alias, prefix + f.canonicalAlias} +} + type pathPart struct { field *fieldInfo path []string // path to the field: walks structs using field names. @@ -239,6 +265,13 @@ type pathPart struct { // ---------------------------------------------------------------------------- +func indirectType(typ reflect.Type) reflect.Type { + if typ.Kind() == reflect.Ptr { + return typ.Elem() + } + return typ +} + // 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 != "" { diff --git a/vendor/github.com/gorilla/schema/decoder.go b/vendor/github.com/gorilla/schema/decoder.go index 5352a908..5afbd921 100644 --- a/vendor/github.com/gorilla/schema/decoder.go +++ b/vendor/github.com/gorilla/schema/decoder.go @@ -81,52 +81,83 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error { errors[path] = err } } else if !d.ignoreUnknownKeys { - errors[path] = fmt.Errorf("schema: invalid path %q", path) + errors[path] = UnknownKeyError{Key: path} } } + errors.merge(d.checkRequired(t, src)) if len(errors) > 0 { return errors } - return d.checkRequired(t, src, "") + return nil } // 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 +// check type t recursively if t has struct fields. // // 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 { +func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string) MultiError { + m, errs := d.findRequiredFields(t, "", "") + for key, fields := range m { + if isEmptyFields(fields, src) { + errs[key] = EmptyFieldError{Key: key} + } + } + return errs +} + +// findRequiredFields recursively searches the struct type t for required fields. +// +// canonicalPrefix and searchPrefix are used to resolve full paths in dotted notation +// for nested struct fields. canonicalPrefix is a complete path which never omits +// any embedded struct fields. searchPrefix is a user-friendly path which may omit +// some embedded struct fields to point promoted fields. +func (d *Decoder) findRequiredFields(t reflect.Type, canonicalPrefix, searchPrefix string) (map[string][]fieldWithPrefix, MultiError) { struc := d.cache.get(t) if struc == nil { // unexpect, cache.get never return nil - return errors.New("cache fail") + return nil, MultiError{canonicalPrefix + "*": errors.New("cache fail")} } + m := map[string][]fieldWithPrefix{} + errs := MultiError{} 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.isAnonymous { - return err - } - // check embedded parent field. - err2 := d.checkRequired(f.typ, src, prefix) - if err2 != nil { - return err + fcprefix := canonicalPrefix + f.canonicalAlias + "." + for _, fspath := range f.paths(searchPrefix) { + fm, ferrs := d.findRequiredFields(f.typ, fcprefix, fspath+".") + for key, fields := range fm { + m[key] = append(m[key], fields...) } + errs.merge(ferrs) } } if f.isRequired { - key := f.alias - if prefix != "" { - key = prefix + key - } - if isEmpty(f.typ, src[key]) { - return fmt.Errorf("%v is empty", key) + key := canonicalPrefix + f.canonicalAlias + m[key] = append(m[key], fieldWithPrefix{ + fieldInfo: f, + prefix: searchPrefix, + }) + } + } + return m, errs +} + +type fieldWithPrefix struct { + *fieldInfo + prefix string +} + +// isEmptyFields returns true if all of specified fields are empty. +func isEmptyFields(fields []fieldWithPrefix, src map[string][]string) bool { + for _, f := range fields { + for _, path := range f.paths(f.prefix) { + if !isEmpty(f.typ, src[path]) { + return false } } } - return nil + return true } // isEmpty returns true if value is empty for specific type @@ -424,6 +455,24 @@ func (e ConversionError) Error() string { return output } +// UnknownKeyError stores information about an unknown key in the source map. +type UnknownKeyError struct { + Key string // key from the source map. +} + +func (e UnknownKeyError) Error() string { + return fmt.Sprintf("schema: invalid path %q", e.Key) +} + +// EmptyFieldError stores information about an empty required field. +type EmptyFieldError struct { + Key string // required key in the source map. +} + +func (e EmptyFieldError) Error() string { + return fmt.Sprintf("%v is empty", e.Key) +} + // MultiError stores multiple decoding errors. // // Borrowed from the App Engine SDK. @@ -445,3 +494,11 @@ func (e MultiError) Error() string { } return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1) } + +func (e MultiError) merge(errors MultiError) { + for key, err := range errors { + if e[key] == nil { + e[key] = err + } + } +} |