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/cache.go63
-rw-r--r--vendor/github.com/gorilla/schema/decoder.go51
-rw-r--r--vendor/github.com/gorilla/schema/doc.go2
-rw-r--r--vendor/github.com/gorilla/schema/encoder.go161
4 files changed, 256 insertions, 21 deletions
diff --git a/vendor/github.com/gorilla/schema/cache.go b/vendor/github.com/gorilla/schema/cache.go
index 1613b1e6..b2c5995d 100644
--- a/vendor/github.com/gorilla/schema/cache.go
+++ b/vendor/github.com/gorilla/schema/cache.go
@@ -138,7 +138,12 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
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)
@@ -148,7 +153,7 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
// createField creates a fieldInfo for the given field.
func (c *cache) createField(field reflect.StructField, info *structInfo) {
- alias := fieldAlias(field, c.tag)
+ alias, options := fieldAlias(field, c.tag)
if alias == "-" {
// Ignore this field.
return
@@ -173,17 +178,19 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
}
}
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
- if conv := c.conv[ft.Kind()]; conv == nil {
+ if conv := c.converter(ft); conv == nil {
// Type is not supported.
return
}
}
info.fields = append(info.fields, &fieldInfo{
- typ: field.Type,
- name: field.Name,
- ss: isSlice && isStruct,
- alias: alias,
+ typ: field.Type,
+ name: field.Name,
+ ss: isSlice && isStruct,
+ alias: alias,
+ anon: field.Anonymous,
+ required: options.Contains("required"),
})
}
@@ -212,10 +219,12 @@ func (i *structInfo) get(alias string) *fieldInfo {
}
type fieldInfo struct {
- typ reflect.Type
- name string // field name in the struct.
- ss bool // true if this is a slice of structs.
- alias string
+ 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 {
@@ -227,19 +236,33 @@ type pathPart struct {
// ----------------------------------------------------------------------------
// fieldAlias parses a field tag to get a field alias.
-func fieldAlias(field reflect.StructField, tagName string) string {
- var alias string
+func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
if tag := field.Tag.Get(tagName); tag != "" {
- // For now tags only support the name but let's follow the
- // comma convention from encoding/json and others.
- if idx := strings.Index(tag, ","); idx == -1 {
- alias = tag
- } else {
- alias = tag[:idx]
- }
+ alias, options = parseTag(tag)
}
if alias == "" {
alias = field.Name
}
- return alias
+ 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/decoder.go b/vendor/github.com/gorilla/schema/decoder.go
index 53b0337f..b0f8cb00 100644
--- a/vendor/github.com/gorilla/schema/decoder.go
+++ b/vendor/github.com/gorilla/schema/decoder.go
@@ -87,9 +87,60 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
if len(errors) > 0 {
return errors
}
+ return d.checkRequired(t, src, "")
+}
+
+// checkRequired checks whether requred field 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.
diff --git a/vendor/github.com/gorilla/schema/doc.go b/vendor/github.com/gorilla/schema/doc.go
index 22c0ff4b..a95e87bd 100644
--- a/vendor/github.com/gorilla/schema/doc.go
+++ b/vendor/github.com/gorilla/schema/doc.go
@@ -12,7 +12,7 @@ The basic usage is really simple. Given this struct:
Phone string
}
-...we can fill it passing a map to the Load() function:
+...we can fill it passing a map to the Decode() function:
values := map[string][]string{
"Name": {"John"},
diff --git a/vendor/github.com/gorilla/schema/encoder.go b/vendor/github.com/gorilla/schema/encoder.go
new file mode 100644
index 00000000..aa43a5c9
--- /dev/null
+++ b/vendor/github.com/gorilla/schema/encoder.go
@@ -0,0 +1,161 @@
+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
+}
+
+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
+ }
+
+ if v.Field(i).Type().Kind() == reflect.Struct {
+ e.encode(v.Field(i), 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 value == "" && opts.Contains("omitempty") {
+ continue
+ }
+
+ dst[name] = append(dst[name], value)
+ 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()
+}