13 type tomlValue struct { |
13 type tomlValue struct { |
14 value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list |
14 value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list |
15 comment string |
15 comment string |
16 commented bool |
16 commented bool |
17 multiline bool |
17 multiline bool |
|
18 literal bool |
18 position Position |
19 position Position |
19 } |
20 } |
20 |
21 |
21 // Tree is the result of the parsing of a TOML file. |
22 // Tree is the result of the parsing of a TOML file. |
22 type Tree struct { |
23 type Tree struct { |
23 values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree |
24 values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree |
24 comment string |
25 comment string |
25 commented bool |
26 commented bool |
|
27 inline bool |
26 position Position |
28 position Position |
27 } |
29 } |
28 |
30 |
29 func newTree() *Tree { |
31 func newTree() *Tree { |
30 return newTreeWithPosition(Position{}) |
32 return newTreeWithPosition(Position{}) |
119 default: |
121 default: |
120 return node |
122 return node |
121 } |
123 } |
122 } |
124 } |
123 |
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. |
|
131 func (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. |
|
140 func (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{} |
|
178 func 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 |
124 // GetPosition returns the position of the given key. |
209 // GetPosition returns the position of the given key. |
125 func (t *Tree) GetPosition(key string) Position { |
210 func (t *Tree) GetPosition(key string) Position { |
126 if key == "" { |
211 if key == "" { |
127 return t.position |
212 return t.position |
128 } |
213 } |
129 return t.GetPositionPath(strings.Split(key, ".")) |
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. |
|
219 func (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 } |
130 } |
259 } |
131 |
260 |
132 // GetPositionPath returns the element in the tree indicated by 'keys'. |
261 // GetPositionPath returns the element in the tree indicated by 'keys'. |
133 // If keys is of length zero, the current tree is returned. |
262 // If keys is of length zero, the current tree is returned. |
134 func (t *Tree) GetPositionPath(keys []string) Position { |
263 func (t *Tree) GetPositionPath(keys []string) Position { |
184 // The default values within the struct are valid default options. |
313 // The default values within the struct are valid default options. |
185 type SetOptions struct { |
314 type SetOptions struct { |
186 Comment string |
315 Comment string |
187 Commented bool |
316 Commented bool |
188 Multiline bool |
317 Multiline bool |
|
318 Literal bool |
189 } |
319 } |
190 |
320 |
191 // SetWithOptions is the same as Set, but allows you to provide formatting |
321 // SetWithOptions is the same as Set, but allows you to provide formatting |
192 // instructions to the key, that will be used by Marshal(). |
322 // instructions to the key, that will be used by Marshal(). |
193 func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) { |
323 func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) { |
209 subtree = node |
339 subtree = node |
210 case []*Tree: |
340 case []*Tree: |
211 // go to most recent element |
341 // go to most recent element |
212 if len(node) == 0 { |
342 if len(node) == 0 { |
213 // create element if it does not exist |
343 // create element if it does not exist |
214 subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) |
344 node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) |
|
345 subtree.values[intermediateKey] = node |
215 } |
346 } |
216 subtree = node[len(node)-1] |
347 subtree = node[len(node)-1] |
217 } |
348 } |
218 } |
349 } |
219 |
350 |
220 var toInsert interface{} |
351 var toInsert interface{} |
221 |
352 |
222 switch v := value.(type) { |
353 switch v := value.(type) { |
223 case *Tree: |
354 case *Tree: |
224 v.comment = opts.Comment |
355 v.comment = opts.Comment |
|
356 v.commented = opts.Commented |
225 toInsert = value |
357 toInsert = value |
226 case []*Tree: |
358 case []*Tree: |
|
359 for i := range v { |
|
360 v[i].commented = opts.Commented |
|
361 } |
227 toInsert = value |
362 toInsert = value |
228 case *tomlValue: |
363 case *tomlValue: |
229 v.comment = opts.Comment |
364 v.comment = opts.Comment |
|
365 v.commented = opts.Commented |
|
366 v.multiline = opts.Multiline |
|
367 v.literal = opts.Literal |
230 toInsert = v |
368 toInsert = v |
231 default: |
369 default: |
232 toInsert = &tomlValue{value: value, |
370 toInsert = &tomlValue{value: value, |
233 comment: opts.Comment, |
371 comment: opts.Comment, |
234 commented: opts.Commented, |
372 commented: opts.Commented, |
235 multiline: opts.Multiline, |
373 multiline: opts.Multiline, |
|
374 literal: opts.Literal, |
236 position: Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}} |
375 position: Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}} |
237 } |
376 } |
238 |
377 |
239 subtree.values[keys[len(keys)-1]] = toInsert |
378 subtree.values[keys[len(keys)-1]] = toInsert |
240 } |
379 } |
305 for i, intermediateKey := range keys { |
444 for i, intermediateKey := range keys { |
306 nextTree, exists := subtree.values[intermediateKey] |
445 nextTree, exists := subtree.values[intermediateKey] |
307 if !exists { |
446 if !exists { |
308 tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) |
447 tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) |
309 tree.position = pos |
448 tree.position = pos |
|
449 tree.inline = subtree.inline |
310 subtree.values[intermediateKey] = tree |
450 subtree.values[intermediateKey] = tree |
311 nextTree = tree |
451 nextTree = tree |
312 } |
452 } |
313 |
453 |
314 switch node := nextTree.(type) { |
454 switch node := nextTree.(type) { |