...

Source file src/github.com/magiconair/properties/decode.go

Documentation: github.com/magiconair/properties

     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  

View as plain text