summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/pelletier/go-toml/tomltree_create.go
blob: 80353500a0a374e42e02090d7f68c47e38668b90 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package toml

import (
	"fmt"
	"reflect"
	"time"
)

var kindToType = [reflect.String + 1]reflect.Type{
	reflect.Bool:    reflect.TypeOf(true),
	reflect.String:  reflect.TypeOf(""),
	reflect.Float32: reflect.TypeOf(float64(1)),
	reflect.Float64: reflect.TypeOf(float64(1)),
	reflect.Int:     reflect.TypeOf(int64(1)),
	reflect.Int8:    reflect.TypeOf(int64(1)),
	reflect.Int16:   reflect.TypeOf(int64(1)),
	reflect.Int32:   reflect.TypeOf(int64(1)),
	reflect.Int64:   reflect.TypeOf(int64(1)),
	reflect.Uint:    reflect.TypeOf(uint64(1)),
	reflect.Uint8:   reflect.TypeOf(uint64(1)),
	reflect.Uint16:  reflect.TypeOf(uint64(1)),
	reflect.Uint32:  reflect.TypeOf(uint64(1)),
	reflect.Uint64:  reflect.TypeOf(uint64(1)),
}

// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found.
// supported values:
// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32
func typeFor(k reflect.Kind) reflect.Type {
	if k > 0 && int(k) < len(kindToType) {
		return kindToType[k]
	}
	return nil
}

func simpleValueCoercion(object interface{}) (interface{}, error) {
	switch original := object.(type) {
	case string, bool, int64, uint64, float64, time.Time:
		return original, nil
	case int:
		return int64(original), nil
	case int8:
		return int64(original), nil
	case int16:
		return int64(original), nil
	case int32:
		return int64(original), nil
	case uint:
		return uint64(original), nil
	case uint8:
		return uint64(original), nil
	case uint16:
		return uint64(original), nil
	case uint32:
		return uint64(original), nil
	case float32:
		return float64(original), nil
	case fmt.Stringer:
		return original.String(), nil
	case []interface{}:
		value := reflect.ValueOf(original)
		length := value.Len()
		arrayValue := reflect.MakeSlice(value.Type(), 0, length)
		for i := 0; i < length; i++ {
			val := value.Index(i).Interface()
			simpleValue, err := simpleValueCoercion(val)
			if err != nil {
				return nil, err
			}
			arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
		}
		return arrayValue.Interface(), nil
	default:
		return nil, fmt.Errorf("cannot convert type %T to Tree", object)
	}
}

func sliceToTree(object interface{}) (interface{}, error) {
	// arrays are a bit tricky, since they can represent either a
	// collection of simple values, which is represented by one
	// *tomlValue, or an array of tables, which is represented by an
	// array of *Tree.

	// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice
	value := reflect.ValueOf(object)
	insideType := value.Type().Elem()
	length := value.Len()
	if length > 0 {
		insideType = reflect.ValueOf(value.Index(0).Interface()).Type()
	}
	if insideType.Kind() == reflect.Map {
		// this is considered as an array of tables
		tablesArray := make([]*Tree, 0, length)
		for i := 0; i < length; i++ {
			table := value.Index(i)
			tree, err := toTree(table.Interface())
			if err != nil {
				return nil, err
			}
			tablesArray = append(tablesArray, tree.(*Tree))
		}
		return tablesArray, nil
	}

	sliceType := typeFor(insideType.Kind())
	if sliceType == nil {
		sliceType = insideType
	}

	arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length)

	for i := 0; i < length; i++ {
		val := value.Index(i).Interface()
		simpleValue, err := simpleValueCoercion(val)
		if err != nil {
			return nil, err
		}
		arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
	}
	return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil
}

func toTree(object interface{}) (interface{}, error) {
	value := reflect.ValueOf(object)

	if value.Kind() == reflect.Map {
		values := map[string]interface{}{}
		keys := value.MapKeys()
		for _, key := range keys {
			if key.Kind() != reflect.String {
				if _, ok := key.Interface().(string); !ok {
					return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind())
				}
			}

			v := value.MapIndex(key)
			newValue, err := toTree(v.Interface())
			if err != nil {
				return nil, err
			}
			values[key.String()] = newValue
		}
		return &Tree{values: values, position: Position{}}, nil
	}

	if value.Kind() == reflect.Array || value.Kind() == reflect.Slice {
		return sliceToTree(object)
	}

	simpleValue, err := simpleValueCoercion(object)
	if err != nil {
		return nil, err
	}
	return &tomlValue{value: simpleValue, position: Position{}}, nil
}