gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

decode.go (8480B)


      1 // Copyright 2013-2022 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 }