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.
81func 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 reflectTime:
112		vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
113	default:
114		return fmt.Errorf("unsupported type '[]%s'", sliceOf)
115	}
116	if err != nil && isStrict {
117		return err
118	}
119
120	slice := reflect.MakeSlice(field.Type(), numVals, numVals)
121	for i := 0; i < numVals; i++ {
122		switch sliceOf {
123		case reflect.String:
124			slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i]))
125		case reflect.Int:
126			slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i]))
127		case reflect.Int64:
128			slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i]))
129		case reflect.Uint:
130			slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i]))
131		case reflect.Uint64:
132			slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
133		case reflect.Float64:
134			slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
135		case reflectTime:
136			slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
137		}
138	}
139	field.Set(slice)
140	return nil
141}
142
143func wrapStrictError(err error, isStrict bool) error {
144	if isStrict {
145		return err
146	}
147	return nil
148}
149
150// setWithProperType sets proper value to field based on its type,
151// but it does not return error for failing parsing,
152// because we want to use default value that is already assigned to strcut.
153func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
154	switch t.Kind() {
155	case reflect.String:
156		if len(key.String()) == 0 {
157			return nil
158		}
159		field.SetString(key.String())
160	case reflect.Bool:
161		boolVal, err := key.Bool()
162		if err != nil {
163			return wrapStrictError(err, isStrict)
164		}
165		field.SetBool(boolVal)
166	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
167		durationVal, err := key.Duration()
168		// Skip zero value
169		if err == nil && int64(durationVal) > 0 {
170			field.Set(reflect.ValueOf(durationVal))
171			return nil
172		}
173
174		intVal, err := key.Int64()
175		if err != nil {
176			return wrapStrictError(err, isStrict)
177		}
178		field.SetInt(intVal)
179	//	byte is an alias for uint8, so supporting uint8 breaks support for byte
180	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
181		durationVal, err := key.Duration()
182		// Skip zero value
183		if err == nil && uint64(durationVal) > 0 {
184			field.Set(reflect.ValueOf(durationVal))
185			return nil
186		}
187
188		uintVal, err := key.Uint64()
189		if err != nil {
190			return wrapStrictError(err, isStrict)
191		}
192		field.SetUint(uintVal)
193
194	case reflect.Float32, reflect.Float64:
195		floatVal, err := key.Float64()
196		if err != nil {
197			return wrapStrictError(err, isStrict)
198		}
199		field.SetFloat(floatVal)
200	case reflectTime:
201		timeVal, err := key.Time()
202		if err != nil {
203			return wrapStrictError(err, isStrict)
204		}
205		field.Set(reflect.ValueOf(timeVal))
206	case reflect.Slice:
207		return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
208	default:
209		return fmt.Errorf("unsupported type '%s'", t)
210	}
211	return nil
212}
213
214func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) {
215	opts := strings.SplitN(tag, ",", 3)
216	rawName = opts[0]
217	if len(opts) > 1 {
218		omitEmpty = opts[1] == "omitempty"
219	}
220	if len(opts) > 2 {
221		allowShadow = opts[2] == "allowshadow"
222	}
223	return rawName, omitEmpty, allowShadow
224}
225
226func (s *Section) mapTo(val reflect.Value, isStrict bool) error {
227	if val.Kind() == reflect.Ptr {
228		val = val.Elem()
229	}
230	typ := val.Type()
231
232	for i := 0; i < typ.NumField(); i++ {
233		field := val.Field(i)
234		tpField := typ.Field(i)
235
236		tag := tpField.Tag.Get("ini")
237		if tag == "-" {
238			continue
239		}
240
241		rawName, _, allowShadow := parseTagOptions(tag)
242		fieldName := s.parseFieldName(tpField.Name, rawName)
243		if len(fieldName) == 0 || !field.CanSet() {
244			continue
245		}
246
247		isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
248		isStruct := tpField.Type.Kind() == reflect.Struct
249		if isAnonymous {
250			field.Set(reflect.New(tpField.Type.Elem()))
251		}
252
253		if isAnonymous || isStruct {
254			if sec, err := s.f.GetSection(fieldName); err == nil {
255				if err = sec.mapTo(field, isStrict); err != nil {
256					return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
257				}
258				continue
259			}
260		}
261
262		if key, err := s.GetKey(fieldName); err == nil {
263			delim := parseDelim(tpField.Tag.Get("delim"))
264			if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
265				return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
266			}
267		}
268	}
269	return nil
270}
271
272// MapTo maps section to given struct.
273func (s *Section) MapTo(v interface{}) error {
274	typ := reflect.TypeOf(v)
275	val := reflect.ValueOf(v)
276	if typ.Kind() == reflect.Ptr {
277		typ = typ.Elem()
278		val = val.Elem()
279	} else {
280		return errors.New("cannot map to non-pointer struct")
281	}
282
283	return s.mapTo(val, false)
284}
285
286// MapTo maps section to given struct in strict mode,
287// which returns all possible error including value parsing error.
288func (s *Section) StrictMapTo(v interface{}) error {
289	typ := reflect.TypeOf(v)
290	val := reflect.ValueOf(v)
291	if typ.Kind() == reflect.Ptr {
292		typ = typ.Elem()
293		val = val.Elem()
294	} else {
295		return errors.New("cannot map to non-pointer struct")
296	}
297
298	return s.mapTo(val, true)
299}
300
301// MapTo maps file to given struct.
302func (f *File) MapTo(v interface{}) error {
303	return f.Section("").MapTo(v)
304}
305
306// MapTo maps file to given struct in strict mode,
307// which returns all possible error including value parsing error.
308func (f *File) StrictMapTo(v interface{}) error {
309	return f.Section("").StrictMapTo(v)
310}
311
312// MapTo maps data sources to given struct with name mapper.
313func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
314	cfg, err := Load(source, others...)
315	if err != nil {
316		return err
317	}
318	cfg.NameMapper = mapper
319	return cfg.MapTo(v)
320}
321
322// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
323// which returns all possible error including value parsing error.
324func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
325	cfg, err := Load(source, others...)
326	if err != nil {
327		return err
328	}
329	cfg.NameMapper = mapper
330	return cfg.StrictMapTo(v)
331}
332
333// MapTo maps data sources to given struct.
334func MapTo(v, source interface{}, others ...interface{}) error {
335	return MapToWithMapper(v, nil, source, others...)
336}
337
338// StrictMapTo maps data sources to given struct in strict mode,
339// which returns all possible error including value parsing error.
340func StrictMapTo(v, source interface{}, others ...interface{}) error {
341	return StrictMapToWithMapper(v, nil, source, others...)
342}
343
344// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
345func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error {
346	slice := field.Slice(0, field.Len())
347	if field.Len() == 0 {
348		return nil
349	}
350
351	var buf bytes.Buffer
352	sliceOf := field.Type().Elem().Kind()
353	for i := 0; i < field.Len(); i++ {
354		switch sliceOf {
355		case reflect.String:
356			buf.WriteString(slice.Index(i).String())
357		case reflect.Int, reflect.Int64:
358			buf.WriteString(fmt.Sprint(slice.Index(i).Int()))
359		case reflect.Uint, reflect.Uint64:
360			buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
361		case reflect.Float64:
362			buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
363		case reflectTime:
364			buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
365		default:
366			return fmt.Errorf("unsupported type '[]%s'", sliceOf)
367		}
368		buf.WriteString(delim)
369	}
370	key.SetValue(buf.String()[:buf.Len()-1])
371	return nil
372}
373
374// reflectWithProperType does the opposite thing as setWithProperType.
375func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
376	switch t.Kind() {
377	case reflect.String:
378		key.SetValue(field.String())
379	case reflect.Bool:
380		key.SetValue(fmt.Sprint(field.Bool()))
381	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
382		key.SetValue(fmt.Sprint(field.Int()))
383	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
384		key.SetValue(fmt.Sprint(field.Uint()))
385	case reflect.Float32, reflect.Float64:
386		key.SetValue(fmt.Sprint(field.Float()))
387	case reflectTime:
388		key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
389	case reflect.Slice:
390		return reflectSliceWithProperType(key, field, delim)
391	default:
392		return fmt.Errorf("unsupported type '%s'", t)
393	}
394	return nil
395}
396
397// CR: copied from encoding/json/encode.go with modifications of time.Time support.
398// TODO: add more test coverage.
399func isEmptyValue(v reflect.Value) bool {
400	switch v.Kind() {
401	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
402		return v.Len() == 0
403	case reflect.Bool:
404		return !v.Bool()
405	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
406		return v.Int() == 0
407	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
408		return v.Uint() == 0
409	case reflect.Float32, reflect.Float64:
410		return v.Float() == 0
411	case reflect.Interface, reflect.Ptr:
412		return v.IsNil()
413	case reflectTime:
414		t, ok := v.Interface().(time.Time)
415		return ok && t.IsZero()
416	}
417	return false
418}
419
420func (s *Section) reflectFrom(val reflect.Value) error {
421	if val.Kind() == reflect.Ptr {
422		val = val.Elem()
423	}
424	typ := val.Type()
425
426	for i := 0; i < typ.NumField(); i++ {
427		field := val.Field(i)
428		tpField := typ.Field(i)
429
430		tag := tpField.Tag.Get("ini")
431		if tag == "-" {
432			continue
433		}
434
435		opts := strings.SplitN(tag, ",", 2)
436		if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) {
437			continue
438		}
439
440		fieldName := s.parseFieldName(tpField.Name, opts[0])
441		if len(fieldName) == 0 || !field.CanSet() {
442			continue
443		}
444
445		if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) ||
446			(tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
447			// Note: The only error here is section doesn't exist.
448			sec, err := s.f.GetSection(fieldName)
449			if err != nil {
450				// Note: fieldName can never be empty here, ignore error.
451				sec, _ = s.f.NewSection(fieldName)
452			}
453
454			// Add comment from comment tag
455			if len(sec.Comment) == 0 {
456				sec.Comment = tpField.Tag.Get("comment")
457			}
458
459			if err = sec.reflectFrom(field); err != nil {
460				return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
461			}
462			continue
463		}
464
465		// Note: Same reason as secion.
466		key, err := s.GetKey(fieldName)
467		if err != nil {
468			key, _ = s.NewKey(fieldName, "")
469		}
470
471		// Add comment from comment tag
472		if len(key.Comment) == 0 {
473			key.Comment = tpField.Tag.Get("comment")
474		}
475
476		if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
477			return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
478		}
479
480	}
481	return nil
482}
483
484// ReflectFrom reflects secion from given struct.
485func (s *Section) ReflectFrom(v interface{}) error {
486	typ := reflect.TypeOf(v)
487	val := reflect.ValueOf(v)
488	if typ.Kind() == reflect.Ptr {
489		typ = typ.Elem()
490		val = val.Elem()
491	} else {
492		return errors.New("cannot reflect from non-pointer struct")
493	}
494
495	return s.reflectFrom(val)
496}
497
498// ReflectFrom reflects file from given struct.
499func (f *File) ReflectFrom(v interface{}) error {
500	return f.Section("").ReflectFrom(v)
501}
502
503// ReflectFrom reflects data sources from given struct with name mapper.
504func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
505	cfg.NameMapper = mapper
506	return cfg.ReflectFrom(v)
507}
508
509// ReflectFrom reflects data sources from given struct.
510func ReflectFrom(cfg *File, v interface{}) error {
511	return ReflectFromWithMapper(cfg, v, nil)
512}
513