13 type tomlValue struct {
14 value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
20 // Tree is the result of the parsing of a TOML file.
22 values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
28 func newTree() *Tree {
30 values: make(map[string]interface{}),
35 // TreeFromMap initializes a new Tree object using the given map.
36 func TreeFromMap(m map[string]interface{}) (*Tree, error) {
37 result, err := toTree(m)
41 return result.(*Tree), nil
44 // Position returns the position of the tree.
45 func (t *Tree) Position() Position {
49 // Has returns a boolean indicating if the given key exists.
50 func (t *Tree) Has(key string) bool {
54 return t.HasPath(strings.Split(key, "."))
57 // HasPath returns true if the given path of keys exists, false otherwise.
58 func (t *Tree) HasPath(keys []string) bool {
59 return t.GetPath(keys) != nil
62 // Keys returns the keys of the toplevel tree (does not recurse).
63 func (t *Tree) Keys() []string {
64 keys := make([]string, len(t.values))
66 for k := range t.values {
73 // Get the value at key in the Tree.
74 // Key is a dot-separated path (e.g. a.b.c).
75 // Returns nil if the path does not exist in the tree.
76 // If keys is of length zero, the current tree is returned.
77 func (t *Tree) Get(key string) interface{} {
81 comps, err := parseKey(key)
85 return t.GetPath(comps)
88 // GetPath returns the element in the tree indicated by 'keys'.
89 // If keys is of length zero, the current tree is returned.
90 func (t *Tree) GetPath(keys []string) interface{} {
95 for _, intermediateKey := range keys[:len(keys)-1] {
96 value, exists := subtree.values[intermediateKey]
100 switch node := value.(type) {
104 // go to most recent element
108 subtree = node[len(node)-1]
110 return nil // cannot navigate through other node types
113 // branch based on final node type
114 switch node := subtree.values[keys[len(keys)-1]].(type) {
122 // GetPosition returns the position of the given key.
123 func (t *Tree) GetPosition(key string) Position {
127 return t.GetPositionPath(strings.Split(key, "."))
130 // GetPositionPath returns the element in the tree indicated by 'keys'.
131 // If keys is of length zero, the current tree is returned.
132 func (t *Tree) GetPositionPath(keys []string) Position {
137 for _, intermediateKey := range keys[:len(keys)-1] {
138 value, exists := subtree.values[intermediateKey]
140 return Position{0, 0}
142 switch node := value.(type) {
146 // go to most recent element
148 return Position{0, 0}
150 subtree = node[len(node)-1]
152 return Position{0, 0}
155 // branch based on final node type
156 switch node := subtree.values[keys[len(keys)-1]].(type) {
162 // go to most recent element
164 return Position{0, 0}
166 return node[len(node)-1].position
168 return Position{0, 0}
172 // GetDefault works like Get but with a default value
173 func (t *Tree) GetDefault(key string, def interface{}) interface{} {
181 // Set an element in the tree.
182 // Key is a dot-separated path (e.g. a.b.c).
183 // Creates all necessary intermediate trees, if needed.
184 func (t *Tree) Set(key string, comment string, commented bool, value interface{}) {
185 t.SetPath(strings.Split(key, "."), comment, commented, value)
188 // SetPath sets an element in the tree.
189 // Keys is an array of path elements (e.g. {"a","b","c"}).
190 // Creates all necessary intermediate trees, if needed.
191 func (t *Tree) SetPath(keys []string, comment string, commented bool, value interface{}) {
193 for _, intermediateKey := range keys[:len(keys)-1] {
194 nextTree, exists := subtree.values[intermediateKey]
197 subtree.values[intermediateKey] = nextTree // add new element here
199 switch node := nextTree.(type) {
203 // go to most recent element
205 // create element if it does not exist
206 subtree.values[intermediateKey] = append(node, newTree())
208 subtree = node[len(node)-1]
212 var toInsert interface{}
214 switch value.(type) {
222 tt := value.(*tomlValue)
226 toInsert = &tomlValue{value: value, comment: comment, commented: commented}
229 subtree.values[keys[len(keys)-1]] = toInsert
232 // createSubTree takes a tree and a key and create the necessary intermediate
233 // subtrees to create a subtree at that point. In-place.
235 // e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
238 // Returns nil on success, error object on failure
239 func (t *Tree) createSubTree(keys []string, pos Position) error {
241 for _, intermediateKey := range keys {
242 nextTree, exists := subtree.values[intermediateKey]
246 subtree.values[intermediateKey] = tree
250 switch node := nextTree.(type) {
252 subtree = node[len(node)-1]
256 return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
257 strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
263 // LoadBytes creates a Tree from a []byte.
264 func LoadBytes(b []byte) (tree *Tree, err error) {
266 if r := recover(); r != nil {
267 if _, ok := r.(runtime.Error); ok {
270 err = errors.New(r.(string))
273 tree = parseToml(lexToml(b))
277 // LoadReader creates a Tree from any io.Reader.
278 func LoadReader(reader io.Reader) (tree *Tree, err error) {
279 inputBytes, err := ioutil.ReadAll(reader)
283 tree, err = LoadBytes(inputBytes)
287 // Load creates a Tree from a string.
288 func Load(content string) (tree *Tree, err error) {
289 return LoadBytes([]byte(content))
292 // LoadFile creates a Tree from a file.
293 func LoadFile(path string) (tree *Tree, err error) {
294 file, err := os.Open(path)
299 return LoadReader(file)