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