1
2
3
4
5 package properties
6
7 import (
8 "fmt"
9 "reflect"
10 "strconv"
11 "strings"
12 "time"
13 )
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
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
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
159
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
243
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
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