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// GetPosition returns the position of the given key.
126func (t *Tree) GetPosition(key string) Position {
127	if key == "" {
128		return t.position
129	}
130	return t.GetPositionPath(strings.Split(key, "."))
131}
132
133// GetPositionPath returns the element in the tree indicated by 'keys'.
134// If keys is of length zero, the current tree is returned.
135func (t *Tree) GetPositionPath(keys []string) Position {
136	if len(keys) == 0 {
137		return t.position
138	}
139	subtree := t
140	for _, intermediateKey := range keys[:len(keys)-1] {
141		value, exists := subtree.values[intermediateKey]
142		if !exists {
143			return Position{0, 0}
144		}
145		switch node := value.(type) {
146		case *Tree:
147			subtree = node
148		case []*Tree:
149			// go to most recent element
150			if len(node) == 0 {
151				return Position{0, 0}
152			}
153			subtree = node[len(node)-1]
154		default:
155			return Position{0, 0}
156		}
157	}
158	// branch based on final node type
159	switch node := subtree.values[keys[len(keys)-1]].(type) {
160	case *tomlValue:
161		return node.position
162	case *Tree:
163		return node.position
164	case []*Tree:
165		// go to most recent element
166		if len(node) == 0 {
167			return Position{0, 0}
168		}
169		return node[len(node)-1].position
170	default:
171		return Position{0, 0}
172	}
173}
174
175// GetDefault works like Get but with a default value
176func (t *Tree) GetDefault(key string, def interface{}) interface{} {
177	val := t.Get(key)
178	if val == nil {
179		return def
180	}
181	return val
182}
183
184// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
185// The default values within the struct are valid default options.
186type SetOptions struct {
187	Comment   string
188	Commented bool
189	Multiline bool
190}
191
192// SetWithOptions is the same as Set, but allows you to provide formatting
193// instructions to the key, that will be used by Marshal().
194func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
195	t.SetPathWithOptions(strings.Split(key, "."), opts, value)
196}
197
198// SetPathWithOptions is the same as SetPath, but allows you to provide
199// formatting instructions to the key, that will be reused by Marshal().
200func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
201	subtree := t
202	for i, intermediateKey := range keys[:len(keys)-1] {
203		nextTree, exists := subtree.values[intermediateKey]
204		if !exists {
205			nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
206			subtree.values[intermediateKey] = nextTree // add new element here
207		}
208		switch node := nextTree.(type) {
209		case *Tree:
210			subtree = node
211		case []*Tree:
212			// go to most recent element
213			if len(node) == 0 {
214				// create element if it does not exist
215				subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
216			}
217			subtree = node[len(node)-1]
218		}
219	}
220
221	var toInsert interface{}
222
223	switch v := value.(type) {
224	case *Tree:
225		v.comment = opts.Comment
226		v.commented = opts.Commented
227		toInsert = value
228	case []*Tree:
229		for i := range v {
230			v[i].commented = opts.Commented
231		}
232		toInsert = value
233	case *tomlValue:
234		v.comment = opts.Comment
235		toInsert = v
236	default:
237		toInsert = &tomlValue{value: value,
238			comment:   opts.Comment,
239			commented: opts.Commented,
240			multiline: opts.Multiline,
241			position:  Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
242	}
243
244	subtree.values[keys[len(keys)-1]] = toInsert
245}
246
247// Set an element in the tree.
248// Key is a dot-separated path (e.g. a.b.c).
249// Creates all necessary intermediate trees, if needed.
250func (t *Tree) Set(key string, value interface{}) {
251	t.SetWithComment(key, "", false, value)
252}
253
254// SetWithComment is the same as Set, but allows you to provide comment
255// information to the key, that will be reused by Marshal().
256func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
257	t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
258}
259
260// SetPath sets an element in the tree.
261// Keys is an array of path elements (e.g. {"a","b","c"}).
262// Creates all necessary intermediate trees, if needed.
263func (t *Tree) SetPath(keys []string, value interface{}) {
264	t.SetPathWithComment(keys, "", false, value)
265}
266
267// SetPathWithComment is the same as SetPath, but allows you to provide comment
268// information to the key, that will be reused by Marshal().
269func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
270	t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
271}
272
273// Delete removes a key from the tree.
274// Key is a dot-separated path (e.g. a.b.c).
275func (t *Tree) Delete(key string) error {
276	keys, err := parseKey(key)
277	if err != nil {
278		return err
279	}
280	return t.DeletePath(keys)
281}
282
283// DeletePath removes a key from the tree.
284// Keys is an array of path elements (e.g. {"a","b","c"}).
285func (t *Tree) DeletePath(keys []string) error {
286	keyLen := len(keys)
287	if keyLen == 1 {
288		delete(t.values, keys[0])
289		return nil
290	}
291	tree := t.GetPath(keys[:keyLen-1])
292	item := keys[keyLen-1]
293	switch node := tree.(type) {
294	case *Tree:
295		delete(node.values, item)
296		return nil
297	}
298	return errors.New("no such key to delete")
299}
300
301// createSubTree takes a tree and a key and create the necessary intermediate
302// subtrees to create a subtree at that point. In-place.
303//
304// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
305// and tree[a][b][c]
306//
307// Returns nil on success, error object on failure
308func (t *Tree) createSubTree(keys []string, pos Position) error {
309	subtree := t
310	for i, intermediateKey := range keys {
311		nextTree, exists := subtree.values[intermediateKey]
312		if !exists {
313			tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
314			tree.position = pos
315			tree.inline = subtree.inline
316			subtree.values[intermediateKey] = tree
317			nextTree = tree
318		}
319
320		switch node := nextTree.(type) {
321		case []*Tree:
322			subtree = node[len(node)-1]
323		case *Tree:
324			subtree = node
325		default:
326			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
327				strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
328		}
329	}
330	return nil
331}
332
333// LoadBytes creates a Tree from a []byte.
334func LoadBytes(b []byte) (tree *Tree, err error) {
335	defer func() {
336		if r := recover(); r != nil {
337			if _, ok := r.(runtime.Error); ok {
338				panic(r)
339			}
340			err = errors.New(r.(string))
341		}
342	}()
343
344	if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
345		b = b[4:]
346	} else if len(b) >= 3 && hasUTF8BOM3(b) {
347		b = b[3:]
348	} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
349		b = b[2:]
350	}
351
352	tree = parseToml(lexToml(b))
353	return
354}
355
356func hasUTF16BigEndianBOM2(b []byte) bool {
357	return b[0] == 0xFE && b[1] == 0xFF
358}
359
360func hasUTF16LittleEndianBOM2(b []byte) bool {
361	return b[0] == 0xFF && b[1] == 0xFE
362}
363
364func hasUTF8BOM3(b []byte) bool {
365	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
366}
367
368func hasUTF32BigEndianBOM4(b []byte) bool {
369	return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
370}
371
372func hasUTF32LittleEndianBOM4(b []byte) bool {
373	return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
374}
375
376// LoadReader creates a Tree from any io.Reader.
377func LoadReader(reader io.Reader) (tree *Tree, err error) {
378	inputBytes, err := ioutil.ReadAll(reader)
379	if err != nil {
380		return
381	}
382	tree, err = LoadBytes(inputBytes)
383	return
384}
385
386// Load creates a Tree from a string.
387func Load(content string) (tree *Tree, err error) {
388	return LoadBytes([]byte(content))
389}
390
391// LoadFile creates a Tree from a file.
392func LoadFile(path string) (tree *Tree, err error) {
393	file, err := os.Open(path)
394	if err != nil {
395		return nil, err
396	}
397	defer file.Close()
398	return LoadReader(file)
399}
400