1 package toml |
|
2 |
|
3 import ( |
|
4 "errors" |
|
5 "fmt" |
|
6 "io" |
|
7 "io/ioutil" |
|
8 "os" |
|
9 "runtime" |
|
10 "strings" |
|
11 ) |
|
12 |
|
13 type 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. |
|
23 type 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 |
|
31 func newTree() *Tree { |
|
32 return newTreeWithPosition(Position{}) |
|
33 } |
|
34 |
|
35 func 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. |
|
43 func 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. |
|
52 func (t *Tree) Position() Position { |
|
53 return t.position |
|
54 } |
|
55 |
|
56 // Has returns a boolean indicating if the given key exists. |
|
57 func (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. |
|
65 func (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). |
|
70 func (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. |
|
85 func (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. |
|
94 func (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. |
|
131 func (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. |
|
140 func (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{} |
|
178 func 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. |
|
210 func (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. |
|
219 func (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. |
|
263 func (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 |
|
304 func (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. |
|
314 type 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(). |
|
323 func (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(). |
|
329 func (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. |
|
384 func (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(). |
|
390 func (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. |
|
397 func (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(). |
|
403 func (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). |
|
409 func (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"}). |
|
419 func (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 |
|
442 func (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. |
|
468 func 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 = fmt.Errorf("%s", r) |
|
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 |
|
490 func hasUTF16BigEndianBOM2(b []byte) bool { |
|
491 return b[0] == 0xFE && b[1] == 0xFF |
|
492 } |
|
493 |
|
494 func hasUTF16LittleEndianBOM2(b []byte) bool { |
|
495 return b[0] == 0xFF && b[1] == 0xFE |
|
496 } |
|
497 |
|
498 func hasUTF8BOM3(b []byte) bool { |
|
499 return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF |
|
500 } |
|
501 |
|
502 func hasUTF32BigEndianBOM4(b []byte) bool { |
|
503 return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF |
|
504 } |
|
505 |
|
506 func 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. |
|
511 func 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. |
|
521 func Load(content string) (tree *Tree, err error) { |
|
522 return LoadBytes([]byte(content)) |
|
523 } |
|
524 |
|
525 // LoadFile creates a Tree from a file. |
|
526 func 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 } |
|