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:
+26
-3
@@ -21,6 +21,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user