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
5package properties
6
7import (
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"`
94func (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
105func 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
244func 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,..."
255func 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
273func isArray(t reflect.Type) bool    { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice }
274func isBool(t reflect.Type) bool     { return t.Kind() == reflect.Bool }
275func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
276func isMap(t reflect.Type) bool      { return t.Kind() == reflect.Map }
277func isPtr(t reflect.Type) bool      { return t.Kind() == reflect.Ptr }
278func isString(t reflect.Type) bool   { return t.Kind() == reflect.String }
279func isStruct(t reflect.Type) bool   { return t.Kind() == reflect.Struct }
280func isTime(t reflect.Type) bool     { return t == reflect.TypeOf(time.Time{}) }
281func isFloat(t reflect.Type) bool {
282	return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
283}
284func 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}
287func 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