vendor/github.com/magiconair/properties/decode.go
changeset 242 2a9ec03fe5a1
child 265 05c40b36d3b2
equal deleted inserted replaced
241:e77dad242f4c 242:2a9ec03fe5a1
       
     1 // Copyright 2018 Frank Schroeder. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style
       
     3 // license that can be found in the LICENSE file.
       
     4 
       
     5 package properties
       
     6 
       
     7 import (
       
     8 	"fmt"
       
     9 	"reflect"
       
    10 	"strconv"
       
    11 	"strings"
       
    12 	"time"
       
    13 )
       
    14 
       
    15 // Decode assigns property values to exported fields of a struct.
       
    16 //
       
    17 // Decode traverses v recursively and returns an error if a value cannot be
       
    18 // converted to the field type or a required value is missing for a field.
       
    19 //
       
    20 // The following type dependent decodings are used:
       
    21 //
       
    22 // String, boolean, numeric fields have the value of the property key assigned.
       
    23 // The property key name is the name of the field. A different key and a default
       
    24 // value can be set in the field's tag. Fields without default value are
       
    25 // required. If the value cannot be converted to the field type an error is
       
    26 // returned.
       
    27 //
       
    28 // time.Duration fields have the result of time.ParseDuration() assigned.
       
    29 //
       
    30 // time.Time fields have the vaule of time.Parse() assigned. The default layout
       
    31 // is time.RFC3339 but can be set in the field's tag.
       
    32 //
       
    33 // Arrays and slices of string, boolean, numeric, time.Duration and time.Time
       
    34 // fields have the value interpreted as a comma separated list of values. The
       
    35 // individual values are trimmed of whitespace and empty values are ignored. A
       
    36 // default value can be provided as a semicolon separated list in the field's
       
    37 // tag.
       
    38 //
       
    39 // Struct fields are decoded recursively using the field name plus "." as
       
    40 // prefix. The prefix (without dot) can be overridden in the field's tag.
       
    41 // Default values are not supported in the field's tag. Specify them on the
       
    42 // fields of the inner struct instead.
       
    43 //
       
    44 // Map fields must have a key of type string and are decoded recursively by
       
    45 // using the field's name plus ".' as prefix and the next element of the key
       
    46 // name as map key. The prefix (without dot) can be overridden in the field's
       
    47 // tag. Default values are not supported.
       
    48 //
       
    49 // Examples:
       
    50 //
       
    51 //     // Field is ignored.
       
    52 //     Field int `properties:"-"`
       
    53 //
       
    54 //     // Field is assigned value of 'Field'.
       
    55 //     Field int
       
    56 //
       
    57 //     // Field is assigned value of 'myName'.
       
    58 //     Field int `properties:"myName"`
       
    59 //
       
    60 //     // Field is assigned value of key 'myName' and has a default
       
    61 //     // value 15 if the key does not exist.
       
    62 //     Field int `properties:"myName,default=15"`
       
    63 //
       
    64 //     // Field is assigned value of key 'Field' and has a default
       
    65 //     // value 15 if the key does not exist.
       
    66 //     Field int `properties:",default=15"`
       
    67 //
       
    68 //     // Field is assigned value of key 'date' and the date
       
    69 //     // is in format 2006-01-02
       
    70 //     Field time.Time `properties:"date,layout=2006-01-02"`
       
    71 //
       
    72 //     // Field is assigned the non-empty and whitespace trimmed
       
    73 //     // values of key 'Field' split by commas.
       
    74 //     Field []string
       
    75 //
       
    76 //     // Field is assigned the non-empty and whitespace trimmed
       
    77 //     // values of key 'Field' split by commas and has a default
       
    78 //     // value ["a", "b", "c"] if the key does not exist.
       
    79 //     Field []string `properties:",default=a;b;c"`
       
    80 //
       
    81 //     // Field is decoded recursively with "Field." as key prefix.
       
    82 //     Field SomeStruct
       
    83 //
       
    84 //     // Field is decoded recursively with "myName." as key prefix.
       
    85 //     Field SomeStruct `properties:"myName"`
       
    86 //
       
    87 //     // Field is decoded recursively with "Field." as key prefix
       
    88 //     // and the next dotted element of the key as map key.
       
    89 //     Field map[string]string
       
    90 //
       
    91 //     // Field is decoded recursively with "myName." as key prefix
       
    92 //     // and the next dotted element of the key as map key.
       
    93 //     Field map[string]string `properties:"myName"`
       
    94 func (p *Properties) Decode(x interface{}) error {
       
    95 	t, v := reflect.TypeOf(x), reflect.ValueOf(x)
       
    96 	if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct {
       
    97 		return fmt.Errorf("not a pointer to struct: %s", t)
       
    98 	}
       
    99 	if err := dec(p, "", nil, nil, v); err != nil {
       
   100 		return err
       
   101 	}
       
   102 	return nil
       
   103 }
       
   104 
       
   105 func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error {
       
   106 	t := v.Type()
       
   107 
       
   108 	// value returns the property value for key or the default if provided.
       
   109 	value := func() (string, error) {
       
   110 		if val, ok := p.Get(key); ok {
       
   111 			return val, nil
       
   112 		}
       
   113 		if def != nil {
       
   114 			return *def, nil
       
   115 		}
       
   116 		return "", fmt.Errorf("missing required key %s", key)
       
   117 	}
       
   118 
       
   119 	// conv converts a string to a value of the given type.
       
   120 	conv := func(s string, t reflect.Type) (val reflect.Value, err error) {
       
   121 		var v interface{}
       
   122 
       
   123 		switch {
       
   124 		case isDuration(t):
       
   125 			v, err = time.ParseDuration(s)
       
   126 
       
   127 		case isTime(t):
       
   128 			layout := opts["layout"]
       
   129 			if layout == "" {
       
   130 				layout = time.RFC3339
       
   131 			}
       
   132 			v, err = time.Parse(layout, s)
       
   133 
       
   134 		case isBool(t):
       
   135 			v, err = boolVal(s), nil
       
   136 
       
   137 		case isString(t):
       
   138 			v, err = s, nil
       
   139 
       
   140 		case isFloat(t):
       
   141 			v, err = strconv.ParseFloat(s, 64)
       
   142 
       
   143 		case isInt(t):
       
   144 			v, err = strconv.ParseInt(s, 10, 64)
       
   145 
       
   146 		case isUint(t):
       
   147 			v, err = strconv.ParseUint(s, 10, 64)
       
   148 
       
   149 		default:
       
   150 			return reflect.Zero(t), fmt.Errorf("unsupported type %s", t)
       
   151 		}
       
   152 		if err != nil {
       
   153 			return reflect.Zero(t), err
       
   154 		}
       
   155 		return reflect.ValueOf(v).Convert(t), nil
       
   156 	}
       
   157 
       
   158 	// keydef returns the property key and the default value based on the
       
   159 	// name of the struct field and the options in the tag.
       
   160 	keydef := func(f reflect.StructField) (string, *string, map[string]string) {
       
   161 		_key, _opts := parseTag(f.Tag.Get("properties"))
       
   162 
       
   163 		var _def *string
       
   164 		if d, ok := _opts["default"]; ok {
       
   165 			_def = &d
       
   166 		}
       
   167 		if _key != "" {
       
   168 			return _key, _def, _opts
       
   169 		}
       
   170 		return f.Name, _def, _opts
       
   171 	}
       
   172 
       
   173 	switch {
       
   174 	case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t):
       
   175 		s, err := value()
       
   176 		if err != nil {
       
   177 			return err
       
   178 		}
       
   179 		val, err := conv(s, t)
       
   180 		if err != nil {
       
   181 			return err
       
   182 		}
       
   183 		v.Set(val)
       
   184 
       
   185 	case isPtr(t):
       
   186 		return dec(p, key, def, opts, v.Elem())
       
   187 
       
   188 	case isStruct(t):
       
   189 		for i := 0; i < v.NumField(); i++ {
       
   190 			fv := v.Field(i)
       
   191 			fk, def, opts := keydef(t.Field(i))
       
   192 			if !fv.CanSet() {
       
   193 				return fmt.Errorf("cannot set %s", t.Field(i).Name)
       
   194 			}
       
   195 			if fk == "-" {
       
   196 				continue
       
   197 			}
       
   198 			if key != "" {
       
   199 				fk = key + "." + fk
       
   200 			}
       
   201 			if err := dec(p, fk, def, opts, fv); err != nil {
       
   202 				return err
       
   203 			}
       
   204 		}
       
   205 		return nil
       
   206 
       
   207 	case isArray(t):
       
   208 		val, err := value()
       
   209 		if err != nil {
       
   210 			return err
       
   211 		}
       
   212 		vals := split(val, ";")
       
   213 		a := reflect.MakeSlice(t, 0, len(vals))
       
   214 		for _, s := range vals {
       
   215 			val, err := conv(s, t.Elem())
       
   216 			if err != nil {
       
   217 				return err
       
   218 			}
       
   219 			a = reflect.Append(a, val)
       
   220 		}
       
   221 		v.Set(a)
       
   222 
       
   223 	case isMap(t):
       
   224 		valT := t.Elem()
       
   225 		m := reflect.MakeMap(t)
       
   226 		for postfix := range p.FilterStripPrefix(key + ".").m {
       
   227 			pp := strings.SplitN(postfix, ".", 2)
       
   228 			mk, mv := pp[0], reflect.New(valT)
       
   229 			if err := dec(p, key+"."+mk, nil, nil, mv); err != nil {
       
   230 				return err
       
   231 			}
       
   232 			m.SetMapIndex(reflect.ValueOf(mk), mv.Elem())
       
   233 		}
       
   234 		v.Set(m)
       
   235 
       
   236 	default:
       
   237 		return fmt.Errorf("unsupported type %s", t)
       
   238 	}
       
   239 	return nil
       
   240 }
       
   241 
       
   242 // split splits a string on sep, trims whitespace of elements
       
   243 // and omits empty elements
       
   244 func split(s string, sep string) []string {
       
   245 	var a []string
       
   246 	for _, v := range strings.Split(s, sep) {
       
   247 		if v = strings.TrimSpace(v); v != "" {
       
   248 			a = append(a, v)
       
   249 		}
       
   250 	}
       
   251 	return a
       
   252 }
       
   253 
       
   254 // parseTag parses a "key,k=v,k=v,..."
       
   255 func parseTag(tag string) (key string, opts map[string]string) {
       
   256 	opts = map[string]string{}
       
   257 	for i, s := range strings.Split(tag, ",") {
       
   258 		if i == 0 {
       
   259 			key = s
       
   260 			continue
       
   261 		}
       
   262 
       
   263 		pp := strings.SplitN(s, "=", 2)
       
   264 		if len(pp) == 1 {
       
   265 			opts[pp[0]] = ""
       
   266 		} else {
       
   267 			opts[pp[0]] = pp[1]
       
   268 		}
       
   269 	}
       
   270 	return key, opts
       
   271 }
       
   272 
       
   273 func isArray(t reflect.Type) bool    { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice }
       
   274 func isBool(t reflect.Type) bool     { return t.Kind() == reflect.Bool }
       
   275 func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
       
   276 func isMap(t reflect.Type) bool      { return t.Kind() == reflect.Map }
       
   277 func isPtr(t reflect.Type) bool      { return t.Kind() == reflect.Ptr }
       
   278 func isString(t reflect.Type) bool   { return t.Kind() == reflect.String }
       
   279 func isStruct(t reflect.Type) bool   { return t.Kind() == reflect.Struct }
       
   280 func isTime(t reflect.Type) bool     { return t == reflect.TypeOf(time.Time{}) }
       
   281 func isFloat(t reflect.Type) bool {
       
   282 	return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
       
   283 }
       
   284 func isInt(t reflect.Type) bool {
       
   285 	return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64
       
   286 }
       
   287 func isUint(t reflect.Type) bool {
       
   288 	return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64
       
   289 }