]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/go-ini/ini/struct.go
Merge pull request #27 from terraform-providers/go-modules-2019-02-22
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / go-ini / ini / struct.go
CommitLineData
bae9f6d2
JC
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
15package ini
16
17import (
18 "bytes"
19 "errors"
20 "fmt"
21 "reflect"
22 "strings"
23 "time"
24 "unicode"
25)
26
27// NameMapper represents a ini tag name mapper.
28type NameMapper func(string) string
29
30// Built-in name getters.
31var (
32 // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
33 AllCapsUnderscore 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
61func (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
71func parseDelim(actual string) string {
72 if len(actual) > 0 {
73 return actual
74 }
75 return ","
76}
77
78var reflectTime = reflect.TypeOf(time.Now()).Kind()
79
80// setSliceWithProperType sets proper values to slice based on its type.
15c0b25d
AP
81func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error {
82 var strs []string
83 if allowShadow {
84 strs = key.StringsWithShadows(delim)
85 } else {
86 strs = key.Strings(delim)
87 }
88
bae9f6d2
JC
89 numVals := len(strs)
90 if numVals == 0 {
91 return nil
92 }
93
94 var vals interface{}
95
96 sliceOf := field.Type().Elem().Kind()
97 switch sliceOf {
98 case reflect.String:
99 vals = strs
100 case reflect.Int:
15c0b25d 101 vals, _ = key.parseInts(strs, true, false)
bae9f6d2 102 case reflect.Int64:
15c0b25d 103 vals, _ = key.parseInt64s(strs, true, false)
bae9f6d2
JC
104 case reflect.Uint:
105 vals = key.Uints(delim)
106 case reflect.Uint64:
107 vals = key.Uint64s(delim)
108 case reflect.Float64:
109 vals = key.Float64s(delim)
110 case reflectTime:
111 vals = key.Times(delim)
112 default:
113 return fmt.Errorf("unsupported type '[]%s'", sliceOf)
114 }
115
116 slice := reflect.MakeSlice(field.Type(), numVals, numVals)
117 for i := 0; i < numVals; i++ {
118 switch sliceOf {
119 case reflect.String:
120 slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i]))
121 case reflect.Int:
122 slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i]))
123 case reflect.Int64:
124 slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i]))
125 case reflect.Uint:
126 slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i]))
127 case reflect.Uint64:
128 slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
129 case reflect.Float64:
130 slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
131 case reflectTime:
132 slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
133 }
134 }
135 field.Set(slice)
136 return nil
137}
138
139// setWithProperType sets proper value to field based on its type,
140// but it does not return error for failing parsing,
141// because we want to use default value that is already assigned to strcut.
15c0b25d 142func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error {
bae9f6d2
JC
143 switch t.Kind() {
144 case reflect.String:
145 if len(key.String()) == 0 {
146 return nil
147 }
148 field.SetString(key.String())
149 case reflect.Bool:
150 boolVal, err := key.Bool()
151 if err != nil {
152 return nil
153 }
154 field.SetBool(boolVal)
155 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
156 durationVal, err := key.Duration()
157 // Skip zero value
158 if err == nil && int(durationVal) > 0 {
159 field.Set(reflect.ValueOf(durationVal))
160 return nil
161 }
162
163 intVal, err := key.Int64()
164 if err != nil || intVal == 0 {
165 return nil
166 }
167 field.SetInt(intVal)
168 // byte is an alias for uint8, so supporting uint8 breaks support for byte
169 case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
170 durationVal, err := key.Duration()
171 // Skip zero value
172 if err == nil && int(durationVal) > 0 {
173 field.Set(reflect.ValueOf(durationVal))
174 return nil
175 }
176
177 uintVal, err := key.Uint64()
178 if err != nil {
179 return nil
180 }
181 field.SetUint(uintVal)
182
183 case reflect.Float32, reflect.Float64:
184 floatVal, err := key.Float64()
185 if err != nil {
186 return nil
187 }
188 field.SetFloat(floatVal)
189 case reflectTime:
190 timeVal, err := key.Time()
191 if err != nil {
192 return nil
193 }
194 field.Set(reflect.ValueOf(timeVal))
195 case reflect.Slice:
15c0b25d 196 return setSliceWithProperType(key, field, delim, allowShadow)
bae9f6d2
JC
197 default:
198 return fmt.Errorf("unsupported type '%s'", t)
199 }
200 return nil
201}
202
15c0b25d
AP
203func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) {
204 opts := strings.SplitN(tag, ",", 3)
205 rawName = opts[0]
206 if len(opts) > 1 {
207 omitEmpty = opts[1] == "omitempty"
208 }
209 if len(opts) > 2 {
210 allowShadow = opts[2] == "allowshadow"
211 }
212 return rawName, omitEmpty, allowShadow
213}
214
bae9f6d2
JC
215func (s *Section) mapTo(val reflect.Value) error {
216 if val.Kind() == reflect.Ptr {
217 val = val.Elem()
218 }
219 typ := val.Type()
220
221 for i := 0; i < typ.NumField(); i++ {
222 field := val.Field(i)
223 tpField := typ.Field(i)
224
225 tag := tpField.Tag.Get("ini")
226 if tag == "-" {
227 continue
228 }
229
15c0b25d
AP
230 rawName, _, allowShadow := parseTagOptions(tag)
231 fieldName := s.parseFieldName(tpField.Name, rawName)
bae9f6d2
JC
232 if len(fieldName) == 0 || !field.CanSet() {
233 continue
234 }
235
236 isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
237 isStruct := tpField.Type.Kind() == reflect.Struct
238 if isAnonymous {
239 field.Set(reflect.New(tpField.Type.Elem()))
240 }
241
242 if isAnonymous || isStruct {
243 if sec, err := s.f.GetSection(fieldName); err == nil {
244 if err = sec.mapTo(field); err != nil {
245 return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
246 }
247 continue
248 }
249 }
250
251 if key, err := s.GetKey(fieldName); err == nil {
15c0b25d
AP
252 delim := parseDelim(tpField.Tag.Get("delim"))
253 if err = setWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil {
bae9f6d2
JC
254 return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
255 }
256 }
257 }
258 return nil
259}
260
261// MapTo maps section to given struct.
262func (s *Section) MapTo(v interface{}) error {
263 typ := reflect.TypeOf(v)
264 val := reflect.ValueOf(v)
265 if typ.Kind() == reflect.Ptr {
266 typ = typ.Elem()
267 val = val.Elem()
268 } else {
269 return errors.New("cannot map to non-pointer struct")
270 }
271
272 return s.mapTo(val)
273}
274
275// MapTo maps file to given struct.
276func (f *File) MapTo(v interface{}) error {
277 return f.Section("").MapTo(v)
278}
279
280// MapTo maps data sources to given struct with name mapper.
281func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
282 cfg, err := Load(source, others...)
283 if err != nil {
284 return err
285 }
286 cfg.NameMapper = mapper
287 return cfg.MapTo(v)
288}
289
290// MapTo maps data sources to given struct.
291func MapTo(v, source interface{}, others ...interface{}) error {
292 return MapToWithMapper(v, nil, source, others...)
293}
294
295// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
296func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error {
297 slice := field.Slice(0, field.Len())
298 if field.Len() == 0 {
299 return nil
300 }
301
302 var buf bytes.Buffer
303 sliceOf := field.Type().Elem().Kind()
304 for i := 0; i < field.Len(); i++ {
305 switch sliceOf {
306 case reflect.String:
307 buf.WriteString(slice.Index(i).String())
308 case reflect.Int, reflect.Int64:
309 buf.WriteString(fmt.Sprint(slice.Index(i).Int()))
310 case reflect.Uint, reflect.Uint64:
311 buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
312 case reflect.Float64:
313 buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
314 case reflectTime:
315 buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
316 default:
317 return fmt.Errorf("unsupported type '[]%s'", sliceOf)
318 }
319 buf.WriteString(delim)
320 }
321 key.SetValue(buf.String()[:buf.Len()-1])
322 return nil
323}
324
325// reflectWithProperType does the opposite thing as setWithProperType.
326func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
327 switch t.Kind() {
328 case reflect.String:
329 key.SetValue(field.String())
330 case reflect.Bool:
331 key.SetValue(fmt.Sprint(field.Bool()))
332 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
333 key.SetValue(fmt.Sprint(field.Int()))
334 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
335 key.SetValue(fmt.Sprint(field.Uint()))
336 case reflect.Float32, reflect.Float64:
337 key.SetValue(fmt.Sprint(field.Float()))
338 case reflectTime:
339 key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
340 case reflect.Slice:
341 return reflectSliceWithProperType(key, field, delim)
342 default:
343 return fmt.Errorf("unsupported type '%s'", t)
344 }
345 return nil
346}
347
348// CR: copied from encoding/json/encode.go with modifications of time.Time support.
349// TODO: add more test coverage.
350func isEmptyValue(v reflect.Value) bool {
351 switch v.Kind() {
352 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
353 return v.Len() == 0
354 case reflect.Bool:
355 return !v.Bool()
356 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
357 return v.Int() == 0
358 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
359 return v.Uint() == 0
360 case reflect.Float32, reflect.Float64:
361 return v.Float() == 0
362 case reflectTime:
363 return v.Interface().(time.Time).IsZero()
364 case reflect.Interface, reflect.Ptr:
365 return v.IsNil()
366 }
367 return false
368}
369
370func (s *Section) reflectFrom(val reflect.Value) error {
371 if val.Kind() == reflect.Ptr {
372 val = val.Elem()
373 }
374 typ := val.Type()
375
376 for i := 0; i < typ.NumField(); i++ {
377 field := val.Field(i)
378 tpField := typ.Field(i)
379
380 tag := tpField.Tag.Get("ini")
381 if tag == "-" {
382 continue
383 }
384
385 opts := strings.SplitN(tag, ",", 2)
386 if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) {
387 continue
388 }
389
390 fieldName := s.parseFieldName(tpField.Name, opts[0])
391 if len(fieldName) == 0 || !field.CanSet() {
392 continue
393 }
394
395 if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) ||
396 (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
397 // Note: The only error here is section doesn't exist.
398 sec, err := s.f.GetSection(fieldName)
399 if err != nil {
400 // Note: fieldName can never be empty here, ignore error.
401 sec, _ = s.f.NewSection(fieldName)
402 }
403 if err = sec.reflectFrom(field); err != nil {
404 return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
405 }
406 continue
407 }
408
409 // Note: Same reason as secion.
410 key, err := s.GetKey(fieldName)
411 if err != nil {
412 key, _ = s.NewKey(fieldName, "")
413 }
414 if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
415 return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
416 }
417
418 }
419 return nil
420}
421
422// ReflectFrom reflects secion from given struct.
423func (s *Section) ReflectFrom(v interface{}) error {
424 typ := reflect.TypeOf(v)
425 val := reflect.ValueOf(v)
426 if typ.Kind() == reflect.Ptr {
427 typ = typ.Elem()
428 val = val.Elem()
429 } else {
430 return errors.New("cannot reflect from non-pointer struct")
431 }
432
433 return s.reflectFrom(val)
434}
435
436// ReflectFrom reflects file from given struct.
437func (f *File) ReflectFrom(v interface{}) error {
438 return f.Section("").ReflectFrom(v)
439}
440
441// ReflectFrom reflects data sources from given struct with name mapper.
442func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
443 cfg.NameMapper = mapper
444 return cfg.ReflectFrom(v)
445}
446
447// ReflectFrom reflects data sources from given struct.
448func ReflectFrom(cfg *File, v interface{}) error {
449 return ReflectFromWithMapper(cfg, v, nil)
450}