1package toml
2
3import (
4	"errors"
5	"fmt"
6	"io"
7	"io/ioutil"
8	"os"
9	"runtime"
10	"strings"
11)
12
13type tomlValue struct {
14	value     interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
15	comment   string
16	commented bool
17	multiline bool
18	position  Position
19}
20
21// Tree is the result of the parsing of a TOML file.
22type Tree struct {
23	values    map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
24	comment   string
25	commented bool
26	inline    bool
27	position  Position
28}
29
30func newTree() *Tree {
31	return newTreeWithPosition(Position{})
32}
33
34func newTreeWithPosition(pos Position) *Tree {
35	return &Tree{
36		values:   make(map[string]interface{}),
37		position: pos,
38	}
39}
40
41// TreeFromMap initializes a new Tree object using the given map.
42func TreeFromMap(m map[string]interface{}) (*Tree, error) {
43	result, err := toTree(m)
44	if err != nil {
45		return nil, err
46	}
47	return result.(*Tree), nil
48}
49
50// Position returns the position of the tree.
51func (t *Tree) Position() Position {
52	return t.position
53}
54
55// Has returns a boolean indicating if the given key exists.
56func (t *Tree) Has(key string) bool {
57	if key == "" {
58		return false
59	}
60	return t.HasPath(strings.Split(key, "."))
61}
62
63// HasPath returns true if the given path of keys exists, false otherwise.
64func (t *Tree) HasPath(keys []string) bool {
65	return t.GetPath(keys) != nil
66}
67
68// Keys returns the keys of the toplevel tree (does not recurse).
69func (t *Tree) Keys() []string {
70	keys := make([]string, len(t.values))
71	i := 0
72	for k := range t.values {
73		keys[i] = k
74		i++
75	}
76	return keys
77}
78
79// Get the value at key in the Tree.
80// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
81// If you need to retrieve non-bare keys, use GetPath.
82// Returns nil if the path does not exist in the tree.
83// If keys is of length zero, the current tree is returned.
84func (t *Tree) Get(key string) interface{} {
85	if key == "" {
86		return t
87	}
88	return t.GetPath(strings.Split(key, "."))
89}
90
91// GetPath returns the element in the tree indicated by 'keys'.
92// If keys is of length zero, the current tree is returned.
93func (t *Tree) GetPath(keys []string) interface{} {
94	if len(keys) == 0 {
95		return t
96	}
97	subtree := t
98	for _, intermediateKey := range keys[:len(keys)-1] {
99		value, exists := subtree.values[intermediateKey]
100		if !exists {
101			return nil
102		}
103		switch node := value.(type) {
104		case *Tree:
105			subtree = node
106		case []*Tree:
107			// go to most recent element
108			if len(node) == 0 {
109				return nil
110			}
111			subtree = node[len(node)-1]
112		default:
113			return nil // cannot navigate through other node types
114		}
115	}
116	// branch based on final node type
117	switch node := subtree.values[keys[len(keys)-1]].(type) {
118	case *tomlValue:
119		return node.value
120	default:
121		return node
122	}
123}
124
125// GetArray returns the value at key in the Tree.
126// It returns []string, []int64, etc type if key has homogeneous lists
127// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
128// Returns nil if the path does not exist in the tree.
129// If keys is of length zero, the current tree is returned.
130func (t *Tree) GetArray(key string) interface{} {
131	if key == "" {
132		return t
133	}
134	return t.GetArrayPath(strings.Split(key, "."))
135}
136
137// GetArrayPath returns the element in the tree indicated by 'keys'.
138// If keys is of length zero, the current tree is returned.
139func (t *Tree) GetArrayPath(keys []string) interface{} {
140	if len(keys) == 0 {
141		return t
142	}
143	subtree := t
144	for _, intermediateKey := range keys[:len(keys)-1] {
145		value, exists := subtree.values[intermediateKey]
146		if !exists {
147			return nil
148		}
149		switch node := value.(type) {
150		case *Tree:
151			subtree = node
152		case []*Tree:
153			// go to most recent element
154			if len(node) == 0 {
155				return nil
156			}
157			subtree = node[len(node)-1]
158		default:
159			return nil // cannot navigate through other node types
160		}
161	}
162	// branch based on final node type
163	switch node := subtree.values[keys[len(keys)-1]].(type) {
164	case *tomlValue:
165		switch n := node.value.(type) {
166		case []interface{}:
167			return getArray(n)
168		default:
169			return node.value
170		}
171	default:
172		return node
173	}
174}
175
176// if homogeneous array, then return slice type object over []interface{}
177func getArray(n []interface{}) interface{} {
178	var s []string
179	var i64 []int64
180	var f64 []float64
181	var bl []bool
182	for _, value := range n {
183		switch v := value.(type) {
184		case string:
185			s = append(s, v)
186		case int64:
187			i64 = append(i64, v)
188		case float64:
189			f64 = append(f64, v)
190		case bool:
191			bl = append(bl, v)
192		default:
193			return n
194		}
195	}
196	if len(s) == len(n) {
197		return s
198	} else if len(i64) == len(n) {
199		return i64
200	} else if len(f64) == len(n) {
201		return f64
202	} else if len(bl) == len(n) {
203		return bl
204	}
205	return n
206}
207
208// GetPosition returns the position of the given key.
209func (t *Tree) GetPosition(key string) Position {
210	if key == "" {
211		return t.position
212	}
213	return t.GetPositionPath(strings.Split(key, "."))
214}
215
216// SetPositionPath sets the position of element in the tree indicated by 'keys'.
217// If keys is of length zero, the current tree position is set.
218func (t *Tree) SetPositionPath(keys []string, pos Position) {
219	if len(keys) == 0 {
220		t.position = pos
221		return
222	}
223	subtree := t
224	for _, intermediateKey := range keys[:len(keys)-1] {
225		value, exists := subtree.values[intermediateKey]
226		if !exists {
227			return
228		}
229		switch node := value.(type) {
230		case *Tree:
231			subtree = node
232		case []*Tree:
233			// go to most recent element
234			if len(node) == 0 {
235				return
236			}
237			subtree = node[len(node)-1]
238		default:
239			return
240		}
241	}
242	// branch based on final node type
243	switch node := subtree.values[keys[len(keys)-1]].(type) {
244	case *tomlValue:
245		node.position = pos
246		return
247	case *Tree:
248		node.position = pos
249		return
250	case []*Tree:
251		// go to most recent element
252		if len(node) == 0 {
253			return
254		}
255		node[len(node)-1].position = pos
256		return
257	}
258}
259
260// GetPositionPath returns the element in the tree indicated by 'keys'.
261// If keys is of length zero, the current tree is returned.
262func (t *Tree) GetPositionPath(keys []string) Position {
263	if len(keys) == 0 {
264		return t.position
265	}
266	subtree := t
267	for _, intermediateKey := range keys[:len(keys)-1] {
268		value, exists := subtree.values[intermediateKey]
269		if !exists {
270			return Position{0, 0}
271		}
272		switch node := value.(type) {
273		case *Tree:
274			subtree = node
275		case []*Tree:
276			// go to most recent element
277			if len(node) == 0 {
278				return Position{0, 0}
279			}
280			subtree = node[len(node)-1]
281		default:
282			return Position{0, 0}
283		}
284	}
285	// branch based on final node type
286	switch node := subtree.values[keys[len(keys)-1]].(type) {
287	case *tomlValue:
288		return node.position
289	case *Tree:
290		return node.position
291	case []*Tree:
292		// go to most recent element
293		if len(node) == 0 {
294			return Position{0, 0}
295		}
296		return node[len(node)-1].position
297	default:
298		return Position{0, 0}
299	}
300}
301
302// GetDefault works like Get but with a default value
303func (t *Tree) GetDefault(key string, def interface{}) interface{} {
304	val := t.Get(key)
305	if val == nil {
306		return def
307	}
308	return val
309}
310
311// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
312// The default values within the struct are valid default options.
313type SetOptions struct {
314	Comment   string
315	Commented bool
316	Multiline bool
317}
318
319// SetWithOptions is the same as Set, but allows you to provide formatting
320// instructions to the key, that will be used by Marshal().
321func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
322	t.SetPathWithOptions(strings.Split(key, "."), opts, value)
323}
324
325// SetPathWithOptions is the same as SetPath, but allows you to provide
326// formatting instructions to the key, that will be reused by Marshal().
327func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
328	subtree := t
329	for i, intermediateKey := range keys[:len(keys)-1] {
330		nextTree, exists := subtree.values[intermediateKey]
331		if !exists {
332			nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
333			subtree.values[intermediateKey] = nextTree // add new element here
334		}
335		switch node := nextTree.(type) {
336		case *Tree:
337			subtree = node
338		case []*Tree:
339			// go to most recent element
340			if len(node) == 0 {
341				// create element if it does not exist
342				node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
343				subtree.values[intermediateKey] = node
344			}
345			subtree = node[len(node)-1]
346		}
347	}
348
349	var toInsert interface{}
350
351	switch v := value.(type) {
352	case *Tree:
353		v.comment = opts.Comment
354		v.commented = opts.Commented
355		toInsert = value
356	case []*Tree:
357		for i := range v {
358			v[i].commented = opts.Commented
359		}
360		toInsert = value
361	case *tomlValue:
362		v.comment = opts.Comment
363		v.commented = opts.Commented
364		v.multiline = opts.Multiline
365		toInsert = v
366	default:
367		toInsert = &tomlValue{value: value,
368			comment:   opts.Comment,
369			commented: opts.Commented,
370			multiline: opts.Multiline,
371			position:  Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
372	}
373
374	subtree.values[keys[len(keys)-1]] = toInsert
375}
376
377// Set an element in the tree.
378// Key is a dot-separated path (e.g. a.b.c).
379// Creates all necessary intermediate trees, if needed.
380func (t *Tree) Set(key string, value interface{}) {
381	t.SetWithComment(key, "", false, value)
382}
383
384// SetWithComment is the same as Set, but allows you to provide comment
385// information to the key, that will be reused by Marshal().
386func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
387	t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
388}
389
390// SetPath sets an element in the tree.
391// Keys is an array of path elements (e.g. {"a","b","c"}).
392// Creates all necessary intermediate trees, if needed.
393func (t *Tree) SetPath(keys []string, value interface{}) {
394	t.SetPathWithComment(keys, "", false, value)
395}
396
397// SetPathWithComment is the same as SetPath, but allows you to provide comment
398// information to the key, that will be reused by Marshal().
399func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
400	t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
401}
402
403// Delete removes a key from the tree.
404// Key is a dot-separated path (e.g. a.b.c).
405func (t *Tree) Delete(key string) error {
406	keys, err := parseKey(key)
407	if err != nil {
408		return err
409	}
410	return t.DeletePath(keys)
411}
412
413// DeletePath removes a key from the tree.
414// Keys is an array of path elements (e.g. {"a","b","c"}).
415func (t *Tree) DeletePath(keys []string) error {
416	keyLen := len(keys)
417	if keyLen == 1 {
418		delete(t.values, keys[0])
419		return nil
420	}
421	tree := t.GetPath(keys[:keyLen-1])
422	item := keys[keyLen-1]
423	switch node := tree.(type) {
424	case *Tree:
425		delete(node.values, item)
426		return nil
427	}
428	return errors.New("no such key to delete")
429}
430
431// createSubTree takes a tree and a key and create the necessary intermediate
432// subtrees to create a subtree at that point. In-place.
433//
434// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
435// and tree[a][b][c]
436//
437// Returns nil on success, error object on failure
438func (t *Tree) createSubTree(keys []string, pos Position) error {
439	subtree := t
440	for i, intermediateKey := range keys {
441		nextTree, exists := subtree.values[intermediateKey]
442		if !exists {
443			tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
444			tree.position = pos
445			tree.inline = subtree.inline
446			subtree.values[intermediateKey] = tree
447			nextTree = tree
448		}
449
450		switch node := nextTree.(type) {
451		case []*Tree:
452			subtree = node[len(node)-1]
453		case *Tree:
454			subtree = node
455		default:
456			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
457				strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
458		}
459	}
460	return nil
461}
462
463// LoadBytes creates a Tree from a []byte.
464func LoadBytes(b []byte) (tree *Tree, err error) {
465	defer func() {
466		if r := recover(); r != nil {
467			if _, ok := r.(runtime.Error); ok {
468				panic(r)
469			}
470			err = errors.New(r.(string))
471		}
472	}()
473
474	if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
475		b = b[4:]
476	} else if len(b) >= 3 && hasUTF8BOM3(b) {
477		b = b[3:]
478	} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
479		b = b[2:]
480	}
481
482	tree = parseToml(lexToml(b))
483	return
484}
485
486func hasUTF16BigEndianBOM2(b []byte) bool {
487	return b[0] == 0xFE && b[1] == 0xFF
488}
489
490func hasUTF16LittleEndianBOM2(b []byte) bool {
491	return b[0] == 0xFF && b[1] == 0xFE
492}
493
494func hasUTF8BOM3(b []byte) bool {
495	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
496}
497
498func hasUTF32BigEndianBOM4(b []byte) bool {
499	return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
500}
501
502func hasUTF32LittleEndianBOM4(b []byte) bool {
503	return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
504}
505
506// LoadReader creates a Tree from any io.Reader.
507func LoadReader(reader io.Reader) (tree *Tree, err error) {
508	inputBytes, err := ioutil.ReadAll(reader)
509	if err != nil {
510		return
511	}
512	tree, err = LoadBytes(inputBytes)
513	return
514}
515
516// Load creates a Tree from a string.
517func Load(content string) (tree *Tree, err error) {
518	return LoadBytes([]byte(content))
519}
520
521// LoadFile creates a Tree from a file.
522func LoadFile(path string) (tree *Tree, err error) {
523	file, err := os.Open(path)
524	if err != nil {
525		return nil, err
526	}
527	defer file.Close()
528	return LoadReader(file)
529}
530