OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / magiconair / properties / decode.go
diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go
new file mode 100644 (file)
index 0000000..0a961bb
--- /dev/null
@@ -0,0 +1,289 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+       "fmt"
+       "reflect"
+       "strconv"
+       "strings"
+       "time"
+)
+
+// Decode assigns property values to exported fields of a struct.
+//
+// Decode traverses v recursively and returns an error if a value cannot be
+// converted to the field type or a required value is missing for a field.
+//
+// The following type dependent decodings are used:
+//
+// String, boolean, numeric fields have the value of the property key assigned.
+// The property key name is the name of the field. A different key and a default
+// value can be set in the field's tag. Fields without default value are
+// required. If the value cannot be converted to the field type an error is
+// returned.
+//
+// time.Duration fields have the result of time.ParseDuration() assigned.
+//
+// time.Time fields have the vaule of time.Parse() assigned. The default layout
+// is time.RFC3339 but can be set in the field's tag.
+//
+// Arrays and slices of string, boolean, numeric, time.Duration and time.Time
+// fields have the value interpreted as a comma separated list of values. The
+// individual values are trimmed of whitespace and empty values are ignored. A
+// default value can be provided as a semicolon separated list in the field's
+// tag.
+//
+// Struct fields are decoded recursively using the field name plus "." as
+// prefix. The prefix (without dot) can be overridden in the field's tag.
+// Default values are not supported in the field's tag. Specify them on the
+// fields of the inner struct instead.
+//
+// Map fields must have a key of type string and are decoded recursively by
+// using the field's name plus ".' as prefix and the next element of the key
+// name as map key. The prefix (without dot) can be overridden in the field's
+// tag. Default values are not supported.
+//
+// Examples:
+//
+//     // Field is ignored.
+//     Field int `properties:"-"`
+//
+//     // Field is assigned value of 'Field'.
+//     Field int
+//
+//     // Field is assigned value of 'myName'.
+//     Field int `properties:"myName"`
+//
+//     // Field is assigned value of key 'myName' and has a default
+//     // value 15 if the key does not exist.
+//     Field int `properties:"myName,default=15"`
+//
+//     // Field is assigned value of key 'Field' and has a default
+//     // value 15 if the key does not exist.
+//     Field int `properties:",default=15"`
+//
+//     // Field is assigned value of key 'date' and the date
+//     // is in format 2006-01-02
+//     Field time.Time `properties:"date,layout=2006-01-02"`
+//
+//     // Field is assigned the non-empty and whitespace trimmed
+//     // values of key 'Field' split by commas.
+//     Field []string
+//
+//     // Field is assigned the non-empty and whitespace trimmed
+//     // values of key 'Field' split by commas and has a default
+//     // value ["a", "b", "c"] if the key does not exist.
+//     Field []string `properties:",default=a;b;c"`
+//
+//     // Field is decoded recursively with "Field." as key prefix.
+//     Field SomeStruct
+//
+//     // Field is decoded recursively with "myName." as key prefix.
+//     Field SomeStruct `properties:"myName"`
+//
+//     // Field is decoded recursively with "Field." as key prefix
+//     // and the next dotted element of the key as map key.
+//     Field map[string]string
+//
+//     // Field is decoded recursively with "myName." as key prefix
+//     // and the next dotted element of the key as map key.
+//     Field map[string]string `properties:"myName"`
+func (p *Properties) Decode(x interface{}) error {
+       t, v := reflect.TypeOf(x), reflect.ValueOf(x)
+       if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct {
+               return fmt.Errorf("not a pointer to struct: %s", t)
+       }
+       if err := dec(p, "", nil, nil, v); err != nil {
+               return err
+       }
+       return nil
+}
+
+func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error {
+       t := v.Type()
+
+       // value returns the property value for key or the default if provided.
+       value := func() (string, error) {
+               if val, ok := p.Get(key); ok {
+                       return val, nil
+               }
+               if def != nil {
+                       return *def, nil
+               }
+               return "", fmt.Errorf("missing required key %s", key)
+       }
+
+       // conv converts a string to a value of the given type.
+       conv := func(s string, t reflect.Type) (val reflect.Value, err error) {
+               var v interface{}
+
+               switch {
+               case isDuration(t):
+                       v, err = time.ParseDuration(s)
+
+               case isTime(t):
+                       layout := opts["layout"]
+                       if layout == "" {
+                               layout = time.RFC3339
+                       }
+                       v, err = time.Parse(layout, s)
+
+               case isBool(t):
+                       v, err = boolVal(s), nil
+
+               case isString(t):
+                       v, err = s, nil
+
+               case isFloat(t):
+                       v, err = strconv.ParseFloat(s, 64)
+
+               case isInt(t):
+                       v, err = strconv.ParseInt(s, 10, 64)
+
+               case isUint(t):
+                       v, err = strconv.ParseUint(s, 10, 64)
+
+               default:
+                       return reflect.Zero(t), fmt.Errorf("unsupported type %s", t)
+               }
+               if err != nil {
+                       return reflect.Zero(t), err
+               }
+               return reflect.ValueOf(v).Convert(t), nil
+       }
+
+       // keydef returns the property key and the default value based on the
+       // name of the struct field and the options in the tag.
+       keydef := func(f reflect.StructField) (string, *string, map[string]string) {
+               _key, _opts := parseTag(f.Tag.Get("properties"))
+
+               var _def *string
+               if d, ok := _opts["default"]; ok {
+                       _def = &d
+               }
+               if _key != "" {
+                       return _key, _def, _opts
+               }
+               return f.Name, _def, _opts
+       }
+
+       switch {
+       case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t):
+               s, err := value()
+               if err != nil {
+                       return err
+               }
+               val, err := conv(s, t)
+               if err != nil {
+                       return err
+               }
+               v.Set(val)
+
+       case isPtr(t):
+               return dec(p, key, def, opts, v.Elem())
+
+       case isStruct(t):
+               for i := 0; i < v.NumField(); i++ {
+                       fv := v.Field(i)
+                       fk, def, opts := keydef(t.Field(i))
+                       if !fv.CanSet() {
+                               return fmt.Errorf("cannot set %s", t.Field(i).Name)
+                       }
+                       if fk == "-" {
+                               continue
+                       }
+                       if key != "" {
+                               fk = key + "." + fk
+                       }
+                       if err := dec(p, fk, def, opts, fv); err != nil {
+                               return err
+                       }
+               }
+               return nil
+
+       case isArray(t):
+               val, err := value()
+               if err != nil {
+                       return err
+               }
+               vals := split(val, ";")
+               a := reflect.MakeSlice(t, 0, len(vals))
+               for _, s := range vals {
+                       val, err := conv(s, t.Elem())
+                       if err != nil {
+                               return err
+                       }
+                       a = reflect.Append(a, val)
+               }
+               v.Set(a)
+
+       case isMap(t):
+               valT := t.Elem()
+               m := reflect.MakeMap(t)
+               for postfix := range p.FilterStripPrefix(key + ".").m {
+                       pp := strings.SplitN(postfix, ".", 2)
+                       mk, mv := pp[0], reflect.New(valT)
+                       if err := dec(p, key+"."+mk, nil, nil, mv); err != nil {
+                               return err
+                       }
+                       m.SetMapIndex(reflect.ValueOf(mk), mv.Elem())
+               }
+               v.Set(m)
+
+       default:
+               return fmt.Errorf("unsupported type %s", t)
+       }
+       return nil
+}
+
+// split splits a string on sep, trims whitespace of elements
+// and omits empty elements
+func split(s string, sep string) []string {
+       var a []string
+       for _, v := range strings.Split(s, sep) {
+               if v = strings.TrimSpace(v); v != "" {
+                       a = append(a, v)
+               }
+       }
+       return a
+}
+
+// parseTag parses a "key,k=v,k=v,..."
+func parseTag(tag string) (key string, opts map[string]string) {
+       opts = map[string]string{}
+       for i, s := range strings.Split(tag, ",") {
+               if i == 0 {
+                       key = s
+                       continue
+               }
+
+               pp := strings.SplitN(s, "=", 2)
+               if len(pp) == 1 {
+                       opts[pp[0]] = ""
+               } else {
+                       opts[pp[0]] = pp[1]
+               }
+       }
+       return key, opts
+}
+
+func isArray(t reflect.Type) bool    { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice }
+func isBool(t reflect.Type) bool     { return t.Kind() == reflect.Bool }
+func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
+func isMap(t reflect.Type) bool      { return t.Kind() == reflect.Map }
+func isPtr(t reflect.Type) bool      { return t.Kind() == reflect.Ptr }
+func isString(t reflect.Type) bool   { return t.Kind() == reflect.String }
+func isStruct(t reflect.Type) bool   { return t.Kind() == reflect.Struct }
+func isTime(t reflect.Type) bool     { return t == reflect.TypeOf(time.Time{}) }
+func isFloat(t reflect.Type) bool {
+       return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
+}
+func isInt(t reflect.Type) bool {
+       return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64
+}
+func isUint(t reflect.Type) bool {
+       return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64
+}