1package toml
2
3import (
4	"fmt"
5	"reflect"
6	"time"
7)
8
9var kindToType = [reflect.String + 1]reflect.Type{
10	reflect.Bool:    reflect.TypeOf(true),
11	reflect.String:  reflect.TypeOf(""),
12	reflect.Float32: reflect.TypeOf(float64(1)),
13	reflect.Float64: reflect.TypeOf(float64(1)),
14	reflect.Int:     reflect.TypeOf(int64(1)),
15	reflect.Int8:    reflect.TypeOf(int64(1)),
16	reflect.Int16:   reflect.TypeOf(int64(1)),
17	reflect.Int32:   reflect.TypeOf(int64(1)),
18	reflect.Int64:   reflect.TypeOf(int64(1)),
19	reflect.Uint:    reflect.TypeOf(uint64(1)),
20	reflect.Uint8:   reflect.TypeOf(uint64(1)),
21	reflect.Uint16:  reflect.TypeOf(uint64(1)),
22	reflect.Uint32:  reflect.TypeOf(uint64(1)),
23	reflect.Uint64:  reflect.TypeOf(uint64(1)),
24}
25
26// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found.
27// supported values:
28// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32
29func typeFor(k reflect.Kind) reflect.Type {
30	if k > 0 && int(k) < len(kindToType) {
31		return kindToType[k]
32	}
33	return nil
34}
35
36func simpleValueCoercion(object interface{}) (interface{}, error) {
37	switch original := object.(type) {
38	case string, bool, int64, uint64, float64, time.Time:
39		return original, nil
40	case int:
41		return int64(original), nil
42	case int8:
43		return int64(original), nil
44	case int16:
45		return int64(original), nil
46	case int32:
47		return int64(original), nil
48	case uint:
49		return uint64(original), nil
50	case uint8:
51		return uint64(original), nil
52	case uint16:
53		return uint64(original), nil
54	case uint32:
55		return uint64(original), nil
56	case float32:
57		return float64(original), nil
58	case fmt.Stringer:
59		return original.String(), nil
60	default:
61		return nil, fmt.Errorf("cannot convert type %T to Tree", object)
62	}
63}
64
65func sliceToTree(object interface{}) (interface{}, error) {
66	// arrays are a bit tricky, since they can represent either a
67	// collection of simple values, which is represented by one
68	// *tomlValue, or an array of tables, which is represented by an
69	// array of *Tree.
70
71	// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice
72	value := reflect.ValueOf(object)
73	insideType := value.Type().Elem()
74	length := value.Len()
75	if length > 0 {
76		insideType = reflect.ValueOf(value.Index(0).Interface()).Type()
77	}
78	if insideType.Kind() == reflect.Map {
79		// this is considered as an array of tables
80		tablesArray := make([]*Tree, 0, length)
81		for i := 0; i < length; i++ {
82			table := value.Index(i)
83			tree, err := toTree(table.Interface())
84			if err != nil {
85				return nil, err
86			}
87			tablesArray = append(tablesArray, tree.(*Tree))
88		}
89		return tablesArray, nil
90	}
91
92	sliceType := typeFor(insideType.Kind())
93	if sliceType == nil {
94		sliceType = insideType
95	}
96
97	arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length)
98
99	for i := 0; i < length; i++ {
100		val := value.Index(i).Interface()
101		simpleValue, err := simpleValueCoercion(val)
102		if err != nil {
103			return nil, err
104		}
105		arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
106	}
107	return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil
108}
109
110func toTree(object interface{}) (interface{}, error) {
111	value := reflect.ValueOf(object)
112
113	if value.Kind() == reflect.Map {
114		values := map[string]interface{}{}
115		keys := value.MapKeys()
116		for _, key := range keys {
117			if key.Kind() != reflect.String {
118				if _, ok := key.Interface().(string); !ok {
119					return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind())
120				}
121			}
122
123			v := value.MapIndex(key)
124			newValue, err := toTree(v.Interface())
125			if err != nil {
126				return nil, err
127			}
128			values[key.String()] = newValue
129		}
130		return &Tree{values: values, position: Position{}}, nil
131	}
132
133	if value.Kind() == reflect.Array || value.Kind() == reflect.Slice {
134		return sliceToTree(object)
135	}
136
137	simpleValue, err := simpleValueCoercion(object)
138	if err != nil {
139		return nil, err
140	}
141	return &tomlValue{value: simpleValue, position: Position{}}, nil
142}
143