Add Encoder opt to emit arrays on multiple lines (#203)
A new Encoder option emits arrays with more than one line on multiple lines. This is off by default and toggled with `ArraysWithOneElementPerLine`. For example: ``` A = [1,2,3] ``` Becomes: ``` A = [ 1, 2, 3 ] ``` Fixes #200
This commit is contained in:
+27
-4
@@ -20,7 +20,8 @@ type tomlOpts struct {
|
||||
}
|
||||
|
||||
type encOpts struct {
|
||||
quoteMapKeys bool
|
||||
quoteMapKeys bool
|
||||
arraysOneElementPerLine bool
|
||||
}
|
||||
|
||||
var encOptsDefaults = encOpts{
|
||||
@@ -174,6 +175,25 @@ func (e *Encoder) QuoteMapKeys(v bool) *Encoder {
|
||||
return e
|
||||
}
|
||||
|
||||
// ArraysWithOneElementPerLine sets up the encoder to encode arrays
|
||||
// with more than one element on multiple lines instead of one.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// A = [1,2,3]
|
||||
//
|
||||
// Becomes
|
||||
//
|
||||
// A = [
|
||||
// 1,
|
||||
// 2,
|
||||
// 3
|
||||
// ]
|
||||
func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder {
|
||||
e.arraysOneElementPerLine = v
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Encoder) marshal(v interface{}) ([]byte, error) {
|
||||
mtype := reflect.TypeOf(v)
|
||||
if mtype.Kind() != reflect.Struct {
|
||||
@@ -187,8 +207,11 @@ func (e *Encoder) marshal(v interface{}) ([]byte, error) {
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
s, err := t.ToTomlString()
|
||||
return []byte(s), err
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = t.writeTo(&buf, "", "", 0, e.arraysOneElementPerLine)
|
||||
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// Convert given marshal struct or map value to toml tree
|
||||
@@ -218,7 +241,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
|
||||
return nil, err
|
||||
}
|
||||
if e.quoteMapKeys {
|
||||
keyStr, err := tomlValueStringRepresentation(key.String())
|
||||
keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -721,6 +721,7 @@ func TestEncodeQuotedMapKeys(t *testing.T) {
|
||||
t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeQuotedMapKeys(t *testing.T) {
|
||||
result := mapsTestStruct{}
|
||||
err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result)
|
||||
@@ -732,3 +733,74 @@ func TestDecodeQuotedMapKeys(t *testing.T) {
|
||||
t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
type structArrayNoTag struct {
|
||||
A struct {
|
||||
B []int64
|
||||
C []int64
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalArray(t *testing.T) {
|
||||
expected := []byte(`
|
||||
[A]
|
||||
B = [1,2,3]
|
||||
C = [1]
|
||||
`)
|
||||
|
||||
m := structArrayNoTag{
|
||||
A: struct {
|
||||
B []int64
|
||||
C []int64
|
||||
}{
|
||||
B: []int64{1, 2, 3},
|
||||
C: []int64{1},
|
||||
},
|
||||
}
|
||||
|
||||
b, err := Marshal(m)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(b, expected) {
|
||||
t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalArrayOnePerLine(t *testing.T) {
|
||||
expected := []byte(`
|
||||
[A]
|
||||
B = [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
C = [1]
|
||||
`)
|
||||
|
||||
m := structArrayNoTag{
|
||||
A: struct {
|
||||
B []int64
|
||||
C []int64
|
||||
}{
|
||||
B: []int64{1, 2, 3},
|
||||
C: []int64{1},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true)
|
||||
err := encoder.Encode(m)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b := buf.Bytes()
|
||||
|
||||
if !bytes.Equal(b, expected) {
|
||||
t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -652,7 +652,7 @@ func TestTomlValueStringRepresentation(t *testing.T) {
|
||||
"[\"gamma\",\"delta\"]"},
|
||||
{nil, ""},
|
||||
} {
|
||||
result, err := tomlValueStringRepresentation(item.Value)
|
||||
result, err := tomlValueStringRepresentation(item.Value, "", false)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d - unexpected error: %s", idx, err)
|
||||
}
|
||||
|
||||
+28
-9
@@ -44,7 +44,7 @@ func encodeTomlString(value string) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func tomlValueStringRepresentation(v interface{}) (string, error) {
|
||||
func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) {
|
||||
switch value := v.(type) {
|
||||
case uint64:
|
||||
return strconv.FormatUint(value, 10), nil
|
||||
@@ -61,7 +61,7 @@ func tomlValueStringRepresentation(v interface{}) (string, error) {
|
||||
return "\"" + encodeTomlString(value) + "\"", nil
|
||||
case []byte:
|
||||
b, _ := v.([]byte)
|
||||
return tomlValueStringRepresentation(string(b))
|
||||
return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine)
|
||||
case bool:
|
||||
if value {
|
||||
return "true", nil
|
||||
@@ -76,21 +76,40 @@ func tomlValueStringRepresentation(v interface{}) (string, error) {
|
||||
rv := reflect.ValueOf(v)
|
||||
|
||||
if rv.Kind() == reflect.Slice {
|
||||
values := []string{}
|
||||
var values []string
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
item := rv.Index(i).Interface()
|
||||
itemRepr, err := tomlValueStringRepresentation(item)
|
||||
itemRepr, err := tomlValueStringRepresentation(item, indent, arraysOneElementPerLine)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
values = append(values, itemRepr)
|
||||
}
|
||||
if arraysOneElementPerLine && len(values) > 1 {
|
||||
stringBuffer := bytes.Buffer{}
|
||||
valueIndent := indent + ` ` // TODO: move that to a shared encoder state
|
||||
|
||||
stringBuffer.WriteString("[\n")
|
||||
|
||||
for i, value := range values {
|
||||
stringBuffer.WriteString(valueIndent)
|
||||
stringBuffer.WriteString(value)
|
||||
if i != len(values)-1 {
|
||||
stringBuffer.WriteString(`,`)
|
||||
}
|
||||
stringBuffer.WriteString("\n")
|
||||
}
|
||||
|
||||
stringBuffer.WriteString(indent + "]")
|
||||
|
||||
return stringBuffer.String(), nil
|
||||
}
|
||||
return "[" + strings.Join(values, ",") + "]", nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupported value type %T: %v", v, v)
|
||||
}
|
||||
|
||||
func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (int64, error) {
|
||||
func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
|
||||
simpleValuesKeys := make([]string, 0)
|
||||
complexValuesKeys := make([]string, 0)
|
||||
|
||||
@@ -113,7 +132,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
|
||||
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
|
||||
}
|
||||
|
||||
repr, err := tomlValueStringRepresentation(v.value)
|
||||
repr, err := tomlValueStringRepresentation(v.value, indent, arraysOneElementPerLine)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
@@ -178,7 +197,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount)
|
||||
bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
@@ -190,7 +209,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
|
||||
return bytesCount, err
|
||||
}
|
||||
|
||||
bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount)
|
||||
bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
@@ -216,7 +235,7 @@ func writeStrings(w io.Writer, s ...string) (int, error) {
|
||||
// WriteTo encode the Tree as Toml and writes it to the writer w.
|
||||
// Returns the number of bytes written in case of success, or an error if anything happened.
|
||||
func (t *Tree) WriteTo(w io.Writer) (int64, error) {
|
||||
return t.writeTo(w, "", "", 0)
|
||||
return t.writeTo(w, "", "", 0, false)
|
||||
}
|
||||
|
||||
// ToTomlString generates a human-readable representation of the current tree.
|
||||
|
||||
Reference in New Issue
Block a user