diff options
Diffstat (limited to 'vendor/gopkg.in/ini.v1/struct.go')
-rw-r--r-- | vendor/gopkg.in/ini.v1/struct.go | 747 |
1 files changed, 0 insertions, 747 deletions
diff --git a/vendor/gopkg.in/ini.v1/struct.go b/vendor/gopkg.in/ini.v1/struct.go deleted file mode 100644 index a486b2f..0000000 --- a/vendor/gopkg.in/ini.v1/struct.go +++ /dev/null | |||
@@ -1,747 +0,0 @@ | |||
1 | // Copyright 2014 Unknwon | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may | ||
4 | // not use this file except in compliance with the License. You may obtain | ||
5 | // a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | // License for the specific language governing permissions and limitations | ||
13 | // under the License. | ||
14 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "bytes" | ||
19 | "errors" | ||
20 | "fmt" | ||
21 | "reflect" | ||
22 | "strings" | ||
23 | "time" | ||
24 | "unicode" | ||
25 | ) | ||
26 | |||
27 | // NameMapper represents a ini tag name mapper. | ||
28 | type NameMapper func(string) string | ||
29 | |||
30 | // Built-in name getters. | ||
31 | var ( | ||
32 | // SnackCase converts to format SNACK_CASE. | ||
33 | SnackCase NameMapper = func(raw string) string { | ||
34 | newstr := make([]rune, 0, len(raw)) | ||
35 | for i, chr := range raw { | ||
36 | if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { | ||
37 | if i > 0 { | ||
38 | newstr = append(newstr, '_') | ||
39 | } | ||
40 | } | ||
41 | newstr = append(newstr, unicode.ToUpper(chr)) | ||
42 | } | ||
43 | return string(newstr) | ||
44 | } | ||
45 | // TitleUnderscore converts to format title_underscore. | ||
46 | TitleUnderscore NameMapper = func(raw string) string { | ||
47 | newstr := make([]rune, 0, len(raw)) | ||
48 | for i, chr := range raw { | ||
49 | if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { | ||
50 | if i > 0 { | ||
51 | newstr = append(newstr, '_') | ||
52 | } | ||
53 | chr -= 'A' - 'a' | ||
54 | } | ||
55 | newstr = append(newstr, chr) | ||
56 | } | ||
57 | return string(newstr) | ||
58 | } | ||
59 | ) | ||
60 | |||
61 | func (s *Section) parseFieldName(raw, actual string) string { | ||
62 | if len(actual) > 0 { | ||
63 | return actual | ||
64 | } | ||
65 | if s.f.NameMapper != nil { | ||
66 | return s.f.NameMapper(raw) | ||
67 | } | ||
68 | return raw | ||
69 | } | ||
70 | |||
71 | func parseDelim(actual string) string { | ||
72 | if len(actual) > 0 { | ||
73 | return actual | ||
74 | } | ||
75 | return "," | ||
76 | } | ||
77 | |||
78 | var reflectTime = reflect.TypeOf(time.Now()).Kind() | ||
79 | |||
80 | // setSliceWithProperType sets proper values to slice based on its type. | ||
81 | func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { | ||
82 | var strs []string | ||
83 | if allowShadow { | ||
84 | strs = key.StringsWithShadows(delim) | ||
85 | } else { | ||
86 | strs = key.Strings(delim) | ||
87 | } | ||
88 | |||
89 | numVals := len(strs) | ||
90 | if numVals == 0 { | ||
91 | return nil | ||
92 | } | ||
93 | |||
94 | var vals interface{} | ||
95 | var err error | ||
96 | |||
97 | sliceOf := field.Type().Elem().Kind() | ||
98 | switch sliceOf { | ||
99 | case reflect.String: | ||
100 | vals = strs | ||
101 | case reflect.Int: | ||
102 | vals, err = key.parseInts(strs, true, false) | ||
103 | case reflect.Int64: | ||
104 | vals, err = key.parseInt64s(strs, true, false) | ||
105 | case reflect.Uint: | ||
106 | vals, err = key.parseUints(strs, true, false) | ||
107 | case reflect.Uint64: | ||
108 | vals, err = key.parseUint64s(strs, true, false) | ||
109 | case reflect.Float64: | ||
110 | vals, err = key.parseFloat64s(strs, true, false) | ||
111 | case reflect.Bool: | ||
112 | vals, err = key.parseBools(strs, true, false) | ||
113 | case reflectTime: | ||
114 | vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false) | ||
115 | default: | ||
116 | return fmt.Errorf("unsupported type '[]%s'", sliceOf) | ||
117 | } | ||
118 | if err != nil && isStrict { | ||
119 | return err | ||
120 | } | ||
121 | |||
122 | slice := reflect.MakeSlice(field.Type(), numVals, numVals) | ||
123 | for i := 0; i < numVals; i++ { | ||
124 | switch sliceOf { | ||
125 | case reflect.String: | ||
126 | slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i])) | ||
127 | case reflect.Int: | ||
128 | slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i])) | ||
129 | case reflect.Int64: | ||
130 | slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i])) | ||
131 | case reflect.Uint: | ||
132 | slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i])) | ||
133 | case reflect.Uint64: | ||
134 | slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) | ||
135 | case reflect.Float64: | ||
136 | slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) | ||
137 | case reflect.Bool: | ||
138 | slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i])) | ||
139 | case reflectTime: | ||
140 | slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) | ||
141 | } | ||
142 | } | ||
143 | field.Set(slice) | ||
144 | return nil | ||
145 | } | ||
146 | |||
147 | func wrapStrictError(err error, isStrict bool) error { | ||
148 | if isStrict { | ||
149 | return err | ||
150 | } | ||
151 | return nil | ||
152 | } | ||
153 | |||
154 | // setWithProperType sets proper value to field based on its type, | ||
155 | // but it does not return error for failing parsing, | ||
156 | // because we want to use default value that is already assigned to struct. | ||
157 | func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { | ||
158 | vt := t | ||
159 | isPtr := t.Kind() == reflect.Ptr | ||
160 | if isPtr { | ||
161 | vt = t.Elem() | ||
162 | } | ||
163 | switch vt.Kind() { | ||
164 | case reflect.String: | ||
165 | stringVal := key.String() | ||
166 | if isPtr { | ||
167 | field.Set(reflect.ValueOf(&stringVal)) | ||
168 | } else if len(stringVal) > 0 { | ||
169 | field.SetString(key.String()) | ||
170 | } | ||
171 | case reflect.Bool: | ||
172 | boolVal, err := key.Bool() | ||
173 | if err != nil { | ||
174 | return wrapStrictError(err, isStrict) | ||
175 | } | ||
176 | if isPtr { | ||
177 | field.Set(reflect.ValueOf(&boolVal)) | ||
178 | } else { | ||
179 | field.SetBool(boolVal) | ||
180 | } | ||
181 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
182 | // ParseDuration will not return err for `0`, so check the type name | ||
183 | if vt.Name() == "Duration" { | ||
184 | durationVal, err := key.Duration() | ||
185 | if err != nil { | ||
186 | if intVal, err := key.Int64(); err == nil { | ||
187 | field.SetInt(intVal) | ||
188 | return nil | ||
189 | } | ||
190 | return wrapStrictError(err, isStrict) | ||
191 | } | ||
192 | if isPtr { | ||
193 | field.Set(reflect.ValueOf(&durationVal)) | ||
194 | } else if int64(durationVal) > 0 { | ||
195 | field.Set(reflect.ValueOf(durationVal)) | ||
196 | } | ||
197 | return nil | ||
198 | } | ||
199 | |||
200 | intVal, err := key.Int64() | ||
201 | if err != nil { | ||
202 | return wrapStrictError(err, isStrict) | ||
203 | } | ||
204 | if isPtr { | ||
205 | pv := reflect.New(t.Elem()) | ||
206 | pv.Elem().SetInt(intVal) | ||
207 | field.Set(pv) | ||
208 | } else { | ||
209 | field.SetInt(intVal) | ||
210 | } | ||
211 | // byte is an alias for uint8, so supporting uint8 breaks support for byte | ||
212 | case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
213 | durationVal, err := key.Duration() | ||
214 | // Skip zero value | ||
215 | if err == nil && uint64(durationVal) > 0 { | ||
216 | if isPtr { | ||
217 | field.Set(reflect.ValueOf(&durationVal)) | ||
218 | } else { | ||
219 | field.Set(reflect.ValueOf(durationVal)) | ||
220 | } | ||
221 | return nil | ||
222 | } | ||
223 | |||
224 | uintVal, err := key.Uint64() | ||
225 | if err != nil { | ||
226 | return wrapStrictError(err, isStrict) | ||
227 | } | ||
228 | if isPtr { | ||
229 | pv := reflect.New(t.Elem()) | ||
230 | pv.Elem().SetUint(uintVal) | ||
231 | field.Set(pv) | ||
232 | } else { | ||
233 | field.SetUint(uintVal) | ||
234 | } | ||
235 | |||
236 | case reflect.Float32, reflect.Float64: | ||
237 | floatVal, err := key.Float64() | ||
238 | if err != nil { | ||
239 | return wrapStrictError(err, isStrict) | ||
240 | } | ||
241 | if isPtr { | ||
242 | pv := reflect.New(t.Elem()) | ||
243 | pv.Elem().SetFloat(floatVal) | ||
244 | field.Set(pv) | ||
245 | } else { | ||
246 | field.SetFloat(floatVal) | ||
247 | } | ||
248 | case reflectTime: | ||
249 | timeVal, err := key.Time() | ||
250 | if err != nil { | ||
251 | return wrapStrictError(err, isStrict) | ||
252 | } | ||
253 | if isPtr { | ||
254 | field.Set(reflect.ValueOf(&timeVal)) | ||
255 | } else { | ||
256 | field.Set(reflect.ValueOf(timeVal)) | ||
257 | } | ||
258 | case reflect.Slice: | ||
259 | return setSliceWithProperType(key, field, delim, allowShadow, isStrict) | ||
260 | default: | ||
261 | return fmt.Errorf("unsupported type %q", t) | ||
262 | } | ||
263 | return nil | ||
264 | } | ||
265 | |||
266 | func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) { | ||
267 | opts := strings.SplitN(tag, ",", 5) | ||
268 | rawName = opts[0] | ||
269 | for _, opt := range opts[1:] { | ||
270 | omitEmpty = omitEmpty || (opt == "omitempty") | ||
271 | allowShadow = allowShadow || (opt == "allowshadow") | ||
272 | allowNonUnique = allowNonUnique || (opt == "nonunique") | ||
273 | extends = extends || (opt == "extends") | ||
274 | } | ||
275 | return rawName, omitEmpty, allowShadow, allowNonUnique, extends | ||
276 | } | ||
277 | |||
278 | // mapToField maps the given value to the matching field of the given section. | ||
279 | // The sectionIndex is the index (if non unique sections are enabled) to which the value should be added. | ||
280 | func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error { | ||
281 | if val.Kind() == reflect.Ptr { | ||
282 | val = val.Elem() | ||
283 | } | ||
284 | typ := val.Type() | ||
285 | |||
286 | for i := 0; i < typ.NumField(); i++ { | ||
287 | field := val.Field(i) | ||
288 | tpField := typ.Field(i) | ||
289 | |||
290 | tag := tpField.Tag.Get("ini") | ||
291 | if tag == "-" { | ||
292 | continue | ||
293 | } | ||
294 | |||
295 | rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag) | ||
296 | fieldName := s.parseFieldName(tpField.Name, rawName) | ||
297 | if len(fieldName) == 0 || !field.CanSet() { | ||
298 | continue | ||
299 | } | ||
300 | |||
301 | isStruct := tpField.Type.Kind() == reflect.Struct | ||
302 | isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct | ||
303 | isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous | ||
304 | if isAnonymousPtr { | ||
305 | field.Set(reflect.New(tpField.Type.Elem())) | ||
306 | } | ||
307 | |||
308 | if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) { | ||
309 | if isStructPtr && field.IsNil() { | ||
310 | field.Set(reflect.New(tpField.Type.Elem())) | ||
311 | } | ||
312 | fieldSection := s | ||
313 | if rawName != "" { | ||
314 | sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName | ||
315 | if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) { | ||
316 | fieldSection = secs[sectionIndex] | ||
317 | } | ||
318 | } | ||
319 | if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil { | ||
320 | return fmt.Errorf("map to field %q: %v", fieldName, err) | ||
321 | } | ||
322 | } else if isAnonymousPtr || isStruct || isStructPtr { | ||
323 | if secs, err := s.f.SectionsByName(fieldName); err == nil { | ||
324 | if len(secs) <= sectionIndex { | ||
325 | return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName) | ||
326 | } | ||
327 | // Only set the field to non-nil struct value if we have a section for it. | ||
328 | // Otherwise, we end up with a non-nil struct ptr even though there is no data. | ||
329 | if isStructPtr && field.IsNil() { | ||
330 | field.Set(reflect.New(tpField.Type.Elem())) | ||
331 | } | ||
332 | if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil { | ||
333 | return fmt.Errorf("map to field %q: %v", fieldName, err) | ||
334 | } | ||
335 | continue | ||
336 | } | ||
337 | } | ||
338 | |||
339 | // Map non-unique sections | ||
340 | if allowNonUnique && tpField.Type.Kind() == reflect.Slice { | ||
341 | newField, err := s.mapToSlice(fieldName, field, isStrict) | ||
342 | if err != nil { | ||
343 | return fmt.Errorf("map to slice %q: %v", fieldName, err) | ||
344 | } | ||
345 | |||
346 | field.Set(newField) | ||
347 | continue | ||
348 | } | ||
349 | |||
350 | if key, err := s.GetKey(fieldName); err == nil { | ||
351 | delim := parseDelim(tpField.Tag.Get("delim")) | ||
352 | if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { | ||
353 | return fmt.Errorf("set field %q: %v", fieldName, err) | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | return nil | ||
358 | } | ||
359 | |||
360 | // mapToSlice maps all sections with the same name and returns the new value. | ||
361 | // The type of the Value must be a slice. | ||
362 | func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) { | ||
363 | secs, err := s.f.SectionsByName(secName) | ||
364 | if err != nil { | ||
365 | return reflect.Value{}, err | ||
366 | } | ||
367 | |||
368 | typ := val.Type().Elem() | ||
369 | for i, sec := range secs { | ||
370 | elem := reflect.New(typ) | ||
371 | if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil { | ||
372 | return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err) | ||
373 | } | ||
374 | |||
375 | val = reflect.Append(val, elem.Elem()) | ||
376 | } | ||
377 | return val, nil | ||
378 | } | ||
379 | |||
380 | // mapTo maps a section to object v. | ||
381 | func (s *Section) mapTo(v interface{}, isStrict bool) error { | ||
382 | typ := reflect.TypeOf(v) | ||
383 | val := reflect.ValueOf(v) | ||
384 | if typ.Kind() == reflect.Ptr { | ||
385 | typ = typ.Elem() | ||
386 | val = val.Elem() | ||
387 | } else { | ||
388 | return errors.New("not a pointer to a struct") | ||
389 | } | ||
390 | |||
391 | if typ.Kind() == reflect.Slice { | ||
392 | newField, err := s.mapToSlice(s.name, val, isStrict) | ||
393 | if err != nil { | ||
394 | return err | ||
395 | } | ||
396 | |||
397 | val.Set(newField) | ||
398 | return nil | ||
399 | } | ||
400 | |||
401 | return s.mapToField(val, isStrict, 0, s.name) | ||
402 | } | ||
403 | |||
404 | // MapTo maps section to given struct. | ||
405 | func (s *Section) MapTo(v interface{}) error { | ||
406 | return s.mapTo(v, false) | ||
407 | } | ||
408 | |||
409 | // StrictMapTo maps section to given struct in strict mode, | ||
410 | // which returns all possible error including value parsing error. | ||
411 | func (s *Section) StrictMapTo(v interface{}) error { | ||
412 | return s.mapTo(v, true) | ||
413 | } | ||
414 | |||
415 | // MapTo maps file to given struct. | ||
416 | func (f *File) MapTo(v interface{}) error { | ||
417 | return f.Section("").MapTo(v) | ||
418 | } | ||
419 | |||
420 | // StrictMapTo maps file to given struct in strict mode, | ||
421 | // which returns all possible error including value parsing error. | ||
422 | func (f *File) StrictMapTo(v interface{}) error { | ||
423 | return f.Section("").StrictMapTo(v) | ||
424 | } | ||
425 | |||
426 | // MapToWithMapper maps data sources to given struct with name mapper. | ||
427 | func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { | ||
428 | cfg, err := Load(source, others...) | ||
429 | if err != nil { | ||
430 | return err | ||
431 | } | ||
432 | cfg.NameMapper = mapper | ||
433 | return cfg.MapTo(v) | ||
434 | } | ||
435 | |||
436 | // StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode, | ||
437 | // which returns all possible error including value parsing error. | ||
438 | func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { | ||
439 | cfg, err := Load(source, others...) | ||
440 | if err != nil { | ||
441 | return err | ||
442 | } | ||
443 | cfg.NameMapper = mapper | ||
444 | return cfg.StrictMapTo(v) | ||
445 | } | ||
446 | |||
447 | // MapTo maps data sources to given struct. | ||
448 | func MapTo(v, source interface{}, others ...interface{}) error { | ||
449 | return MapToWithMapper(v, nil, source, others...) | ||
450 | } | ||
451 | |||
452 | // StrictMapTo maps data sources to given struct in strict mode, | ||
453 | // which returns all possible error including value parsing error. | ||
454 | func StrictMapTo(v, source interface{}, others ...interface{}) error { | ||
455 | return StrictMapToWithMapper(v, nil, source, others...) | ||
456 | } | ||
457 | |||
458 | // reflectSliceWithProperType does the opposite thing as setSliceWithProperType. | ||
459 | func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error { | ||
460 | slice := field.Slice(0, field.Len()) | ||
461 | if field.Len() == 0 { | ||
462 | return nil | ||
463 | } | ||
464 | sliceOf := field.Type().Elem().Kind() | ||
465 | |||
466 | if allowShadow { | ||
467 | var keyWithShadows *Key | ||
468 | for i := 0; i < field.Len(); i++ { | ||
469 | var val string | ||
470 | switch sliceOf { | ||
471 | case reflect.String: | ||
472 | val = slice.Index(i).String() | ||
473 | case reflect.Int, reflect.Int64: | ||
474 | val = fmt.Sprint(slice.Index(i).Int()) | ||
475 | case reflect.Uint, reflect.Uint64: | ||
476 | val = fmt.Sprint(slice.Index(i).Uint()) | ||
477 | case reflect.Float64: | ||
478 | val = fmt.Sprint(slice.Index(i).Float()) | ||
479 | case reflect.Bool: | ||
480 | val = fmt.Sprint(slice.Index(i).Bool()) | ||
481 | case reflectTime: | ||
482 | val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339) | ||
483 | default: | ||
484 | return fmt.Errorf("unsupported type '[]%s'", sliceOf) | ||
485 | } | ||
486 | |||
487 | if i == 0 { | ||
488 | keyWithShadows = newKey(key.s, key.name, val) | ||
489 | } else { | ||
490 | _ = keyWithShadows.AddShadow(val) | ||
491 | } | ||
492 | } | ||
493 | *key = *keyWithShadows | ||
494 | return nil | ||
495 | } | ||
496 | |||
497 | var buf bytes.Buffer | ||
498 | for i := 0; i < field.Len(); i++ { | ||
499 | switch sliceOf { | ||
500 | case reflect.String: | ||
501 | buf.WriteString(slice.Index(i).String()) | ||
502 | case reflect.Int, reflect.Int64: | ||
503 | buf.WriteString(fmt.Sprint(slice.Index(i).Int())) | ||
504 | case reflect.Uint, reflect.Uint64: | ||
505 | buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) | ||
506 | case reflect.Float64: | ||
507 | buf.WriteString(fmt.Sprint(slice.Index(i).Float())) | ||
508 | case reflect.Bool: | ||
509 | buf.WriteString(fmt.Sprint(slice.Index(i).Bool())) | ||
510 | case reflectTime: | ||
511 | buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) | ||
512 | default: | ||
513 | return fmt.Errorf("unsupported type '[]%s'", sliceOf) | ||
514 | } | ||
515 | buf.WriteString(delim) | ||
516 | } | ||
517 | key.SetValue(buf.String()[:buf.Len()-len(delim)]) | ||
518 | return nil | ||
519 | } | ||
520 | |||
521 | // reflectWithProperType does the opposite thing as setWithProperType. | ||
522 | func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error { | ||
523 | switch t.Kind() { | ||
524 | case reflect.String: | ||
525 | key.SetValue(field.String()) | ||
526 | case reflect.Bool: | ||
527 | key.SetValue(fmt.Sprint(field.Bool())) | ||
528 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
529 | key.SetValue(fmt.Sprint(field.Int())) | ||
530 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
531 | key.SetValue(fmt.Sprint(field.Uint())) | ||
532 | case reflect.Float32, reflect.Float64: | ||
533 | key.SetValue(fmt.Sprint(field.Float())) | ||
534 | case reflectTime: | ||
535 | key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) | ||
536 | case reflect.Slice: | ||
537 | return reflectSliceWithProperType(key, field, delim, allowShadow) | ||
538 | case reflect.Ptr: | ||
539 | if !field.IsNil() { | ||
540 | return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow) | ||
541 | } | ||
542 | default: | ||
543 | return fmt.Errorf("unsupported type %q", t) | ||
544 | } | ||
545 | return nil | ||
546 | } | ||
547 | |||
548 | // CR: copied from encoding/json/encode.go with modifications of time.Time support. | ||
549 | // TODO: add more test coverage. | ||
550 | func isEmptyValue(v reflect.Value) bool { | ||
551 | switch v.Kind() { | ||
552 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: | ||
553 | return v.Len() == 0 | ||
554 | case reflect.Bool: | ||
555 | return !v.Bool() | ||
556 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
557 | return v.Int() == 0 | ||
558 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
559 | return v.Uint() == 0 | ||
560 | case reflect.Float32, reflect.Float64: | ||
561 | return v.Float() == 0 | ||
562 | case reflect.Interface, reflect.Ptr: | ||
563 | return v.IsNil() | ||
564 | case reflectTime: | ||
565 | t, ok := v.Interface().(time.Time) | ||
566 | return ok && t.IsZero() | ||
567 | } | ||
568 | return false | ||
569 | } | ||
570 | |||
571 | // StructReflector is the interface implemented by struct types that can extract themselves into INI objects. | ||
572 | type StructReflector interface { | ||
573 | ReflectINIStruct(*File) error | ||
574 | } | ||
575 | |||
576 | func (s *Section) reflectFrom(val reflect.Value) error { | ||
577 | if val.Kind() == reflect.Ptr { | ||
578 | val = val.Elem() | ||
579 | } | ||
580 | typ := val.Type() | ||
581 | |||
582 | for i := 0; i < typ.NumField(); i++ { | ||
583 | if !val.Field(i).CanInterface() { | ||
584 | continue | ||
585 | } | ||
586 | |||
587 | field := val.Field(i) | ||
588 | tpField := typ.Field(i) | ||
589 | |||
590 | tag := tpField.Tag.Get("ini") | ||
591 | if tag == "-" { | ||
592 | continue | ||
593 | } | ||
594 | |||
595 | rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag) | ||
596 | if omitEmpty && isEmptyValue(field) { | ||
597 | continue | ||
598 | } | ||
599 | |||
600 | if r, ok := field.Interface().(StructReflector); ok { | ||
601 | return r.ReflectINIStruct(s.f) | ||
602 | } | ||
603 | |||
604 | fieldName := s.parseFieldName(tpField.Name, rawName) | ||
605 | if len(fieldName) == 0 || !field.CanSet() { | ||
606 | continue | ||
607 | } | ||
608 | |||
609 | if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) { | ||
610 | if err := s.reflectFrom(field); err != nil { | ||
611 | return fmt.Errorf("reflect from field %q: %v", fieldName, err) | ||
612 | } | ||
613 | continue | ||
614 | } | ||
615 | |||
616 | if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) || | ||
617 | (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { | ||
618 | // Note: The only error here is section doesn't exist. | ||
619 | sec, err := s.f.GetSection(fieldName) | ||
620 | if err != nil { | ||
621 | // Note: fieldName can never be empty here, ignore error. | ||
622 | sec, _ = s.f.NewSection(fieldName) | ||
623 | } | ||
624 | |||
625 | // Add comment from comment tag | ||
626 | if len(sec.Comment) == 0 { | ||
627 | sec.Comment = tpField.Tag.Get("comment") | ||
628 | } | ||
629 | |||
630 | if err = sec.reflectFrom(field); err != nil { | ||
631 | return fmt.Errorf("reflect from field %q: %v", fieldName, err) | ||
632 | } | ||
633 | continue | ||
634 | } | ||
635 | |||
636 | if allowNonUnique && tpField.Type.Kind() == reflect.Slice { | ||
637 | slice := field.Slice(0, field.Len()) | ||
638 | if field.Len() == 0 { | ||
639 | return nil | ||
640 | } | ||
641 | sliceOf := field.Type().Elem().Kind() | ||
642 | |||
643 | for i := 0; i < field.Len(); i++ { | ||
644 | if sliceOf != reflect.Struct && sliceOf != reflect.Ptr { | ||
645 | return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName) | ||
646 | } | ||
647 | |||
648 | sec, err := s.f.NewSection(fieldName) | ||
649 | if err != nil { | ||
650 | return err | ||
651 | } | ||
652 | |||
653 | // Add comment from comment tag | ||
654 | if len(sec.Comment) == 0 { | ||
655 | sec.Comment = tpField.Tag.Get("comment") | ||
656 | } | ||
657 | |||
658 | if err := sec.reflectFrom(slice.Index(i)); err != nil { | ||
659 | return fmt.Errorf("reflect from field %q: %v", fieldName, err) | ||
660 | } | ||
661 | } | ||
662 | continue | ||
663 | } | ||
664 | |||
665 | // Note: Same reason as section. | ||
666 | key, err := s.GetKey(fieldName) | ||
667 | if err != nil { | ||
668 | key, _ = s.NewKey(fieldName, "") | ||
669 | } | ||
670 | |||
671 | // Add comment from comment tag | ||
672 | if len(key.Comment) == 0 { | ||
673 | key.Comment = tpField.Tag.Get("comment") | ||
674 | } | ||
675 | |||
676 | delim := parseDelim(tpField.Tag.Get("delim")) | ||
677 | if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { | ||
678 | return fmt.Errorf("reflect field %q: %v", fieldName, err) | ||
679 | } | ||
680 | |||
681 | } | ||
682 | return nil | ||
683 | } | ||
684 | |||
685 | // ReflectFrom reflects section from given struct. It overwrites existing ones. | ||
686 | func (s *Section) ReflectFrom(v interface{}) error { | ||
687 | typ := reflect.TypeOf(v) | ||
688 | val := reflect.ValueOf(v) | ||
689 | |||
690 | if s.name != DefaultSection && s.f.options.AllowNonUniqueSections && | ||
691 | (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) { | ||
692 | // Clear sections to make sure none exists before adding the new ones | ||
693 | s.f.DeleteSection(s.name) | ||
694 | |||
695 | if typ.Kind() == reflect.Ptr { | ||
696 | sec, err := s.f.NewSection(s.name) | ||
697 | if err != nil { | ||
698 | return err | ||
699 | } | ||
700 | return sec.reflectFrom(val.Elem()) | ||
701 | } | ||
702 | |||
703 | slice := val.Slice(0, val.Len()) | ||
704 | sliceOf := val.Type().Elem().Kind() | ||
705 | if sliceOf != reflect.Ptr { | ||
706 | return fmt.Errorf("not a slice of pointers") | ||
707 | } | ||
708 | |||
709 | for i := 0; i < slice.Len(); i++ { | ||
710 | sec, err := s.f.NewSection(s.name) | ||
711 | if err != nil { | ||
712 | return err | ||
713 | } | ||
714 | |||
715 | err = sec.reflectFrom(slice.Index(i)) | ||
716 | if err != nil { | ||
717 | return fmt.Errorf("reflect from %dth field: %v", i, err) | ||
718 | } | ||
719 | } | ||
720 | |||
721 | return nil | ||
722 | } | ||
723 | |||
724 | if typ.Kind() == reflect.Ptr { | ||
725 | val = val.Elem() | ||
726 | } else { | ||
727 | return errors.New("not a pointer to a struct") | ||
728 | } | ||
729 | |||
730 | return s.reflectFrom(val) | ||
731 | } | ||
732 | |||
733 | // ReflectFrom reflects file from given struct. | ||
734 | func (f *File) ReflectFrom(v interface{}) error { | ||
735 | return f.Section("").ReflectFrom(v) | ||
736 | } | ||
737 | |||
738 | // ReflectFromWithMapper reflects data sources from given struct with name mapper. | ||
739 | func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error { | ||
740 | cfg.NameMapper = mapper | ||
741 | return cfg.ReflectFrom(v) | ||
742 | } | ||
743 | |||
744 | // ReflectFrom reflects data sources from given struct. | ||
745 | func ReflectFrom(cfg *File, v interface{}) error { | ||
746 | return ReflectFromWithMapper(cfg, v, nil) | ||
747 | } | ||