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{}
15 comment string
16 commented bool
17 multiline bool
18 literal bool
19 position Position
20 }
21
22
23 type Tree struct {
24 values map[string]interface{}
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
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
52 func (t *Tree) Position() Position {
53 return t.position
54 }
55
56
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
65 func (t *Tree) HasPath(keys []string) bool {
66 return t.GetPath(keys) != nil
67 }
68
69
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
81
82
83
84
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
93
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
109 if len(node) == 0 {
110 return nil
111 }
112 subtree = node[len(node)-1]
113 default:
114 return nil
115 }
116 }
117
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
127
128
129
130
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
139
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
155 if len(node) == 0 {
156 return nil
157 }
158 subtree = node[len(node)-1]
159 default:
160 return nil
161 }
162 }
163
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
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
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
218
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
235 if len(node) == 0 {
236 return
237 }
238 subtree = node[len(node)-1]
239 default:
240 return
241 }
242 }
243
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
253 if len(node) == 0 {
254 return
255 }
256 node[len(node)-1].position = pos
257 return
258 }
259 }
260
261
262
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
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
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
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
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
313
314 type SetOptions struct {
315 Comment string
316 Commented bool
317 Multiline bool
318 Literal bool
319 }
320
321
322
323 func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
324 t.SetPathWithOptions(strings.Split(key, "."), opts, value)
325 }
326
327
328
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
336 }
337 switch node := nextTree.(type) {
338 case *Tree:
339 subtree = node
340 case []*Tree:
341
342 if len(node) == 0 {
343
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
382
383
384 func (t *Tree) Set(key string, value interface{}) {
385 t.SetWithComment(key, "", false, value)
386 }
387
388
389
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
395
396
397 func (t *Tree) SetPath(keys []string, value interface{}) {
398 t.SetPathWithComment(keys, "", false, value)
399 }
400
401
402
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
408
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
418
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
436
437
438
439
440
441
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
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
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
521 func Load(content string) (tree *Tree, err error) {
522 return LoadBytes([]byte(content))
523 }
524
525
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 }
534
View as plain text