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