1package toml
2
3import (
4	"errors"
5	"fmt"
6	"io"
7	"os"
8	"runtime"
9	"strings"
10)
11
12type tomlValue struct {
13	value    interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
14	position Position
15}
16
17// Tree is the result of the parsing of a TOML file.
18type Tree struct {
19	values   map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
20	position Position
21}
22
23func newTree() *Tree {
24	return &Tree{
25		values:   make(map[string]interface{}),
26		position: Position{},
27	}
28}
29
30// TreeFromMap initializes a new Tree object using the given map.
31func TreeFromMap(m map[string]interface{}) (*Tree, error) {
32	result, err := toTree(m)
33	if err != nil {
34		return nil, err
35	}
36	return result.(*Tree), nil
37}
38
39// Position returns the position of the tree.
40func (t *Tree) Position() Position {
41	return t.position
42}
43
44// Has returns a boolean indicating if the given key exists.
45func (t *Tree) Has(key string) bool {
46	if key == "" {
47		return false
48	}
49	return t.HasPath(strings.Split(key, "."))
50}
51
52// HasPath returns true if the given path of keys exists, false otherwise.
53func (t *Tree) HasPath(keys []string) bool {
54	return t.GetPath(keys) != nil
55}
56
57// Keys returns the keys of the toplevel tree (does not recurse).
58func (t *Tree) Keys() []string {
59	keys := make([]string, len(t.values))
60	i := 0
61	for k := range t.values {
62		keys[i] = k
63		i++
64	}
65	return keys
66}
67
68// Get the value at key in the Tree.
69// Key is a dot-separated path (e.g. a.b.c).
70// Returns nil if the path does not exist in the tree.
71// If keys is of length zero, the current tree is returned.
72func (t *Tree) Get(key string) interface{} {
73	if key == "" {
74		return t
75	}
76	comps, err := parseKey(key)
77	if err != nil {
78		return nil
79	}
80	return t.GetPath(comps)
81}
82
83// GetPath returns the element in the tree indicated by 'keys'.
84// If keys is of length zero, the current tree is returned.
85func (t *Tree) GetPath(keys []string) interface{} {
86	if len(keys) == 0 {
87		return t
88	}
89	subtree := t
90	for _, intermediateKey := range keys[:len(keys)-1] {
91		value, exists := subtree.values[intermediateKey]
92		if !exists {
93			return nil
94		}
95		switch node := value.(type) {
96		case *Tree:
97			subtree = node
98		case []*Tree:
99			// go to most recent element
100			if len(node) == 0 {
101				return nil
102			}
103			subtree = node[len(node)-1]
104		default:
105			return nil // cannot navigate through other node types
106		}
107	}
108	// branch based on final node type
109	switch node := subtree.values[keys[len(keys)-1]].(type) {
110	case *tomlValue:
111		return node.value
112	default:
113		return node
114	}
115}
116
117// GetPosition returns the position of the given key.
118func (t *Tree) GetPosition(key string) Position {
119	if key == "" {
120		return t.position
121	}
122	return t.GetPositionPath(strings.Split(key, "."))
123}
124
125// GetPositionPath returns the element in the tree indicated by 'keys'.
126// If keys is of length zero, the current tree is returned.
127func (t *Tree) GetPositionPath(keys []string) Position {
128	if len(keys) == 0 {
129		return t.position
130	}
131	subtree := t
132	for _, intermediateKey := range keys[:len(keys)-1] {
133		value, exists := subtree.values[intermediateKey]
134		if !exists {
135			return Position{0, 0}
136		}
137		switch node := value.(type) {
138		case *Tree:
139			subtree = node
140		case []*Tree:
141			// go to most recent element
142			if len(node) == 0 {
143				return Position{0, 0}
144			}
145			subtree = node[len(node)-1]
146		default:
147			return Position{0, 0}
148		}
149	}
150	// branch based on final node type
151	switch node := subtree.values[keys[len(keys)-1]].(type) {
152	case *tomlValue:
153		return node.position
154	case *Tree:
155		return node.position
156	case []*Tree:
157		// go to most recent element
158		if len(node) == 0 {
159			return Position{0, 0}
160		}
161		return node[len(node)-1].position
162	default:
163		return Position{0, 0}
164	}
165}
166
167// GetDefault works like Get but with a default value
168func (t *Tree) GetDefault(key string, def interface{}) interface{} {
169	val := t.Get(key)
170	if val == nil {
171		return def
172	}
173	return val
174}
175
176// Set an element in the tree.
177// Key is a dot-separated path (e.g. a.b.c).
178// Creates all necessary intermediate trees, if needed.
179func (t *Tree) Set(key string, value interface{}) {
180	t.SetPath(strings.Split(key, "."), value)
181}
182
183// SetPath sets an element in the tree.
184// Keys is an array of path elements (e.g. {"a","b","c"}).
185// Creates all necessary intermediate trees, if needed.
186func (t *Tree) SetPath(keys []string, value interface{}) {
187	subtree := t
188	for _, intermediateKey := range keys[:len(keys)-1] {
189		nextTree, exists := subtree.values[intermediateKey]
190		if !exists {
191			nextTree = newTree()
192			subtree.values[intermediateKey] = nextTree // add new element here
193		}
194		switch node := nextTree.(type) {
195		case *Tree:
196			subtree = node
197		case []*Tree:
198			// go to most recent element
199			if len(node) == 0 {
200				// create element if it does not exist
201				subtree.values[intermediateKey] = append(node, newTree())
202			}
203			subtree = node[len(node)-1]
204		}
205	}
206
207	var toInsert interface{}
208
209	switch value.(type) {
210	case *Tree:
211		toInsert = value
212	case []*Tree:
213		toInsert = value
214	case *tomlValue:
215		toInsert = value
216	default:
217		toInsert = &tomlValue{value: value}
218	}
219
220	subtree.values[keys[len(keys)-1]] = toInsert
221}
222
223// createSubTree takes a tree and a key and create the necessary intermediate
224// subtrees to create a subtree at that point. In-place.
225//
226// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
227// and tree[a][b][c]
228//
229// Returns nil on success, error object on failure
230func (t *Tree) createSubTree(keys []string, pos Position) error {
231	subtree := t
232	for _, intermediateKey := range keys {
233		nextTree, exists := subtree.values[intermediateKey]
234		if !exists {
235			tree := newTree()
236			tree.position = pos
237			subtree.values[intermediateKey] = tree
238			nextTree = tree
239		}
240
241		switch node := nextTree.(type) {
242		case []*Tree:
243			subtree = node[len(node)-1]
244		case *Tree:
245			subtree = node
246		default:
247			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
248				strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
249		}
250	}
251	return nil
252}
253
254// LoadReader creates a Tree from any io.Reader.
255func LoadReader(reader io.Reader) (tree *Tree, err error) {
256	defer func() {
257		if r := recover(); r != nil {
258			if _, ok := r.(runtime.Error); ok {
259				panic(r)
260			}
261			err = errors.New(r.(string))
262		}
263	}()
264	tree = parseToml(lexToml(reader))
265	return
266}
267
268// Load creates a Tree from a string.
269func Load(content string) (tree *Tree, err error) {
270	return LoadReader(strings.NewReader(content))
271}
272
273// LoadFile creates a Tree from a file.
274func LoadFile(path string) (tree *Tree, err error) {
275	file, err := os.Open(path)
276	if err != nil {
277		return nil, err
278	}
279	defer file.Close()
280	return LoadReader(file)
281}
282