summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gorilla/schema
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gorilla/schema')
-rw-r--r--vendor/github.com/gorilla/schema/README.md16
-rw-r--r--vendor/github.com/gorilla/schema/cache.go79
-rw-r--r--vendor/github.com/gorilla/schema/decoder.go99
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
+ }
+ }
+}