151 return "[" + strings.Join(values, ",") + "]", nil |
178 return "[" + strings.Join(values, ",") + "]", nil |
152 } |
179 } |
153 return "", fmt.Errorf("unsupported value type %T: %v", v, v) |
180 return "", fmt.Errorf("unsupported value type %T: %v", v, v) |
154 } |
181 } |
155 |
182 |
156 func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { |
183 func getTreeArrayLine(trees []*Tree) (line int) { |
157 simpleValuesKeys := make([]string, 0) |
184 // get lowest line number that is not 0 |
158 complexValuesKeys := make([]string, 0) |
185 for _, tv := range trees { |
|
186 if tv.position.Line < line || line == 0 { |
|
187 line = tv.position.Line |
|
188 } |
|
189 } |
|
190 return |
|
191 } |
|
192 |
|
193 func sortByLines(t *Tree) (vals []sortNode) { |
|
194 var ( |
|
195 line int |
|
196 lines []int |
|
197 tv *Tree |
|
198 tom *tomlValue |
|
199 node sortNode |
|
200 ) |
|
201 vals = make([]sortNode, 0) |
|
202 m := make(map[int]sortNode) |
|
203 |
|
204 for k := range t.values { |
|
205 v := t.values[k] |
|
206 switch v.(type) { |
|
207 case *Tree: |
|
208 tv = v.(*Tree) |
|
209 line = tv.position.Line |
|
210 node = sortNode{key: k, complexity: valueComplex} |
|
211 case []*Tree: |
|
212 line = getTreeArrayLine(v.([]*Tree)) |
|
213 node = sortNode{key: k, complexity: valueComplex} |
|
214 default: |
|
215 tom = v.(*tomlValue) |
|
216 line = tom.position.Line |
|
217 node = sortNode{key: k, complexity: valueSimple} |
|
218 } |
|
219 lines = append(lines, line) |
|
220 vals = append(vals, node) |
|
221 m[line] = node |
|
222 } |
|
223 sort.Ints(lines) |
|
224 |
|
225 for i, line := range lines { |
|
226 vals[i] = m[line] |
|
227 } |
|
228 |
|
229 return vals |
|
230 } |
|
231 |
|
232 func sortAlphabetical(t *Tree) (vals []sortNode) { |
|
233 var ( |
|
234 node sortNode |
|
235 simpVals []string |
|
236 compVals []string |
|
237 ) |
|
238 vals = make([]sortNode, 0) |
|
239 m := make(map[string]sortNode) |
159 |
240 |
160 for k := range t.values { |
241 for k := range t.values { |
161 v := t.values[k] |
242 v := t.values[k] |
162 switch v.(type) { |
243 switch v.(type) { |
163 case *Tree, []*Tree: |
244 case *Tree, []*Tree: |
164 complexValuesKeys = append(complexValuesKeys, k) |
245 node = sortNode{key: k, complexity: valueComplex} |
|
246 compVals = append(compVals, node.key) |
165 default: |
247 default: |
166 simpleValuesKeys = append(simpleValuesKeys, k) |
248 node = sortNode{key: k, complexity: valueSimple} |
167 } |
249 simpVals = append(simpVals, node.key) |
168 } |
250 } |
169 |
251 vals = append(vals, node) |
170 sort.Strings(simpleValuesKeys) |
252 m[node.key] = node |
171 sort.Strings(complexValuesKeys) |
253 } |
172 |
254 |
173 for _, k := range simpleValuesKeys { |
255 // Simples first to match previous implementation |
174 v, ok := t.values[k].(*tomlValue) |
256 sort.Strings(simpVals) |
175 if !ok { |
257 i := 0 |
176 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) |
258 for _, key := range simpVals { |
177 } |
259 vals[i] = m[key] |
178 |
260 i++ |
179 repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine) |
261 } |
180 if err != nil { |
262 |
181 return bytesCount, err |
263 sort.Strings(compVals) |
182 } |
264 for _, key := range compVals { |
183 |
265 vals[i] = m[key] |
184 if v.comment != "" { |
266 i++ |
185 comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) |
267 } |
186 start := "# " |
268 |
187 if strings.HasPrefix(comment, "#") { |
269 return vals |
188 start = "" |
270 } |
189 } |
271 |
190 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") |
272 func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { |
191 bytesCount += int64(writtenBytesCountComment) |
273 return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical) |
192 if errc != nil { |
274 } |
193 return bytesCount, errc |
275 |
194 } |
276 func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder) (int64, error) { |
195 } |
277 var orderedVals []sortNode |
196 |
278 |
197 var commented string |
279 switch ord { |
198 if v.commented { |
280 case OrderPreserve: |
199 commented = "# " |
281 orderedVals = sortByLines(t) |
200 } |
282 default: |
201 writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n") |
283 orderedVals = sortAlphabetical(t) |
202 bytesCount += int64(writtenBytesCount) |
284 } |
203 if err != nil { |
285 |
204 return bytesCount, err |
286 for _, node := range orderedVals { |
205 } |
287 switch node.complexity { |
206 } |
288 case valueComplex: |
207 |
289 k := node.key |
208 for _, k := range complexValuesKeys { |
290 v := t.values[k] |
209 v := t.values[k] |
291 |
210 |
292 combinedKey := k |
211 combinedKey := k |
293 if keyspace != "" { |
212 if keyspace != "" { |
294 combinedKey = keyspace + "." + combinedKey |
213 combinedKey = keyspace + "." + combinedKey |
295 } |
214 } |
296 var commented string |
215 var commented string |
297 if t.commented { |
216 if t.commented { |
298 commented = "# " |
217 commented = "# " |
299 } |
218 } |
300 |
219 |
301 switch node := v.(type) { |
220 switch node := v.(type) { |
302 // node has to be of those two types given how keys are sorted above |
221 // node has to be of those two types given how keys are sorted above |
303 case *Tree: |
222 case *Tree: |
304 tv, ok := t.values[k].(*Tree) |
223 tv, ok := t.values[k].(*Tree) |
305 if !ok { |
|
306 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) |
|
307 } |
|
308 if tv.comment != "" { |
|
309 comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) |
|
310 start := "# " |
|
311 if strings.HasPrefix(comment, "#") { |
|
312 start = "" |
|
313 } |
|
314 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) |
|
315 bytesCount += int64(writtenBytesCountComment) |
|
316 if errc != nil { |
|
317 return bytesCount, errc |
|
318 } |
|
319 } |
|
320 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") |
|
321 bytesCount += int64(writtenBytesCount) |
|
322 if err != nil { |
|
323 return bytesCount, err |
|
324 } |
|
325 bytesCount, err = node.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord) |
|
326 if err != nil { |
|
327 return bytesCount, err |
|
328 } |
|
329 case []*Tree: |
|
330 for _, subTree := range node { |
|
331 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") |
|
332 bytesCount += int64(writtenBytesCount) |
|
333 if err != nil { |
|
334 return bytesCount, err |
|
335 } |
|
336 |
|
337 bytesCount, err = subTree.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord) |
|
338 if err != nil { |
|
339 return bytesCount, err |
|
340 } |
|
341 } |
|
342 } |
|
343 default: // Simple |
|
344 k := node.key |
|
345 v, ok := t.values[k].(*tomlValue) |
224 if !ok { |
346 if !ok { |
225 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) |
347 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) |
226 } |
348 } |
227 if tv.comment != "" { |
349 |
228 comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) |
350 repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine) |
|
351 if err != nil { |
|
352 return bytesCount, err |
|
353 } |
|
354 |
|
355 if v.comment != "" { |
|
356 comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) |
229 start := "# " |
357 start := "# " |
230 if strings.HasPrefix(comment, "#") { |
358 if strings.HasPrefix(comment, "#") { |
231 start = "" |
359 start = "" |
232 } |
360 } |
233 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) |
361 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") |
234 bytesCount += int64(writtenBytesCountComment) |
362 bytesCount += int64(writtenBytesCountComment) |
235 if errc != nil { |
363 if errc != nil { |
236 return bytesCount, errc |
364 return bytesCount, errc |
237 } |
365 } |
238 } |
366 } |
239 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") |
367 |
|
368 var commented string |
|
369 if v.commented { |
|
370 commented = "# " |
|
371 } |
|
372 quotedKey := quoteKeyIfNeeded(k) |
|
373 writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n") |
240 bytesCount += int64(writtenBytesCount) |
374 bytesCount += int64(writtenBytesCount) |
241 if err != nil { |
375 if err != nil { |
242 return bytesCount, err |
376 return bytesCount, err |
243 } |
377 } |
244 bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine) |
|
245 if err != nil { |
|
246 return bytesCount, err |
|
247 } |
|
248 case []*Tree: |
|
249 for _, subTree := range node { |
|
250 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") |
|
251 bytesCount += int64(writtenBytesCount) |
|
252 if err != nil { |
|
253 return bytesCount, err |
|
254 } |
|
255 |
|
256 bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine) |
|
257 if err != nil { |
|
258 return bytesCount, err |
|
259 } |
|
260 } |
|
261 } |
378 } |
262 } |
379 } |
263 |
380 |
264 return bytesCount, nil |
381 return bytesCount, nil |
|
382 } |
|
383 |
|
384 // quote a key if it does not fit the bare key format (A-Za-z0-9_-) |
|
385 // quoted keys use the same rules as strings |
|
386 func quoteKeyIfNeeded(k string) string { |
|
387 // when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain |
|
388 // keys that have already been quoted. |
|
389 // not an ideal situation, but good enough of a stop gap. |
|
390 if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' { |
|
391 return k |
|
392 } |
|
393 isBare := true |
|
394 for _, r := range k { |
|
395 if !isValidBareChar(r) { |
|
396 isBare = false |
|
397 break |
|
398 } |
|
399 } |
|
400 if isBare { |
|
401 return k |
|
402 } |
|
403 return quoteKey(k) |
|
404 } |
|
405 |
|
406 func quoteKey(k string) string { |
|
407 return "\"" + encodeTomlString(k) + "\"" |
265 } |
408 } |
266 |
409 |
267 func writeStrings(w io.Writer, s ...string) (int, error) { |
410 func writeStrings(w io.Writer, s ...string) (int, error) { |
268 var n int |
411 var n int |
269 for i := range s { |
412 for i := range s { |