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 position Position 27} 28 29func newTree() *Tree { 30 return newTreeWithPosition(Position{}) 31} 32 33func newTreeWithPosition(pos Position) *Tree { 34 return &Tree{ 35 values: make(map[string]interface{}), 36 position: pos, 37 } 38} 39 40// TreeFromMap initializes a new Tree object using the given map. 41func TreeFromMap(m map[string]interface{}) (*Tree, error) { 42 result, err := toTree(m) 43 if err != nil { 44 return nil, err 45 } 46 return result.(*Tree), nil 47} 48 49// Position returns the position of the tree. 50func (t *Tree) Position() Position { 51 return t.position 52} 53 54// Has returns a boolean indicating if the given key exists. 55func (t *Tree) Has(key string) bool { 56 if key == "" { 57 return false 58 } 59 return t.HasPath(strings.Split(key, ".")) 60} 61 62// HasPath returns true if the given path of keys exists, false otherwise. 63func (t *Tree) HasPath(keys []string) bool { 64 return t.GetPath(keys) != nil 65} 66 67// Keys returns the keys of the toplevel tree (does not recurse). 68func (t *Tree) Keys() []string { 69 keys := make([]string, len(t.values)) 70 i := 0 71 for k := range t.values { 72 keys[i] = k 73 i++ 74 } 75 return keys 76} 77 78// Get the value at key in the Tree. 79// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings. 80// If you need to retrieve non-bare keys, use GetPath. 81// Returns nil if the path does not exist in the tree. 82// If keys is of length zero, the current tree is returned. 83func (t *Tree) Get(key string) interface{} { 84 if key == "" { 85 return t 86 } 87 return t.GetPath(strings.Split(key, ".")) 88} 89 90// GetPath returns the element in the tree indicated by 'keys'. 91// If keys is of length zero, the current tree is returned. 92func (t *Tree) GetPath(keys []string) interface{} { 93 if len(keys) == 0 { 94 return t 95 } 96 subtree := t 97 for _, intermediateKey := range keys[:len(keys)-1] { 98 value, exists := subtree.values[intermediateKey] 99 if !exists { 100 return nil 101 } 102 switch node := value.(type) { 103 case *Tree: 104 subtree = node 105 case []*Tree: 106 // go to most recent element 107 if len(node) == 0 { 108 return nil 109 } 110 subtree = node[len(node)-1] 111 default: 112 return nil // cannot navigate through other node types 113 } 114 } 115 // branch based on final node type 116 switch node := subtree.values[keys[len(keys)-1]].(type) { 117 case *tomlValue: 118 return node.value 119 default: 120 return node 121 } 122} 123 124// GetPosition returns the position of the given key. 125func (t *Tree) GetPosition(key string) Position { 126 if key == "" { 127 return t.position 128 } 129 return t.GetPositionPath(strings.Split(key, ".")) 130} 131 132// GetPositionPath returns the element in the tree indicated by 'keys'. 133// If keys is of length zero, the current tree is returned. 134func (t *Tree) GetPositionPath(keys []string) Position { 135 if len(keys) == 0 { 136 return t.position 137 } 138 subtree := t 139 for _, intermediateKey := range keys[:len(keys)-1] { 140 value, exists := subtree.values[intermediateKey] 141 if !exists { 142 return Position{0, 0} 143 } 144 switch node := value.(type) { 145 case *Tree: 146 subtree = node 147 case []*Tree: 148 // go to most recent element 149 if len(node) == 0 { 150 return Position{0, 0} 151 } 152 subtree = node[len(node)-1] 153 default: 154 return Position{0, 0} 155 } 156 } 157 // branch based on final node type 158 switch node := subtree.values[keys[len(keys)-1]].(type) { 159 case *tomlValue: 160 return node.position 161 case *Tree: 162 return node.position 163 case []*Tree: 164 // go to most recent element 165 if len(node) == 0 { 166 return Position{0, 0} 167 } 168 return node[len(node)-1].position 169 default: 170 return Position{0, 0} 171 } 172} 173 174// GetDefault works like Get but with a default value 175func (t *Tree) GetDefault(key string, def interface{}) interface{} { 176 val := t.Get(key) 177 if val == nil { 178 return def 179 } 180 return val 181} 182 183// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour. 184// The default values within the struct are valid default options. 185type SetOptions struct { 186 Comment string 187 Commented bool 188 Multiline bool 189} 190 191// SetWithOptions is the same as Set, but allows you to provide formatting 192// instructions to the key, that will be used by Marshal(). 193func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) { 194 t.SetPathWithOptions(strings.Split(key, "."), opts, value) 195} 196 197// SetPathWithOptions is the same as SetPath, but allows you to provide 198// formatting instructions to the key, that will be reused by Marshal(). 199func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) { 200 subtree := t 201 for i, intermediateKey := range keys[:len(keys)-1] { 202 nextTree, exists := subtree.values[intermediateKey] 203 if !exists { 204 nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) 205 subtree.values[intermediateKey] = nextTree // add new element here 206 } 207 switch node := nextTree.(type) { 208 case *Tree: 209 subtree = node 210 case []*Tree: 211 // go to most recent element 212 if len(node) == 0 { 213 // create element if it does not exist 214 subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) 215 } 216 subtree = node[len(node)-1] 217 } 218 } 219 220 var toInsert interface{} 221 222 switch v := value.(type) { 223 case *Tree: 224 v.comment = opts.Comment 225 toInsert = value 226 case []*Tree: 227 toInsert = value 228 case *tomlValue: 229 v.comment = opts.Comment 230 toInsert = v 231 default: 232 toInsert = &tomlValue{value: value, 233 comment: opts.Comment, 234 commented: opts.Commented, 235 multiline: opts.Multiline, 236 position: Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}} 237 } 238 239 subtree.values[keys[len(keys)-1]] = toInsert 240} 241 242// Set an element in the tree. 243// Key is a dot-separated path (e.g. a.b.c). 244// Creates all necessary intermediate trees, if needed. 245func (t *Tree) Set(key string, value interface{}) { 246 t.SetWithComment(key, "", false, value) 247} 248 249// SetWithComment is the same as Set, but allows you to provide comment 250// information to the key, that will be reused by Marshal(). 251func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) { 252 t.SetPathWithComment(strings.Split(key, "."), comment, commented, value) 253} 254 255// SetPath sets an element in the tree. 256// Keys is an array of path elements (e.g. {"a","b","c"}). 257// Creates all necessary intermediate trees, if needed. 258func (t *Tree) SetPath(keys []string, value interface{}) { 259 t.SetPathWithComment(keys, "", false, value) 260} 261 262// SetPathWithComment is the same as SetPath, but allows you to provide comment 263// information to the key, that will be reused by Marshal(). 264func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) { 265 t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value) 266} 267 268// Delete removes a key from the tree. 269// Key is a dot-separated path (e.g. a.b.c). 270func (t *Tree) Delete(key string) error { 271 keys, err := parseKey(key) 272 if err != nil { 273 return err 274 } 275 return t.DeletePath(keys) 276} 277 278// DeletePath removes a key from the tree. 279// Keys is an array of path elements (e.g. {"a","b","c"}). 280func (t *Tree) DeletePath(keys []string) error { 281 keyLen := len(keys) 282 if keyLen == 1 { 283 delete(t.values, keys[0]) 284 return nil 285 } 286 tree := t.GetPath(keys[:keyLen-1]) 287 item := keys[keyLen-1] 288 switch node := tree.(type) { 289 case *Tree: 290 delete(node.values, item) 291 return nil 292 } 293 return errors.New("no such key to delete") 294} 295 296// createSubTree takes a tree and a key and create the necessary intermediate 297// subtrees to create a subtree at that point. In-place. 298// 299// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b] 300// and tree[a][b][c] 301// 302// Returns nil on success, error object on failure 303func (t *Tree) createSubTree(keys []string, pos Position) error { 304 subtree := t 305 for i, intermediateKey := range keys { 306 nextTree, exists := subtree.values[intermediateKey] 307 if !exists { 308 tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) 309 tree.position = pos 310 subtree.values[intermediateKey] = tree 311 nextTree = tree 312 } 313 314 switch node := nextTree.(type) { 315 case []*Tree: 316 subtree = node[len(node)-1] 317 case *Tree: 318 subtree = node 319 default: 320 return fmt.Errorf("unknown type for path %s (%s): %T (%#v)", 321 strings.Join(keys, "."), intermediateKey, nextTree, nextTree) 322 } 323 } 324 return nil 325} 326 327// LoadBytes creates a Tree from a []byte. 328func LoadBytes(b []byte) (tree *Tree, err error) { 329 defer func() { 330 if r := recover(); r != nil { 331 if _, ok := r.(runtime.Error); ok { 332 panic(r) 333 } 334 err = errors.New(r.(string)) 335 } 336 }() 337 338 if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) { 339 b = b[4:] 340 } else if len(b) >= 3 && hasUTF8BOM3(b) { 341 b = b[3:] 342 } else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) { 343 b = b[2:] 344 } 345 346 tree = parseToml(lexToml(b)) 347 return 348} 349 350func hasUTF16BigEndianBOM2(b []byte) bool { 351 return b[0] == 0xFE && b[1] == 0xFF 352} 353 354func hasUTF16LittleEndianBOM2(b []byte) bool { 355 return b[0] == 0xFF && b[1] == 0xFE 356} 357 358func hasUTF8BOM3(b []byte) bool { 359 return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 360} 361 362func hasUTF32BigEndianBOM4(b []byte) bool { 363 return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF 364} 365 366func hasUTF32LittleEndianBOM4(b []byte) bool { 367 return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00 368} 369 370// LoadReader creates a Tree from any io.Reader. 371func LoadReader(reader io.Reader) (tree *Tree, err error) { 372 inputBytes, err := ioutil.ReadAll(reader) 373 if err != nil { 374 return 375 } 376 tree, err = LoadBytes(inputBytes) 377 return 378} 379 380// Load creates a Tree from a string. 381func Load(content string) (tree *Tree, err error) { 382 return LoadBytes([]byte(content)) 383} 384 385// LoadFile creates a Tree from a file. 386func LoadFile(path string) (tree *Tree, err error) { 387 file, err := os.Open(path) 388 if err != nil { 389 return nil, err 390 } 391 defer file.Close() 392 return LoadReader(file) 393} 394