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