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