decode.go raw

   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  }
 290