Make values come before tables in ToString output (#111)
If no order on the key is enforced in ToString, the following tree:
foo = 1
bar = "baz"
foobar = true
[qux]
foo = 1
bar = "baz"
may come out as:
bar = "baz"
foobar = true
[qux]
foo = 1
bar = "baz"
foo = 1
which is incorrect, since putting that back to the parser would panic
because of a duplicated key (qux.foo). Those changes make sure that
leaf values come before tables in the ToString output.
diff --git a/tomltree_conversions.go b/tomltree_conversions.go
index bf9321b..08efebc 100644
--- a/tomltree_conversions.go
+++ b/tomltree_conversions.go
@@ -94,55 +94,64 @@
// Recursive support function for ToString()
// Outputs a tree, using the provided keyspace to prefix group names
func (t *TomlTree) toToml(indent, keyspace string) string {
- result := ""
+ resultChunks := []string{}
for k, v := range t.values {
// figure out the keyspace
combinedKey := k
if keyspace != "" {
combinedKey = keyspace + "." + combinedKey
}
+ resultChunk := ""
// output based on type
switch node := v.(type) {
case []*TomlTree:
for _, item := range node {
if len(item.Keys()) > 0 {
- result += fmt.Sprintf("\n%s[[%s]]\n", indent, combinedKey)
+ resultChunk += fmt.Sprintf("\n%s[[%s]]\n", indent, combinedKey)
}
- result += item.toToml(indent+" ", combinedKey)
+ resultChunk += item.toToml(indent+" ", combinedKey)
}
+ resultChunks = append(resultChunks, resultChunk)
case *TomlTree:
if len(node.Keys()) > 0 {
- result += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
+ resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
- result += node.toToml(indent+" ", combinedKey)
+ resultChunk += node.toToml(indent+" ", combinedKey)
+ resultChunks = append(resultChunks, resultChunk)
case map[string]interface{}:
sub := TreeFromMap(node)
if len(sub.Keys()) > 0 {
- result += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
+ resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
- result += sub.toToml(indent+" ", combinedKey)
+ resultChunk += sub.toToml(indent+" ", combinedKey)
+ resultChunks = append(resultChunks, resultChunk)
case map[string]string:
sub := TreeFromMap(convertMapStringString(node))
if len(sub.Keys()) > 0 {
- result += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
+ resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
- result += sub.toToml(indent+" ", combinedKey)
+ resultChunk += sub.toToml(indent+" ", combinedKey)
+ resultChunks = append(resultChunks, resultChunk)
case map[interface{}]interface{}:
sub := TreeFromMap(convertMapInterfaceInterface(node))
if len(sub.Keys()) > 0 {
- result += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
+ resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
- result += sub.toToml(indent+" ", combinedKey)
+ resultChunk += sub.toToml(indent+" ", combinedKey)
+ resultChunks = append(resultChunks, resultChunk)
case *tomlValue:
- result += fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(node.value, 0))
+ resultChunk = fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(node.value, 0))
+ resultChunks = append([]string{resultChunk}, resultChunks...)
default:
- result += fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(v, 0))
+ resultChunk = fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(v, 0))
+ resultChunks = append([]string{resultChunk}, resultChunks...)
}
+
}
- return result
+ return strings.Join(resultChunks, "")
}
func convertMapStringString(in map[string]string) map[string]interface{} {
diff --git a/tomltree_conversions_test.go b/tomltree_conversions_test.go
index 58dbf4e..4ad9009 100644
--- a/tomltree_conversions_test.go
+++ b/tomltree_conversions_test.go
@@ -4,6 +4,7 @@
"reflect"
"testing"
"time"
+ "strings"
)
func TestTomlTreeConversionToString(t *testing.T) {
@@ -28,6 +29,41 @@
})
}
+func TestTomlTreeConversionToStringKeysOrders(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ tree, _ := Load(`
+ foobar = true
+ bar = "baz"
+ foo = 1
+ [qux]
+ foo = 1
+ bar = "baz2"`)
+
+ stringRepr := tree.ToString()
+
+ t.Log("Intermediate string representation:")
+ t.Log(stringRepr)
+
+ r := strings.NewReader(stringRepr)
+ toml, err := LoadReader(r)
+
+
+ if err != nil {
+ t.Fatal("Unexpected error:", err)
+ }
+
+ assertTree(t, toml, err, map[string]interface{}{
+ "foobar": true,
+ "bar": "baz",
+ "foo": 1,
+ "qux": map[string]interface{}{
+ "foo": 1,
+ "bar": "baz2",
+ },
+ })
+ }
+}
+
func testMaps(t *testing.T, actual, expected map[string]interface{}) {
if !reflect.DeepEqual(actual, expected) {
t.Fatal("trees aren't equal.\n", "Expected:\n", expected, "\nActual:\n", actual)