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:
Thomas Pelletier
2017-12-18 14:57:16 +01:00
committed by GitHub
parent 4e9e0ee19b
commit b8b5e76965
4 changed files with 128 additions and 14 deletions
+27 -4
View File
@@ -20,7 +20,8 @@ type tomlOpts struct {
} }
type encOpts struct { type encOpts struct {
quoteMapKeys bool quoteMapKeys bool
arraysOneElementPerLine bool
} }
var encOptsDefaults = encOpts{ var encOptsDefaults = encOpts{
@@ -174,6 +175,25 @@ func (e *Encoder) QuoteMapKeys(v bool) *Encoder {
return e 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) { func (e *Encoder) marshal(v interface{}) ([]byte, error) {
mtype := reflect.TypeOf(v) mtype := reflect.TypeOf(v)
if mtype.Kind() != reflect.Struct { if mtype.Kind() != reflect.Struct {
@@ -187,8 +207,11 @@ func (e *Encoder) marshal(v interface{}) ([]byte, error) {
if err != nil { if err != nil {
return []byte{}, err 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 // 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 return nil, err
} }
if e.quoteMapKeys { if e.quoteMapKeys {
keyStr, err := tomlValueStringRepresentation(key.String()) keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+72
View File
@@ -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) t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
} }
} }
func TestDecodeQuotedMapKeys(t *testing.T) { func TestDecodeQuotedMapKeys(t *testing.T) {
result := mapsTestStruct{} result := mapsTestStruct{}
err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result) 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) 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
View File
@@ -652,7 +652,7 @@ func TestTomlValueStringRepresentation(t *testing.T) {
"[\"gamma\",\"delta\"]"}, "[\"gamma\",\"delta\"]"},
{nil, ""}, {nil, ""},
} { } {
result, err := tomlValueStringRepresentation(item.Value) result, err := tomlValueStringRepresentation(item.Value, "", false)
if err != nil { if err != nil {
t.Errorf("Test %d - unexpected error: %s", idx, err) t.Errorf("Test %d - unexpected error: %s", idx, err)
} }
+28 -9
View File
@@ -44,7 +44,7 @@ func encodeTomlString(value string) string {
return b.String() return b.String()
} }
func tomlValueStringRepresentation(v interface{}) (string, error) { func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) {
switch value := v.(type) { switch value := v.(type) {
case uint64: case uint64:
return strconv.FormatUint(value, 10), nil return strconv.FormatUint(value, 10), nil
@@ -61,7 +61,7 @@ func tomlValueStringRepresentation(v interface{}) (string, error) {
return "\"" + encodeTomlString(value) + "\"", nil return "\"" + encodeTomlString(value) + "\"", nil
case []byte: case []byte:
b, _ := v.([]byte) b, _ := v.([]byte)
return tomlValueStringRepresentation(string(b)) return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine)
case bool: case bool:
if value { if value {
return "true", nil return "true", nil
@@ -76,21 +76,40 @@ func tomlValueStringRepresentation(v interface{}) (string, error) {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Slice { if rv.Kind() == reflect.Slice {
values := []string{} var values []string
for i := 0; i < rv.Len(); i++ { for i := 0; i < rv.Len(); i++ {
item := rv.Index(i).Interface() item := rv.Index(i).Interface()
itemRepr, err := tomlValueStringRepresentation(item) itemRepr, err := tomlValueStringRepresentation(item, indent, arraysOneElementPerLine)
if err != nil { if err != nil {
return "", err return "", err
} }
values = append(values, itemRepr) 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 "[" + strings.Join(values, ",") + "]", nil
} }
return "", fmt.Errorf("unsupported value type %T: %v", v, v) 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) simpleValuesKeys := make([]string, 0)
complexValuesKeys := 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]) 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 { if err != nil {
return bytesCount, err return bytesCount, err
} }
@@ -178,7 +197,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
if err != nil { if err != nil {
return bytesCount, err return bytesCount, err
} }
bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount) bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine)
if err != nil { if err != nil {
return bytesCount, err return bytesCount, err
} }
@@ -190,7 +209,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
return bytesCount, err return bytesCount, err
} }
bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount) bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine)
if err != nil { if err != nil {
return bytesCount, err 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. // 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. // 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) { 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. // ToTomlString generates a human-readable representation of the current tree.