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.
This commit is contained in:
+23
-14
@@ -94,55 +94,64 @@ func toTomlValue(item interface{}, indent int) string {
|
|||||||
// Recursive support function for ToString()
|
// Recursive support function for ToString()
|
||||||
// Outputs a tree, using the provided keyspace to prefix group names
|
// Outputs a tree, using the provided keyspace to prefix group names
|
||||||
func (t *TomlTree) toToml(indent, keyspace string) string {
|
func (t *TomlTree) toToml(indent, keyspace string) string {
|
||||||
result := ""
|
resultChunks := []string{}
|
||||||
for k, v := range t.values {
|
for k, v := range t.values {
|
||||||
// figure out the keyspace
|
// figure out the keyspace
|
||||||
combinedKey := k
|
combinedKey := k
|
||||||
if keyspace != "" {
|
if keyspace != "" {
|
||||||
combinedKey = keyspace + "." + combinedKey
|
combinedKey = keyspace + "." + combinedKey
|
||||||
}
|
}
|
||||||
|
resultChunk := ""
|
||||||
// output based on type
|
// output based on type
|
||||||
switch node := v.(type) {
|
switch node := v.(type) {
|
||||||
case []*TomlTree:
|
case []*TomlTree:
|
||||||
for _, item := range node {
|
for _, item := range node {
|
||||||
if len(item.Keys()) > 0 {
|
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:
|
case *TomlTree:
|
||||||
if len(node.Keys()) > 0 {
|
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{}:
|
case map[string]interface{}:
|
||||||
sub := TreeFromMap(node)
|
sub := TreeFromMap(node)
|
||||||
|
|
||||||
if len(sub.Keys()) > 0 {
|
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:
|
case map[string]string:
|
||||||
sub := TreeFromMap(convertMapStringString(node))
|
sub := TreeFromMap(convertMapStringString(node))
|
||||||
|
|
||||||
if len(sub.Keys()) > 0 {
|
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{}:
|
case map[interface{}]interface{}:
|
||||||
sub := TreeFromMap(convertMapInterfaceInterface(node))
|
sub := TreeFromMap(convertMapInterfaceInterface(node))
|
||||||
|
|
||||||
if len(sub.Keys()) > 0 {
|
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:
|
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:
|
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{} {
|
func convertMapStringString(in map[string]string) map[string]interface{} {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTomlTreeConversionToString(t *testing.T) {
|
func TestTomlTreeConversionToString(t *testing.T) {
|
||||||
@@ -28,6 +29,41 @@ points = { x = 1, y = 2 }`)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{}) {
|
func testMaps(t *testing.T, actual, expected map[string]interface{}) {
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
t.Fatal("trees aren't equal.\n", "Expected:\n", expected, "\nActual:\n", actual)
|
t.Fatal("trees aren't equal.\n", "Expected:\n", expected, "\nActual:\n", actual)
|
||||||
|
|||||||
Reference in New Issue
Block a user