Skip to content

Commit

Permalink
Add Encoder opt to emit arrays on multiple lines
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pelletier committed Dec 5, 2017
1 parent 4e9e0ee commit 4d7ce2c
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 13 deletions.
29 changes: 26 additions & 3 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type tomlOpts struct {

type encOpts struct {
quoteMapKeys bool
arraysOneElementPerLine bool
}

var encOptsDefaults = encOpts{
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
71 changes: 71 additions & 0 deletions marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -732,3 +733,73 @@ 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)
}
}
2 changes: 1 addition & 1 deletion parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
37 changes: 28 additions & 9 deletions tomltree_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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.
Expand Down

0 comments on commit 4d7ce2c

Please sign in to comment.