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 }