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