diff options
Diffstat (limited to 'vendor/github.com/gorilla/schema')
-rw-r--r-- | vendor/github.com/gorilla/schema/cache.go | 63 | ||||
-rw-r--r-- | vendor/github.com/gorilla/schema/decoder.go | 51 | ||||
-rw-r--r-- | vendor/github.com/gorilla/schema/doc.go | 2 | ||||
-rw-r--r-- | vendor/github.com/gorilla/schema/encoder.go | 161 |
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() +} |