Add Encoder/Decoder types (#192)
Usage is similar to the stdlibs JSON encoder/decoder but I tried to leave the general structure of the code the same. Main motivation was to support encoding/decoding options to allow encoding string-type map keys as quoted TOML keys. This was implemented on the Encoder with QuoteMapKeys(bool). > The TOML spec supports using UTF-8 strings as keys. > https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md#table
This commit is contained in:
committed by
Thomas Pelletier
parent
8c31c2ec65
commit
4e9e0ee19b
+139
-39
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -18,6 +19,14 @@ type tomlOpts struct {
|
|||||||
omitempty bool
|
omitempty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type encOpts struct {
|
||||||
|
quoteMapKeys bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var encOptsDefaults = encOpts{
|
||||||
|
quoteMapKeys: false,
|
||||||
|
}
|
||||||
|
|
||||||
var timeType = reflect.TypeOf(time.Time{})
|
var timeType = reflect.TypeOf(time.Time{})
|
||||||
var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
||||||
|
|
||||||
@@ -125,6 +134,47 @@ Tree primitive types and corresponding marshal types:
|
|||||||
time.Time time.Time{}, pointers to same
|
time.Time time.Time{}, pointers to same
|
||||||
*/
|
*/
|
||||||
func Marshal(v interface{}) ([]byte, error) {
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return NewEncoder(nil).marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder writes TOML values to an output stream.
|
||||||
|
type Encoder struct {
|
||||||
|
w io.Writer
|
||||||
|
encOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new encoder that writes to w.
|
||||||
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
|
return &Encoder{
|
||||||
|
w: w,
|
||||||
|
encOpts: encOptsDefaults,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the TOML encoding of v to the stream.
|
||||||
|
//
|
||||||
|
// See the documentation for Marshal for details.
|
||||||
|
func (e *Encoder) Encode(v interface{}) error {
|
||||||
|
b, err := e.marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := e.w.Write(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuoteMapKeys sets up the encoder to encode
|
||||||
|
// maps with string type keys with quoted TOML keys.
|
||||||
|
//
|
||||||
|
// This relieves the character limitations on map keys.
|
||||||
|
func (e *Encoder) QuoteMapKeys(v bool) *Encoder {
|
||||||
|
e.quoteMapKeys = v
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
return []byte{}, errors.New("Only a struct can be marshaled to TOML")
|
return []byte{}, errors.New("Only a struct can be marshaled to TOML")
|
||||||
@@ -133,7 +183,7 @@ func Marshal(v interface{}) ([]byte, error) {
|
|||||||
if isCustomMarshaler(mtype) {
|
if isCustomMarshaler(mtype) {
|
||||||
return callCustomMarshaler(sval)
|
return callCustomMarshaler(sval)
|
||||||
}
|
}
|
||||||
t, err := valueToTree(mtype, sval)
|
t, err := e.valueToTree(mtype, sval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
@@ -142,9 +192,9 @@ func Marshal(v interface{}) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert given marshal struct or map value to toml tree
|
// Convert given marshal struct or map value to toml tree
|
||||||
func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
|
func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
|
||||||
if mtype.Kind() == reflect.Ptr {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return valueToTree(mtype.Elem(), mval.Elem())
|
return e.valueToTree(mtype.Elem(), mval.Elem())
|
||||||
}
|
}
|
||||||
tval := newTree()
|
tval := newTree()
|
||||||
switch mtype.Kind() {
|
switch mtype.Kind() {
|
||||||
@@ -153,7 +203,7 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
|
|||||||
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
||||||
opts := tomlOptions(mtypef)
|
opts := tomlOptions(mtypef)
|
||||||
if opts.include && (!opts.omitempty || !isZero(mvalf)) {
|
if opts.include && (!opts.omitempty || !isZero(mvalf)) {
|
||||||
val, err := valueToToml(mtypef.Type, mvalf)
|
val, err := e.valueToToml(mtypef.Type, mvalf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -163,21 +213,29 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
|
|||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
for _, key := range mval.MapKeys() {
|
for _, key := range mval.MapKeys() {
|
||||||
mvalf := mval.MapIndex(key)
|
mvalf := mval.MapIndex(key)
|
||||||
val, err := valueToToml(mtype.Elem(), mvalf)
|
val, err := e.valueToToml(mtype.Elem(), mvalf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if e.quoteMapKeys {
|
||||||
|
keyStr, err := tomlValueStringRepresentation(key.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tval.SetPath([]string{keyStr}, "", false, val)
|
||||||
|
} else {
|
||||||
tval.Set(key.String(), "", false, val)
|
tval.Set(key.String(), "", false, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return tval, nil
|
return tval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert given marshal slice to slice of Toml trees
|
// Convert given marshal slice to slice of Toml trees
|
||||||
func valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) {
|
func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) {
|
||||||
tval := make([]*Tree, mval.Len(), mval.Len())
|
tval := make([]*Tree, mval.Len(), mval.Len())
|
||||||
for i := 0; i < mval.Len(); i++ {
|
for i := 0; i < mval.Len(); i++ {
|
||||||
val, err := valueToTree(mtype.Elem(), mval.Index(i))
|
val, err := e.valueToTree(mtype.Elem(), mval.Index(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -187,10 +245,10 @@ func valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert given marshal slice to slice of toml values
|
// Convert given marshal slice to slice of toml values
|
||||||
func valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||||
tval := make([]interface{}, mval.Len(), mval.Len())
|
tval := make([]interface{}, mval.Len(), mval.Len())
|
||||||
for i := 0; i < mval.Len(); i++ {
|
for i := 0; i < mval.Len(); i++ {
|
||||||
val, err := valueToToml(mtype.Elem(), mval.Index(i))
|
val, err := e.valueToToml(mtype.Elem(), mval.Index(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -200,19 +258,19 @@ func valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert given marshal value to toml value
|
// Convert given marshal value to toml value
|
||||||
func valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||||
if mtype.Kind() == reflect.Ptr {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return valueToToml(mtype.Elem(), mval.Elem())
|
return e.valueToToml(mtype.Elem(), mval.Elem())
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case isCustomMarshaler(mtype):
|
case isCustomMarshaler(mtype):
|
||||||
return callCustomMarshaler(mval)
|
return callCustomMarshaler(mval)
|
||||||
case isTree(mtype):
|
case isTree(mtype):
|
||||||
return valueToTree(mtype, mval)
|
return e.valueToTree(mtype, mval)
|
||||||
case isTreeSlice(mtype):
|
case isTreeSlice(mtype):
|
||||||
return valueToTreeSlice(mtype, mval)
|
return e.valueToTreeSlice(mtype, mval)
|
||||||
case isOtherSlice(mtype):
|
case isOtherSlice(mtype):
|
||||||
return valueToOtherSlice(mtype, mval)
|
return e.valueToOtherSlice(mtype, mval)
|
||||||
default:
|
default:
|
||||||
switch mtype.Kind() {
|
switch mtype.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
@@ -237,17 +295,16 @@ func valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
|||||||
// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for
|
// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for
|
||||||
// sub-structs, and only definite types can be unmarshaled.
|
// sub-structs, and only definite types can be unmarshaled.
|
||||||
func (t *Tree) Unmarshal(v interface{}) error {
|
func (t *Tree) Unmarshal(v interface{}) error {
|
||||||
mtype := reflect.TypeOf(v)
|
d := Decoder{tval: t}
|
||||||
if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct {
|
return d.unmarshal(v)
|
||||||
return errors.New("Only a pointer to struct can be unmarshaled from TOML")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sval, err := valueFromTree(mtype.Elem(), t)
|
// Marshal returns the TOML encoding of Tree.
|
||||||
if err != nil {
|
// See Marshal() documentation for types mapping table.
|
||||||
return err
|
func (t *Tree) Marshal() ([]byte, error) {
|
||||||
}
|
var buf bytes.Buffer
|
||||||
reflect.ValueOf(v).Elem().Set(sval)
|
err := NewEncoder(&buf).Encode(t)
|
||||||
return nil
|
return buf.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parses the TOML-encoded data and stores the result in the value
|
// Unmarshal parses the TOML-encoded data and stores the result in the value
|
||||||
@@ -269,10 +326,52 @@ func Unmarshal(data []byte, v interface{}) error {
|
|||||||
return t.Unmarshal(v)
|
return t.Unmarshal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decoder reads and decodes TOML values from an input stream.
|
||||||
|
type Decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
tval *Tree
|
||||||
|
encOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new decoder that reads from r.
|
||||||
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
|
return &Decoder{
|
||||||
|
r: r,
|
||||||
|
encOpts: encOptsDefaults,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode reads a TOML-encoded value from it's input
|
||||||
|
// and unmarshals it in the value pointed at by v.
|
||||||
|
//
|
||||||
|
// See the documentation for Marshal for details.
|
||||||
|
func (d *Decoder) Decode(v interface{}) error {
|
||||||
|
var err error
|
||||||
|
d.tval, err = LoadReader(d.r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.unmarshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) unmarshal(v interface{}) error {
|
||||||
|
mtype := reflect.TypeOf(v)
|
||||||
|
if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct {
|
||||||
|
return errors.New("Only a pointer to struct can be unmarshaled from TOML")
|
||||||
|
}
|
||||||
|
|
||||||
|
sval, err := d.valueFromTree(mtype.Elem(), d.tval)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reflect.ValueOf(v).Elem().Set(sval)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Convert toml tree to marshal struct or map, using marshal type
|
// Convert toml tree to marshal struct or map, using marshal type
|
||||||
func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
||||||
if mtype.Kind() == reflect.Ptr {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return unwrapPointer(mtype, tval)
|
return d.unwrapPointer(mtype, tval)
|
||||||
}
|
}
|
||||||
var mval reflect.Value
|
var mval reflect.Value
|
||||||
switch mtype.Kind() {
|
switch mtype.Kind() {
|
||||||
@@ -290,7 +389,7 @@ func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val := tval.Get(key)
|
val := tval.Get(key)
|
||||||
mvalf, err := valueFromToml(mtypef.Type, val)
|
mvalf, err := d.valueFromToml(mtypef.Type, val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mval, formatError(err, tval.GetPosition(key))
|
return mval, formatError(err, tval.GetPosition(key))
|
||||||
}
|
}
|
||||||
@@ -302,8 +401,9 @@ func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
|||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
mval = reflect.MakeMap(mtype)
|
mval = reflect.MakeMap(mtype)
|
||||||
for _, key := range tval.Keys() {
|
for _, key := range tval.Keys() {
|
||||||
val := tval.Get(key)
|
// TODO: path splits key
|
||||||
mvalf, err := valueFromToml(mtype.Elem(), val)
|
val := tval.GetPath([]string{key})
|
||||||
|
mvalf, err := d.valueFromToml(mtype.Elem(), val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mval, formatError(err, tval.GetPosition(key))
|
return mval, formatError(err, tval.GetPosition(key))
|
||||||
}
|
}
|
||||||
@@ -314,10 +414,10 @@ func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert toml value to marshal struct/map slice, using marshal type
|
// Convert toml value to marshal struct/map slice, using marshal type
|
||||||
func valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
|
func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
|
||||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||||
for i := 0; i < len(tval); i++ {
|
for i := 0; i < len(tval); i++ {
|
||||||
val, err := valueFromTree(mtype.Elem(), tval[i])
|
val, err := d.valueFromTree(mtype.Elem(), tval[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mval, err
|
return mval, err
|
||||||
}
|
}
|
||||||
@@ -327,10 +427,10 @@ func valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert toml value to marshal primitive slice, using marshal type
|
// Convert toml value to marshal primitive slice, using marshal type
|
||||||
func valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
||||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||||
for i := 0; i < len(tval); i++ {
|
for i := 0; i < len(tval); i++ {
|
||||||
val, err := valueFromToml(mtype.Elem(), tval[i])
|
val, err := d.valueFromToml(mtype.Elem(), tval[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mval, err
|
return mval, err
|
||||||
}
|
}
|
||||||
@@ -340,27 +440,27 @@ func valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert toml value to marshal value, using marshal type
|
// Convert toml value to marshal value, using marshal type
|
||||||
func valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||||
if mtype.Kind() == reflect.Ptr {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return unwrapPointer(mtype, tval)
|
return d.unwrapPointer(mtype, tval)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch tval.(type) {
|
switch tval.(type) {
|
||||||
case *Tree:
|
case *Tree:
|
||||||
if isTree(mtype) {
|
if isTree(mtype) {
|
||||||
return valueFromTree(mtype, tval.(*Tree))
|
return d.valueFromTree(mtype, tval.(*Tree))
|
||||||
} else {
|
} else {
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
|
||||||
}
|
}
|
||||||
case []*Tree:
|
case []*Tree:
|
||||||
if isTreeSlice(mtype) {
|
if isTreeSlice(mtype) {
|
||||||
return valueFromTreeSlice(mtype, tval.([]*Tree))
|
return d.valueFromTreeSlice(mtype, tval.([]*Tree))
|
||||||
} else {
|
} else {
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
|
||||||
}
|
}
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
if isOtherSlice(mtype) {
|
if isOtherSlice(mtype) {
|
||||||
return valueFromOtherSlice(mtype, tval.([]interface{}))
|
return d.valueFromOtherSlice(mtype, tval.([]interface{}))
|
||||||
} else {
|
} else {
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval)
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval)
|
||||||
}
|
}
|
||||||
@@ -462,8 +562,8 @@ func valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||||
val, err := valueFromToml(mtype.Elem(), tval)
|
val, err := d.valueFromToml(mtype.Elem(), tval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return reflect.ValueOf(nil), err
|
return reflect.ValueOf(nil), err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -658,3 +658,77 @@ func TestMarshalComment(t *testing.T) {
|
|||||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mapsTestStruct struct {
|
||||||
|
Simple map[string]string
|
||||||
|
Paths map[string]string
|
||||||
|
Other map[string]float64
|
||||||
|
X struct {
|
||||||
|
Y struct {
|
||||||
|
Z map[string]bool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapsTestData = mapsTestStruct{
|
||||||
|
Simple: map[string]string{
|
||||||
|
"one plus one": "two",
|
||||||
|
"next": "three",
|
||||||
|
},
|
||||||
|
Paths: map[string]string{
|
||||||
|
"/this/is/a/path": "/this/is/also/a/path",
|
||||||
|
"/heloo.txt": "/tmp/lololo.txt",
|
||||||
|
},
|
||||||
|
Other: map[string]float64{
|
||||||
|
"testing": 3.9999,
|
||||||
|
},
|
||||||
|
X: struct{ Y struct{ Z map[string]bool } }{
|
||||||
|
Y: struct{ Z map[string]bool }{
|
||||||
|
Z: map[string]bool{
|
||||||
|
"is.Nested": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var mapsTestToml = []byte(`
|
||||||
|
[Other]
|
||||||
|
"testing" = 3.9999
|
||||||
|
|
||||||
|
[Paths]
|
||||||
|
"/heloo.txt" = "/tmp/lololo.txt"
|
||||||
|
"/this/is/a/path" = "/this/is/also/a/path"
|
||||||
|
|
||||||
|
[Simple]
|
||||||
|
"next" = "three"
|
||||||
|
"one plus one" = "two"
|
||||||
|
|
||||||
|
[X]
|
||||||
|
|
||||||
|
[X.Y]
|
||||||
|
|
||||||
|
[X.Y.Z]
|
||||||
|
"is.Nested" = true
|
||||||
|
`)
|
||||||
|
|
||||||
|
func TestEncodeQuotedMapKeys(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result := buf.Bytes()
|
||||||
|
expected := mapsTestToml
|
||||||
|
if !bytes.Equal(result, expected) {
|
||||||
|
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)
|
||||||
|
expected := mapsTestData
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(result, expected) {
|
||||||
|
t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user