diff --git a/parser_test.go b/parser_test.go index 048f514..188876b 100644 --- a/parser_test.go +++ b/parser_test.go @@ -673,7 +673,10 @@ func TestToString(t *testing.T) { t.Errorf("Test failed to parse: %v", err) return } - result := tree.ToString() + result, err := tree.ToString() + if err != nil { + t.Errorf("Unexpected error: %s", err) + } expected := "\n[foo]\n\n [[foo.bar]]\n a = 42\n\n [[foo.bar]]\n a = 69\n" if result != expected { t.Errorf("Expected got '%s', expected '%s'", result, expected) diff --git a/tomltree_conversions.go b/tomltree_conversions.go index db3da0d..fc8f22b 100644 --- a/tomltree_conversions.go +++ b/tomltree_conversions.go @@ -87,7 +87,7 @@ func toTomlValue(item interface{}, indent int) string { case nil: return "" default: - panic(fmt.Sprintf("unsupported value type %T: %v", value, value)) + panic(fmt.Errorf("unsupported value type %T: %v", value, value)) } } @@ -154,6 +154,23 @@ func (t *TomlTree) toToml(indent, keyspace string) string { return strings.Join(resultChunks, "") } +// Same as ToToml(), but does not panic and returns an error +func (t *TomlTree) toTomlSafe(indent, keyspace string) (result string, err error) { + defer func() { + if r := recover(); r != nil { + result = "" + switch x := r.(type) { + case error: + err = x + default: + err = fmt.Errorf("unknown panic: %s", r) + } + } + }() + result = t.toToml(indent, keyspace) + return +} + func convertMapStringString(in map[string]string) map[string]interface{} { result := make(map[string]interface{}, len(in)) for k, v := range in { @@ -170,15 +187,18 @@ func convertMapInterfaceInterface(in map[interface{}]interface{}) map[string]int return result } -// ToString is an alias for String -func (t *TomlTree) ToString() string { - return t.String() +// ToString generates a human-readable representation of the current tree. +// Output spans multiple lines, and is suitable for ingest by a TOML parser. +// If the conversion cannot be performed, ToString returns a non-nil error. +func (t *TomlTree) ToString() (string, error) { + return t.toTomlSafe("", "") } // String generates a human-readable representation of the current tree. -// Output spans multiple lines, and is suitable for ingest by a TOML parser +// Alias of ToString. func (t *TomlTree) String() string { - return t.toToml("", "") + result, _ := t.ToString() + return result } // ToMap recursively generates a representation of the current tree using map[string]interface{}. diff --git a/tomltree_conversions_test.go b/tomltree_conversions_test.go index af3e9df..40b29b7 100644 --- a/tomltree_conversions_test.go +++ b/tomltree_conversions_test.go @@ -1,6 +1,7 @@ package toml import ( + "errors" "reflect" "strings" "testing" @@ -15,7 +16,8 @@ points = { x = 1, y = 2 }`) t.Fatal("Unexpected error:", err) } - reparsedTree, err := Load(toml.ToString()) + tomlString, _ := toml.ToString() + reparsedTree, err := Load(tomlString) assertTree(t, reparsedTree, err, map[string]interface{}{ "name": map[string]interface{}{ @@ -39,7 +41,7 @@ func TestTomlTreeConversionToStringKeysOrders(t *testing.T) { foo = 1 bar = "baz2"`) - stringRepr := tree.ToString() + stringRepr, _ := tree.ToString() t.Log("Intermediate string representation:") t.Log(stringRepr) @@ -69,6 +71,19 @@ func testMaps(t *testing.T, actual, expected map[string]interface{}) { } } +func TestToStringTypeConversionError(t *testing.T) { + tree := TomlTree{ + values: map[string]interface{}{ + "thing": []string{"unsupported"}, + }, + } + _, err := tree.ToString() + expected := errors.New("unsupported value type []string: [unsupported]") + if err.Error() != expected.Error() { + t.Errorf("expecting error %s, but got %s instead", expected, err) + } +} + func TestTomlTreeConversionToMapSimple(t *testing.T) { tree, _ := Load("a = 42\nb = 17")