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	position  Position
27}
28
29func newTree() *Tree {
30	return newTreeWithPosition(Position{})
31}
32
33func newTreeWithPosition(pos Position) *Tree {
34	return &Tree{
35		values:   make(map[string]interface{}),
36		position: pos,
37	}
38}
39
40// TreeFromMap initializes a new Tree object using the given map.
41func TreeFromMap(m map[string]interface{}) (*Tree, error) {
42	result, err := toTree(m)
43	if err != nil {
44		return nil, err
45	}
46	return result.(*Tree), nil
47}
48
49// Position returns the position of the tree.
50func (t *Tree) Position() Position {
51	return t.position
52}
53
54// Has returns a boolean indicating if the given key exists.
55func (t *Tree) Has(key string) bool {
56	if key == "" {
57		return false
58	}
59	return t.HasPath(strings.Split(key, "."))
60}
61
62// HasPath returns true if the given path of keys exists, false otherwise.
63func (t *Tree) HasPath(keys []string) bool {
64	return t.GetPath(keys) != nil
65}
66
67// Keys returns the keys of the toplevel tree (does not recurse).
68func (t *Tree) Keys() []string {
69	keys := make([]string, len(t.values))
70	i := 0
71	for k := range t.values {
72		keys[i] = k
73		i++
74	}
75	return keys
76}
77
78// Get the value at key in the Tree.
79// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
80// If you need to retrieve non-bare keys, use GetPath.
81// Returns nil if the path does not exist in the tree.
82// If keys is of length zero, the current tree is returned.
83func (t *Tree) Get(key string) interface{} {
84	if key == "" {
85		return t
86	}
87	return t.GetPath(strings.Split(key, "."))
88}
89
90// GetPath returns the element in the tree indicated by 'keys'.
91// If keys is of length zero, the current tree is returned.
92func (t *Tree) GetPath(keys []string) interface{} {
93	if len(keys) == 0 {
94		return t
95	}
96	subtree := t
97	for _, intermediateKey := range keys[:len(keys)-1] {
98		value, exists := subtree.values[intermediateKey]
99		if !exists {
100			return nil
101		}
102		switch node := value.(type) {
103		case *Tree:
104			subtree = node
105		case []*Tree:
106			// go to most recent element
107			if len(node) == 0 {
108				return nil
109			}
110			subtree = node[len(node)-1]
111		default:
112			return nil // cannot navigate through other node types
113		}
114	}
115	// branch based on final node type
116	switch node := subtree.values[keys[len(keys)-1]].(type) {
117	case *tomlValue:
118		return node.value
119	default:
120		return node
121	}
122}
123
124// GetPosition returns the position of the given key.
125func (t *Tree) GetPosition(key string) Position {
126	if key == "" {
127		return t.position
128	}
129	return t.GetPositionPath(strings.Split(key, "."))
130}
131
132// GetPositionPath returns the element in the tree indicated by 'keys'.
133// If keys is of length zero, the current tree is returned.
134func (t *Tree) GetPositionPath(keys []string) Position {
135	if len(keys) == 0 {
136		return t.position
137	}
138	subtree := t
139	for _, intermediateKey := range keys[:len(keys)-1] {
140		value, exists := subtree.values[intermediateKey]
141		if !exists {
142			return Position{0, 0}
143		}
144		switch node := value.(type) {
145		case *Tree:
146			subtree = node
147		case []*Tree:
148			// go to most recent element
149			if len(node) == 0 {
150				return Position{0, 0}
151			}
152			subtree = node[len(node)-1]
153		default:
154			return Position{0, 0}
155		}
156	}
157	// branch based on final node type
158	switch node := subtree.values[keys[len(keys)-1]].(type) {
159	case *tomlValue:
160		return node.position
161	case *Tree:
162		return node.position
163	case []*Tree:
164		// go to most recent element
165		if len(node) == 0 {
166			return Position{0, 0}
167		}
168		return node[len(node)-1].position
169	default:
170		return Position{0, 0}
171	}
172}
173
174// GetDefault works like Get but with a default value
175func (t *Tree) GetDefault(key string, def interface{}) interface{} {
176	val := t.Get(key)
177	if val == nil {
178		return def
179	}
180	return val
181}
182
183// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
184// The default values within the struct are valid default options.
185type SetOptions struct {
186	Comment   string
187	Commented bool
188	Multiline bool
189}
190
191// SetWithOptions is the same as Set, but allows you to provide formatting
192// instructions to the key, that will be used by Marshal().
193func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
194	t.SetPathWithOptions(strings.Split(key, "."), opts, value)
195}
196
197// SetPathWithOptions is the same as SetPath, but allows you to provide
198// formatting instructions to the key, that will be reused by Marshal().
199func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
200	subtree := t
201	for i, intermediateKey := range keys[:len(keys)-1] {
202		nextTree, exists := subtree.values[intermediateKey]
203		if !exists {
204			nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
205			subtree.values[intermediateKey] = nextTree // add new element here
206		}
207		switch node := nextTree.(type) {
208		case *Tree:
209			subtree = node
210		case []*Tree:
211			// go to most recent element
212			if len(node) == 0 {
213				// create element if it does not exist
214				subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
215			}
216			subtree = node[len(node)-1]
217		}
218	}
219
220	var toInsert interface{}
221
222	switch v := value.(type) {
223	case *Tree:
224		v.comment = opts.Comment
225		toInsert = value
226	case []*Tree:
227		toInsert = value
228	case *tomlValue:
229		v.comment = opts.Comment
230		toInsert = v
231	default:
232		toInsert = &tomlValue{value: value,
233			comment:   opts.Comment,
234			commented: opts.Commented,
235			multiline: opts.Multiline,
236			position:  Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
237	}
238
239	subtree.values[keys[len(keys)-1]] = toInsert
240}
241
242// Set an element in the tree.
243// Key is a dot-separated path (e.g. a.b.c).
244// Creates all necessary intermediate trees, if needed.
245func (t *Tree) Set(key string, value interface{}) {
246	t.SetWithComment(key, "", false, value)
247}
248
249// SetWithComment is the same as Set, but allows you to provide comment
250// information to the key, that will be reused by Marshal().
251func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
252	t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
253}
254
255// SetPath sets an element in the tree.
256// Keys is an array of path elements (e.g. {"a","b","c"}).
257// Creates all necessary intermediate trees, if needed.
258func (t *Tree) SetPath(keys []string, value interface{}) {
259	t.SetPathWithComment(keys, "", false, value)
260}
261
262// SetPathWithComment is the same as SetPath, but allows you to provide comment
263// information to the key, that will be reused by Marshal().
264func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
265	t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
266}
267
268// Delete removes a key from the tree.
269// Key is a dot-separated path (e.g. a.b.c).
270func (t *Tree) Delete(key string) error {
271	keys, err := parseKey(key)
272	if err != nil {
273		return err
274	}
275	return t.DeletePath(keys)
276}
277
278// DeletePath removes a key from the tree.
279// Keys is an array of path elements (e.g. {"a","b","c"}).
280func (t *Tree) DeletePath(keys []string) error {
281	keyLen := len(keys)
282	if keyLen == 1 {
283		delete(t.values, keys[0])
284		return nil
285	}
286	tree := t.GetPath(keys[:keyLen-1])
287	item := keys[keyLen-1]
288	switch node := tree.(type) {
289	case *Tree:
290		delete(node.values, item)
291		return nil
292	}
293	return errors.New("no such key to delete")
294}
295
296// createSubTree takes a tree and a key and create the necessary intermediate
297// subtrees to create a subtree at that point. In-place.
298//
299// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
300// and tree[a][b][c]
301//
302// Returns nil on success, error object on failure
303func (t *Tree) createSubTree(keys []string, pos Position) error {
304	subtree := t
305	for i, intermediateKey := range keys {
306		nextTree, exists := subtree.values[intermediateKey]
307		if !exists {
308			tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
309			tree.position = pos
310			subtree.values[intermediateKey] = tree
311			nextTree = tree
312		}
313
314		switch node := nextTree.(type) {
315		case []*Tree:
316			subtree = node[len(node)-1]
317		case *Tree:
318			subtree = node
319		default:
320			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
321				strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
322		}
323	}
324	return nil
325}
326
327// LoadBytes creates a Tree from a []byte.
328func LoadBytes(b []byte) (tree *Tree, err error) {
329	defer func() {
330		if r := recover(); r != nil {
331			if _, ok := r.(runtime.Error); ok {
332				panic(r)
333			}
334			err = errors.New(r.(string))
335		}
336	}()
337
338	if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
339		b = b[4:]
340	} else if len(b) >= 3 && hasUTF8BOM3(b) {
341		b = b[3:]
342	} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
343		b = b[2:]
344	}
345
346	tree = parseToml(lexToml(b))
347	return
348}
349
350func hasUTF16BigEndianBOM2(b []byte) bool {
351	return b[0] == 0xFE && b[1] == 0xFF
352}
353
354func hasUTF16LittleEndianBOM2(b []byte) bool {
355	return b[0] == 0xFF && b[1] == 0xFE
356}
357
358func hasUTF8BOM3(b []byte) bool {
359	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
360}
361
362func hasUTF32BigEndianBOM4(b []byte) bool {
363	return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
364}
365
366func hasUTF32LittleEndianBOM4(b []byte) bool {
367	return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
368}
369
370// LoadReader creates a Tree from any io.Reader.
371func LoadReader(reader io.Reader) (tree *Tree, err error) {
372	inputBytes, err := ioutil.ReadAll(reader)
373	if err != nil {
374		return
375	}
376	tree, err = LoadBytes(inputBytes)
377	return
378}
379
380// Load creates a Tree from a string.
381func Load(content string) (tree *Tree, err error) {
382	return LoadBytes([]byte(content))
383}
384
385// LoadFile creates a Tree from a file.
386func LoadFile(path string) (tree *Tree, err error) {
387	file, err := os.Open(path)
388	if err != nil {
389		return nil, err
390	}
391	defer file.Close()
392	return LoadReader(file)
393}
394