25 commented bool |
25 commented bool |
26 position Position |
26 position Position |
27 } |
27 } |
28 |
28 |
29 func newTree() *Tree { |
29 func newTree() *Tree { |
|
30 return newTreeWithPosition(Position{}) |
|
31 } |
|
32 |
|
33 func newTreeWithPosition(pos Position) *Tree { |
30 return &Tree{ |
34 return &Tree{ |
31 values: make(map[string]interface{}), |
35 values: make(map[string]interface{}), |
32 position: Position{}, |
36 position: pos, |
33 } |
37 } |
34 } |
38 } |
35 |
39 |
36 // TreeFromMap initializes a new Tree object using the given map. |
40 // TreeFromMap initializes a new Tree object using the given map. |
37 func TreeFromMap(m map[string]interface{}) (*Tree, error) { |
41 func TreeFromMap(m map[string]interface{}) (*Tree, error) { |
192 |
196 |
193 // SetPathWithOptions is the same as SetPath, but allows you to provide |
197 // SetPathWithOptions is the same as SetPath, but allows you to provide |
194 // formatting instructions to the key, that will be reused by Marshal(). |
198 // formatting instructions to the key, that will be reused by Marshal(). |
195 func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) { |
199 func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) { |
196 subtree := t |
200 subtree := t |
197 for _, intermediateKey := range keys[:len(keys)-1] { |
201 for i, intermediateKey := range keys[:len(keys)-1] { |
198 nextTree, exists := subtree.values[intermediateKey] |
202 nextTree, exists := subtree.values[intermediateKey] |
199 if !exists { |
203 if !exists { |
200 nextTree = newTree() |
204 nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) |
201 subtree.values[intermediateKey] = nextTree // add new element here |
205 subtree.values[intermediateKey] = nextTree // add new element here |
202 } |
206 } |
203 switch node := nextTree.(type) { |
207 switch node := nextTree.(type) { |
204 case *Tree: |
208 case *Tree: |
205 subtree = node |
209 subtree = node |
206 case []*Tree: |
210 case []*Tree: |
207 // go to most recent element |
211 // go to most recent element |
208 if len(node) == 0 { |
212 if len(node) == 0 { |
209 // create element if it does not exist |
213 // create element if it does not exist |
210 subtree.values[intermediateKey] = append(node, newTree()) |
214 subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) |
211 } |
215 } |
212 subtree = node[len(node)-1] |
216 subtree = node[len(node)-1] |
213 } |
217 } |
214 } |
218 } |
215 |
219 |
216 var toInsert interface{} |
220 var toInsert interface{} |
217 |
221 |
218 switch value.(type) { |
222 switch v := value.(type) { |
219 case *Tree: |
223 case *Tree: |
220 tt := value.(*Tree) |
224 v.comment = opts.Comment |
221 tt.comment = opts.Comment |
|
222 toInsert = value |
225 toInsert = value |
223 case []*Tree: |
226 case []*Tree: |
224 toInsert = value |
227 toInsert = value |
225 case *tomlValue: |
228 case *tomlValue: |
226 tt := value.(*tomlValue) |
229 v.comment = opts.Comment |
227 tt.comment = opts.Comment |
230 toInsert = v |
228 toInsert = tt |
|
229 default: |
231 default: |
230 toInsert = &tomlValue{value: value, comment: opts.Comment, commented: opts.Commented, multiline: opts.Multiline} |
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}} |
231 } |
237 } |
232 |
238 |
233 subtree.values[keys[len(keys)-1]] = toInsert |
239 subtree.values[keys[len(keys)-1]] = toInsert |
234 } |
240 } |
235 |
241 |
254 } |
260 } |
255 |
261 |
256 // SetPathWithComment is the same as SetPath, but allows you to provide comment |
262 // SetPathWithComment is the same as SetPath, but allows you to provide comment |
257 // information to the key, that will be reused by Marshal(). |
263 // information to the key, that will be reused by Marshal(). |
258 func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) { |
264 func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) { |
259 subtree := t |
265 t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value) |
260 for _, intermediateKey := range keys[:len(keys)-1] { |
266 } |
261 nextTree, exists := subtree.values[intermediateKey] |
267 |
262 if !exists { |
268 // Delete removes a key from the tree. |
263 nextTree = newTree() |
269 // Key is a dot-separated path (e.g. a.b.c). |
264 subtree.values[intermediateKey] = nextTree // add new element here |
270 func (t *Tree) Delete(key string) error { |
265 } |
271 keys, err := parseKey(key) |
266 switch node := nextTree.(type) { |
272 if err != nil { |
267 case *Tree: |
273 return err |
268 subtree = node |
274 } |
269 case []*Tree: |
275 return t.DeletePath(keys) |
270 // go to most recent element |
276 } |
271 if len(node) == 0 { |
277 |
272 // create element if it does not exist |
278 // DeletePath removes a key from the tree. |
273 subtree.values[intermediateKey] = append(node, newTree()) |
279 // Keys is an array of path elements (e.g. {"a","b","c"}). |
274 } |
280 func (t *Tree) DeletePath(keys []string) error { |
275 subtree = node[len(node)-1] |
281 keyLen := len(keys) |
276 } |
282 if keyLen == 1 { |
277 } |
283 delete(t.values, keys[0]) |
278 |
284 return nil |
279 var toInsert interface{} |
285 } |
280 |
286 tree := t.GetPath(keys[:keyLen-1]) |
281 switch value.(type) { |
287 item := keys[keyLen-1] |
|
288 switch node := tree.(type) { |
282 case *Tree: |
289 case *Tree: |
283 tt := value.(*Tree) |
290 delete(node.values, item) |
284 tt.comment = comment |
291 return nil |
285 toInsert = value |
292 } |
286 case []*Tree: |
293 return errors.New("no such key to delete") |
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 } |
294 } |
298 |
295 |
299 // createSubTree takes a tree and a key and create the necessary intermediate |
296 // createSubTree takes a tree and a key and create the necessary intermediate |
300 // subtrees to create a subtree at that point. In-place. |
297 // subtrees to create a subtree at that point. In-place. |
301 // |
298 // |
303 // and tree[a][b][c] |
300 // and tree[a][b][c] |
304 // |
301 // |
305 // Returns nil on success, error object on failure |
302 // Returns nil on success, error object on failure |
306 func (t *Tree) createSubTree(keys []string, pos Position) error { |
303 func (t *Tree) createSubTree(keys []string, pos Position) error { |
307 subtree := t |
304 subtree := t |
308 for _, intermediateKey := range keys { |
305 for i, intermediateKey := range keys { |
309 nextTree, exists := subtree.values[intermediateKey] |
306 nextTree, exists := subtree.values[intermediateKey] |
310 if !exists { |
307 if !exists { |
311 tree := newTree() |
308 tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) |
312 tree.position = pos |
309 tree.position = pos |
313 subtree.values[intermediateKey] = tree |
310 subtree.values[intermediateKey] = tree |
314 nextTree = tree |
311 nextTree = tree |
315 } |
312 } |
316 |
313 |
335 panic(r) |
332 panic(r) |
336 } |
333 } |
337 err = errors.New(r.(string)) |
334 err = errors.New(r.(string)) |
338 } |
335 } |
339 }() |
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 |
340 tree = parseToml(lexToml(b)) |
346 tree = parseToml(lexToml(b)) |
341 return |
347 return |
|
348 } |
|
349 |
|
350 func hasUTF16BigEndianBOM2(b []byte) bool { |
|
351 return b[0] == 0xFE && b[1] == 0xFF |
|
352 } |
|
353 |
|
354 func hasUTF16LittleEndianBOM2(b []byte) bool { |
|
355 return b[0] == 0xFF && b[1] == 0xFE |
|
356 } |
|
357 |
|
358 func hasUTF8BOM3(b []byte) bool { |
|
359 return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF |
|
360 } |
|
361 |
|
362 func hasUTF32BigEndianBOM4(b []byte) bool { |
|
363 return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF |
|
364 } |
|
365 |
|
366 func hasUTF32LittleEndianBOM4(b []byte) bool { |
|
367 return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00 |
342 } |
368 } |
343 |
369 |
344 // LoadReader creates a Tree from any io.Reader. |
370 // LoadReader creates a Tree from any io.Reader. |
345 func LoadReader(reader io.Reader) (tree *Tree, err error) { |
371 func LoadReader(reader io.Reader) (tree *Tree, err error) { |
346 inputBytes, err := ioutil.ReadAll(reader) |
372 inputBytes, err := ioutil.ReadAll(reader) |