+459
@@ -0,0 +1,459 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
TomlTree structural types and corresponding marshal types
|
||||
-------------------------------------------------------------------------------
|
||||
*TomlTree (*)struct, (*)map[string]interface{}
|
||||
[]*TomlTree (*)[](*)struct, (*)[](*)map[string]interface{}
|
||||
[]interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{})
|
||||
interface{} (*)primitive
|
||||
|
||||
TomlTree primitive types and corresponding marshal types
|
||||
-----------------------------------------------------------
|
||||
uint64 uint, uint8-uint64, pointers to same
|
||||
int64 int, int8-uint64, pointers to same
|
||||
float64 float32, float64, pointers to same
|
||||
string string, pointers to same
|
||||
bool bool, pointers to same
|
||||
time.Time time.Time{}, pointers to same
|
||||
*/
|
||||
|
||||
type tomlOpts struct {
|
||||
name string
|
||||
include bool
|
||||
omitempty bool
|
||||
}
|
||||
|
||||
var timeType = reflect.TypeOf(time.Time{})
|
||||
|
||||
// Check if the given marshall type maps to a TomlTree primitive
|
||||
func isPrimitive(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Ptr:
|
||||
return isPrimitive(mtype.Elem())
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
return mtype == timeType
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given marshall type maps to a TomlTree slice
|
||||
func isTreeSlice(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Slice:
|
||||
return !isOtherSlice(mtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given marshall type maps to a non-TomlTree slice
|
||||
func isOtherSlice(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Ptr:
|
||||
return isOtherSlice(mtype.Elem())
|
||||
case reflect.Slice:
|
||||
return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem())
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given marshall type maps to a TomlTree
|
||||
func isTree(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Map:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
return !isPrimitive(mtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marshal returns the TOML encoding of v. Behavior is similar to the Go json
|
||||
encoder, except that there is no concept of a Marshaler interface or MarshalTOML
|
||||
function for sub-structs, and currently only definite types can be marshaled
|
||||
(i.e. no `interface{}`).
|
||||
|
||||
Note that pointers are automatically assigned the "omitempty" option, as TOML
|
||||
explicity does not handle null values (saying instead the label should be
|
||||
dropped).
|
||||
*/
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
mtype := reflect.TypeOf(v)
|
||||
if mtype.Kind() != reflect.Struct {
|
||||
return []byte{}, errors.New("Only a struct can be marshaled to TOML")
|
||||
}
|
||||
sval := reflect.ValueOf(v)
|
||||
t, err := valueToTree(mtype, sval)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
s, err := t.ToTomlString()
|
||||
return []byte(s), err
|
||||
}
|
||||
|
||||
// Convert given marshal struct or map value to toml tree
|
||||
func valueToTree(mtype reflect.Type, mval reflect.Value) (*TomlTree, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return valueToTree(mtype.Elem(), mval.Elem())
|
||||
}
|
||||
tval := newTomlTree()
|
||||
switch mtype.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < mtype.NumField(); i++ {
|
||||
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
||||
opts := tomlOptions(mtypef)
|
||||
if opts.include && (!opts.omitempty || !isZero(mvalf)) {
|
||||
val, err := valueToToml(mtypef.Type, mvalf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval.Set(opts.name, val)
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, key := range mval.MapKeys() {
|
||||
mvalf := mval.MapIndex(key)
|
||||
val, err := valueToToml(mtype.Elem(), mvalf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval.Set(key.String(), val)
|
||||
}
|
||||
}
|
||||
return tval, nil
|
||||
}
|
||||
|
||||
// Convert given marshal slice to slice of Toml trees
|
||||
func valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*TomlTree, error) {
|
||||
tval := make([]*TomlTree, mval.Len(), mval.Len())
|
||||
for i := 0; i < mval.Len(); i++ {
|
||||
val, err := valueToTree(mtype.Elem(), mval.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval[i] = val
|
||||
}
|
||||
return tval, nil
|
||||
}
|
||||
|
||||
// Convert given marshal slice to slice of toml values
|
||||
func valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||
tval := make([]interface{}, mval.Len(), mval.Len())
|
||||
for i := 0; i < mval.Len(); i++ {
|
||||
val, err := valueToToml(mtype.Elem(), mval.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval[i] = val
|
||||
}
|
||||
return tval, nil
|
||||
}
|
||||
|
||||
// Convert given marshal value to toml value
|
||||
func valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return valueToToml(mtype.Elem(), mval.Elem())
|
||||
}
|
||||
switch {
|
||||
case isTree(mtype):
|
||||
return valueToTree(mtype, mval)
|
||||
case isTreeSlice(mtype):
|
||||
return valueToTreeSlice(mtype, mval)
|
||||
case isOtherSlice(mtype):
|
||||
return valueToOtherSlice(mtype, mval)
|
||||
default:
|
||||
switch mtype.Kind() {
|
||||
case reflect.Bool:
|
||||
return mval.Bool(), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return mval.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return mval.Uint(), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return mval.Float(), nil
|
||||
case reflect.String:
|
||||
return mval.String(), nil
|
||||
case reflect.Struct:
|
||||
return mval.Interface().(time.Time), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Unmarshal parses the TOML-encoded data and stores the result in the value
|
||||
pointed to by v. Behavior is similar to the Go json encoder, except that there
|
||||
is no concept of an Unmarshaler interface or UnmarshalTOML function for
|
||||
sub-structs, and currently only definite types can be unmarshaled to (i.e. no
|
||||
`interface{}`).
|
||||
*/
|
||||
func Unmarshal(data []byte, 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")
|
||||
}
|
||||
|
||||
t, err := Load(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sval, err := valueFromTree(mtype.Elem(), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reflect.ValueOf(v).Elem().Set(sval)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert toml tree to marshal struct or map, using marshal type
|
||||
func valueFromTree(mtype reflect.Type, tval *TomlTree) (reflect.Value, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return unwrapPointer(mtype, tval)
|
||||
}
|
||||
var mval reflect.Value
|
||||
switch mtype.Kind() {
|
||||
case reflect.Struct:
|
||||
mval = reflect.New(mtype).Elem()
|
||||
for i := 0; i < mtype.NumField(); i++ {
|
||||
mtypef := mtype.Field(i)
|
||||
opts := tomlOptions(mtypef)
|
||||
if opts.include {
|
||||
key := opts.name
|
||||
exists := tval.Has(key)
|
||||
if exists {
|
||||
val := tval.Get(key)
|
||||
mvalf, err := valueFromToml(mtypef.Type, val)
|
||||
if err != nil {
|
||||
return mval, formatError(err, tval.GetPosition(key))
|
||||
}
|
||||
mval.Field(i).Set(mvalf)
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
mval = reflect.MakeMap(mtype)
|
||||
for _, key := range tval.Keys() {
|
||||
val := tval.Get(key)
|
||||
mvalf, err := valueFromToml(mtype.Elem(), val)
|
||||
if err != nil {
|
||||
return mval, formatError(err, tval.GetPosition(key))
|
||||
}
|
||||
mval.SetMapIndex(reflect.ValueOf(key), mvalf)
|
||||
}
|
||||
}
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
// Convert toml value to marshal struct/map slice, using marshal type
|
||||
func valueFromTreeSlice(mtype reflect.Type, tval []*TomlTree) (reflect.Value, error) {
|
||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||
for i := 0; i < len(tval); i++ {
|
||||
val, err := valueFromTree(mtype.Elem(), tval[i])
|
||||
if err != nil {
|
||||
return mval, err
|
||||
}
|
||||
mval.Index(i).Set(val)
|
||||
}
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
// Convert toml value to marshal primitive slice, using marshal type
|
||||
func valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||
for i := 0; i < len(tval); i++ {
|
||||
val, err := valueFromToml(mtype.Elem(), tval[i])
|
||||
if err != nil {
|
||||
return mval, err
|
||||
}
|
||||
mval.Index(i).Set(val)
|
||||
}
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
// Convert toml value to marshal value, using marshal type
|
||||
func valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return unwrapPointer(mtype, tval)
|
||||
}
|
||||
switch {
|
||||
case isTree(mtype):
|
||||
return valueFromTree(mtype, tval.(*TomlTree))
|
||||
case isTreeSlice(mtype):
|
||||
return valueFromTreeSlice(mtype, tval.([]*TomlTree))
|
||||
case isOtherSlice(mtype):
|
||||
return valueFromOtherSlice(mtype, tval.([]interface{}))
|
||||
default:
|
||||
switch mtype.Kind() {
|
||||
case reflect.Bool:
|
||||
val, ok := tval.(bool)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to bool", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(val), nil
|
||||
case reflect.Int:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(int(val)), nil
|
||||
case reflect.Int8:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(int8(val)), nil
|
||||
case reflect.Int16:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(int16(val)), nil
|
||||
case reflect.Int32:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(int32(val)), nil
|
||||
case reflect.Int64:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(val), nil
|
||||
case reflect.Uint:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(uint(val)), nil
|
||||
case reflect.Uint8:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(uint8(val)), nil
|
||||
case reflect.Uint16:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(uint16(val)), nil
|
||||
case reflect.Uint32:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(uint32(val)), nil
|
||||
case reflect.Uint64:
|
||||
val, ok := tval.(int64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(uint64(val)), nil
|
||||
case reflect.Float32:
|
||||
val, ok := tval.(float64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to float", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(float32(val)), nil
|
||||
case reflect.Float64:
|
||||
val, ok := tval.(float64)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to float", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(val), nil
|
||||
case reflect.String:
|
||||
val, ok := tval.(string)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to string", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(val), nil
|
||||
case reflect.Struct:
|
||||
val, ok := tval.(time.Time)
|
||||
if !ok {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to time", tval, tval)
|
||||
}
|
||||
return reflect.ValueOf(val), nil
|
||||
default:
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Unmarshal can't handle %v(%v)", mtype, mtype.Kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||
val, err := valueFromToml(mtype.Elem(), tval)
|
||||
if err != nil {
|
||||
return reflect.ValueOf(nil), err
|
||||
}
|
||||
mval := reflect.New(mtype.Elem())
|
||||
mval.Elem().Set(val)
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
func tomlOptions(vf reflect.StructField) tomlOpts {
|
||||
tag := vf.Tag.Get("toml")
|
||||
parse := strings.Split(tag, ",")
|
||||
result := tomlOpts{vf.Name, true, false}
|
||||
if parse[0] != "" {
|
||||
if parse[0] == "-" && len(parse) == 1 {
|
||||
result.include = false
|
||||
} else {
|
||||
result.name = strings.Trim(parse[0], " ")
|
||||
}
|
||||
}
|
||||
if vf.PkgPath != "" {
|
||||
result.include = false
|
||||
}
|
||||
if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" {
|
||||
result.omitempty = true
|
||||
}
|
||||
if vf.Type.Kind() == reflect.Ptr {
|
||||
result.omitempty = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func isZero(val reflect.Value) bool {
|
||||
switch val.Type().Kind() {
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
return val.Len() == 0
|
||||
default:
|
||||
return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func formatError(err error, pos Position) error {
|
||||
if err.Error()[0] == '(' { // Error already contains position information
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s: %s", pos, err)
|
||||
}
|
||||
+535
@@ -0,0 +1,535 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type basicMarshalTestStruct struct {
|
||||
String string `toml:"string"`
|
||||
StringList []string `toml:"strlist"`
|
||||
Sub basicMarshalTestSubStruct `toml:"subdoc"`
|
||||
SubList []basicMarshalTestSubStruct `toml:"sublist"`
|
||||
}
|
||||
|
||||
type basicMarshalTestSubStruct struct {
|
||||
String2 string
|
||||
}
|
||||
|
||||
var basicTestData = basicMarshalTestStruct{
|
||||
String: "Hello",
|
||||
StringList: []string{"Howdy", "Hey There"},
|
||||
Sub: basicMarshalTestSubStruct{"One"},
|
||||
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
|
||||
}
|
||||
|
||||
var basicTestToml = []byte(`string = "Hello"
|
||||
strlist = ["Howdy","Hey There"]
|
||||
|
||||
[subdoc]
|
||||
String2 = "One"
|
||||
|
||||
[[sublist]]
|
||||
String2 = "Two"
|
||||
|
||||
[[sublist]]
|
||||
String2 = "Three"
|
||||
`)
|
||||
|
||||
func TestBasicMarshal(t *testing.T) {
|
||||
result, err := Marshal(basicTestData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := basicTestToml
|
||||
if !bytes.Equal(result, expected) {
|
||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicUnmarshal(t *testing.T) {
|
||||
result := basicMarshalTestStruct{}
|
||||
err := Unmarshal(basicTestToml, &result)
|
||||
expected := basicTestData
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
type testDoc struct {
|
||||
Title string `toml:"title"`
|
||||
Basics testDocBasics `toml:"basic"`
|
||||
BasicLists testDocBasicLists `toml:"basic_lists"`
|
||||
BasicMap map[string]string `toml:"basic_map"`
|
||||
Subdocs testDocSubs `toml:"subdoc"`
|
||||
SubDocList []testSubDoc `toml:"subdoclist"`
|
||||
SubDocPtrs []*testSubDoc `toml:"subdocptrs"`
|
||||
err int `toml:"shouldntBeHere"`
|
||||
unexported int `toml:"shouldntBeHere"`
|
||||
Unexported2 int `toml:"-"`
|
||||
}
|
||||
|
||||
type testDocBasics struct {
|
||||
Bool bool `toml:"bool"`
|
||||
Date time.Time `toml:"date"`
|
||||
Float float32 `toml:"float"`
|
||||
Int int `toml:"int"`
|
||||
Uint uint `toml:"uint"`
|
||||
String *string `toml:"string"`
|
||||
unexported int `toml:"shouldntBeHere"`
|
||||
}
|
||||
|
||||
type testDocBasicLists struct {
|
||||
Bools []bool `toml:"bools"`
|
||||
Dates []time.Time `toml:"dates"`
|
||||
Floats []*float32 `toml:"floats"`
|
||||
Ints []int `toml:"ints"`
|
||||
Strings []string `toml:"strings"`
|
||||
UInts []uint `toml:"uints"`
|
||||
}
|
||||
|
||||
type testDocSubs struct {
|
||||
First testSubDoc `toml:"first"`
|
||||
Second *testSubDoc `toml:"second"`
|
||||
}
|
||||
|
||||
type testSubDoc struct {
|
||||
Name string `toml:"name"`
|
||||
unexported int `toml:"shouldntBeHere"`
|
||||
}
|
||||
|
||||
var biteMe = "Bite me"
|
||||
var float1 float32 = 12.3
|
||||
var float2 float32 = 45.6
|
||||
var float3 float32 = 78.9
|
||||
var subdoc = testSubDoc{"Second", 0}
|
||||
|
||||
var docData = testDoc{
|
||||
Title: "TOML Marshal Testing",
|
||||
unexported: 0,
|
||||
Unexported2: 0,
|
||||
Basics: testDocBasics{
|
||||
Bool: true,
|
||||
Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
||||
Float: 123.4,
|
||||
Int: 5000,
|
||||
Uint: 5001,
|
||||
String: &biteMe,
|
||||
unexported: 0,
|
||||
},
|
||||
BasicLists: testDocBasicLists{
|
||||
Bools: []bool{true, false, true},
|
||||
Dates: []time.Time{
|
||||
time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
||||
time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC),
|
||||
},
|
||||
Floats: []*float32{&float1, &float2, &float3},
|
||||
Ints: []int{8001, 8001, 8002},
|
||||
Strings: []string{"One", "Two", "Three"},
|
||||
UInts: []uint{5002, 5003},
|
||||
},
|
||||
BasicMap: map[string]string{
|
||||
"one": "one",
|
||||
"two": "two",
|
||||
},
|
||||
Subdocs: testDocSubs{
|
||||
First: testSubDoc{"First", 0},
|
||||
Second: &subdoc,
|
||||
},
|
||||
SubDocList: []testSubDoc{
|
||||
testSubDoc{"List.First", 0},
|
||||
testSubDoc{"List.Second", 0},
|
||||
},
|
||||
SubDocPtrs: []*testSubDoc{&subdoc},
|
||||
}
|
||||
|
||||
func TestDocMarshal(t *testing.T) {
|
||||
result, err := Marshal(docData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected, _ := ioutil.ReadFile("marshal_test.toml")
|
||||
if !bytes.Equal(result, expected) {
|
||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocUnmarshal(t *testing.T) {
|
||||
result := testDoc{}
|
||||
tomlData, _ := ioutil.ReadFile("marshal_test.toml")
|
||||
err := Unmarshal(tomlData, &result)
|
||||
expected := docData
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
resStr, _ := json.MarshalIndent(result, "", " ")
|
||||
expStr, _ := json.MarshalIndent(expected, "", " ")
|
||||
t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
|
||||
}
|
||||
}
|
||||
|
||||
type tomlTypeCheckTest struct {
|
||||
name string
|
||||
item interface{}
|
||||
typ int //0=primitive, 1=otherslice, 2=treeslice, 3=tree
|
||||
}
|
||||
|
||||
func TestTypeChecks(t *testing.T) {
|
||||
tests := []tomlTypeCheckTest{
|
||||
{"integer", 2, 0},
|
||||
{"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0},
|
||||
{"stringlist", []string{"hello", "hi"}, 1},
|
||||
{"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
|
||||
{"objectlist", []tomlTypeCheckTest{}, 2},
|
||||
{"object", tomlTypeCheckTest{}, 3},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
expected := []bool{false, false, false, false}
|
||||
expected[test.typ] = true
|
||||
result := []bool{
|
||||
isPrimitive(reflect.TypeOf(test.item)),
|
||||
isOtherSlice(reflect.TypeOf(test.item)),
|
||||
isTreeSlice(reflect.TypeOf(test.item)),
|
||||
isTree(reflect.TypeOf(test.item)),
|
||||
}
|
||||
if !reflect.DeepEqual(expected, result) {
|
||||
t.Errorf("Bad type check on %q: expected %v, got %v", test.name, expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type unexportedMarshalTestStruct struct {
|
||||
String string `toml:"string"`
|
||||
StringList []string `toml:"strlist"`
|
||||
Sub basicMarshalTestSubStruct `toml:"subdoc"`
|
||||
SubList []basicMarshalTestSubStruct `toml:"sublist"`
|
||||
unexported int `toml:"shouldntBeHere"`
|
||||
Unexported2 int `toml:"-"`
|
||||
}
|
||||
|
||||
var unexportedTestData = unexportedMarshalTestStruct{
|
||||
String: "Hello",
|
||||
StringList: []string{"Howdy", "Hey There"},
|
||||
Sub: basicMarshalTestSubStruct{"One"},
|
||||
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
|
||||
unexported: 0,
|
||||
Unexported2: 0,
|
||||
}
|
||||
|
||||
var unexportedTestToml = []byte(`string = "Hello"
|
||||
strlist = ["Howdy","Hey There"]
|
||||
unexported = 1
|
||||
shouldntBeHere = 2
|
||||
|
||||
[subdoc]
|
||||
String2 = "One"
|
||||
|
||||
[[sublist]]
|
||||
String2 = "Two"
|
||||
|
||||
[[sublist]]
|
||||
String2 = "Three"
|
||||
`)
|
||||
|
||||
func TestUnexportedUnmarshal(t *testing.T) {
|
||||
result := unexportedMarshalTestStruct{}
|
||||
err := Unmarshal(unexportedTestToml, &result)
|
||||
expected := unexportedTestData
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Bad unexported unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
type errStruct struct {
|
||||
Bool bool `toml:"bool"`
|
||||
Date time.Time `toml:"date"`
|
||||
Float float64 `toml:"float"`
|
||||
Int int16 `toml:"int"`
|
||||
String *string `toml:"string"`
|
||||
}
|
||||
|
||||
var errTomls = []string{
|
||||
"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
|
||||
"bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"",
|
||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1",
|
||||
}
|
||||
|
||||
type mapErr struct {
|
||||
Vals map[string]float64
|
||||
}
|
||||
|
||||
type intErr struct {
|
||||
Int1 int
|
||||
Int2 int8
|
||||
Int3 int16
|
||||
Int4 int32
|
||||
Int5 int64
|
||||
UInt1 uint
|
||||
UInt2 uint8
|
||||
UInt3 uint16
|
||||
UInt4 uint32
|
||||
UInt5 uint64
|
||||
Flt1 float32
|
||||
Flt2 float64
|
||||
}
|
||||
|
||||
var intErrTomls = []string{
|
||||
"Int1 = []\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = []\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = []\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = []\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = []\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = []\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = []\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = []\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = []\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = []\nFlt1 = 1.0\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0",
|
||||
"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []",
|
||||
}
|
||||
|
||||
func TestErrUnmarshal(t *testing.T) {
|
||||
for ind, toml := range errTomls {
|
||||
result := errStruct{}
|
||||
err := Unmarshal([]byte(toml), &result)
|
||||
if err == nil {
|
||||
t.Errorf("Expected err from case %d\n", ind)
|
||||
}
|
||||
}
|
||||
result2 := mapErr{}
|
||||
err := Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2)
|
||||
if err == nil {
|
||||
t.Errorf("Expected err from map")
|
||||
}
|
||||
for ind, toml := range intErrTomls {
|
||||
result3 := intErr{}
|
||||
err := Unmarshal([]byte(toml), &result3)
|
||||
if err == nil {
|
||||
t.Errorf("Expected int err from case %d\n", ind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type emptyMarshalTestStruct struct {
|
||||
Title string `toml:"title"`
|
||||
Bool bool `toml:"bool"`
|
||||
Int int `toml:"int"`
|
||||
String string `toml:"string"`
|
||||
StringList []string `toml:"stringlist"`
|
||||
Ptr *basicMarshalTestStruct `toml:"ptr"`
|
||||
Map map[string]string `toml:"map"`
|
||||
}
|
||||
|
||||
var emptyTestData = emptyMarshalTestStruct{
|
||||
Title: "Placeholder",
|
||||
Bool: false,
|
||||
Int: 0,
|
||||
String: "",
|
||||
StringList: []string{},
|
||||
Ptr: nil,
|
||||
Map: map[string]string{},
|
||||
}
|
||||
|
||||
var emptyTestToml = []byte(`bool = false
|
||||
int = 0
|
||||
string = ""
|
||||
stringlist = []
|
||||
title = "Placeholder"
|
||||
|
||||
[map]
|
||||
`)
|
||||
|
||||
type emptyMarshalTestStruct2 struct {
|
||||
Title string `toml:"title"`
|
||||
Bool bool `toml:"bool,omitempty"`
|
||||
Int int `toml:"int, omitempty"`
|
||||
String string `toml:"string,omitempty "`
|
||||
StringList []string `toml:"stringlist,omitempty"`
|
||||
Ptr *basicMarshalTestStruct `toml:"ptr,omitempty"`
|
||||
Map map[string]string `toml:"map,omitempty"`
|
||||
}
|
||||
|
||||
var emptyTestData2 = emptyMarshalTestStruct2{
|
||||
Title: "Placeholder",
|
||||
Bool: false,
|
||||
Int: 0,
|
||||
String: "",
|
||||
StringList: []string{},
|
||||
Ptr: nil,
|
||||
Map: map[string]string{},
|
||||
}
|
||||
|
||||
var emptyTestToml2 = []byte(`title = "Placeholder"
|
||||
`)
|
||||
|
||||
func TestEmptyMarshal(t *testing.T) {
|
||||
result, err := Marshal(emptyTestData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := emptyTestToml
|
||||
if !bytes.Equal(result, expected) {
|
||||
t.Errorf("Bad empty marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyMarshalOmit(t *testing.T) {
|
||||
result, err := Marshal(emptyTestData2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := emptyTestToml2
|
||||
if !bytes.Equal(result, expected) {
|
||||
t.Errorf("Bad empty omit marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyUnmarshal(t *testing.T) {
|
||||
result := emptyMarshalTestStruct{}
|
||||
err := Unmarshal(emptyTestToml, &result)
|
||||
expected := emptyTestData
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Bad empty unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyUnmarshalOmit(t *testing.T) {
|
||||
result := emptyMarshalTestStruct2{}
|
||||
err := Unmarshal(emptyTestToml, &result)
|
||||
expected := emptyTestData2
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Bad empty omit unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
type pointerMarshalTestStruct struct {
|
||||
Str *string
|
||||
List *[]string
|
||||
ListPtr *[]*string
|
||||
Map *map[string]string
|
||||
MapPtr *map[string]*string
|
||||
EmptyStr *string
|
||||
EmptyList *[]string
|
||||
EmptyMap *map[string]string
|
||||
DblPtr *[]*[]*string
|
||||
}
|
||||
|
||||
var pointerStr = "Hello"
|
||||
var pointerList = []string{"Hello back"}
|
||||
var pointerListPtr = []*string{&pointerStr}
|
||||
var pointerMap = map[string]string{"response": "Goodbye"}
|
||||
var pointerMapPtr = map[string]*string{"alternate": &pointerStr}
|
||||
var pointerTestData = pointerMarshalTestStruct{
|
||||
Str: &pointerStr,
|
||||
List: &pointerList,
|
||||
ListPtr: &pointerListPtr,
|
||||
Map: &pointerMap,
|
||||
MapPtr: &pointerMapPtr,
|
||||
EmptyStr: nil,
|
||||
EmptyList: nil,
|
||||
EmptyMap: nil,
|
||||
}
|
||||
|
||||
var pointerTestToml = []byte(`List = ["Hello back"]
|
||||
ListPtr = ["Hello"]
|
||||
Str = "Hello"
|
||||
|
||||
[Map]
|
||||
response = "Goodbye"
|
||||
|
||||
[MapPtr]
|
||||
alternate = "Hello"
|
||||
`)
|
||||
|
||||
func TestPointerMarshal(t *testing.T) {
|
||||
result, err := Marshal(pointerTestData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := pointerTestToml
|
||||
if !bytes.Equal(result, expected) {
|
||||
t.Errorf("Bad pointer marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPointerUnmarshal(t *testing.T) {
|
||||
result := pointerMarshalTestStruct{}
|
||||
err := Unmarshal(pointerTestToml, &result)
|
||||
expected := pointerTestData
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Bad pointer unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
type nestedMarshalTestStruct struct {
|
||||
String [][]string
|
||||
//Struct [][]basicMarshalTestSubStruct
|
||||
StringPtr *[]*[]*string
|
||||
// StructPtr *[]*[]*basicMarshalTestSubStruct
|
||||
}
|
||||
|
||||
var str1 = "Three"
|
||||
var str2 = "Four"
|
||||
var strPtr = []*string{&str1, &str2}
|
||||
var strPtr2 = []*[]*string{&strPtr}
|
||||
|
||||
var nestedTestData = nestedMarshalTestStruct{
|
||||
String: [][]string{[]string{"Five", "Six"}, []string{"One", "Two"}},
|
||||
StringPtr: &strPtr2,
|
||||
}
|
||||
|
||||
var nestedTestToml = []byte(`String = [["Five","Six"],["One","Two"]]
|
||||
StringPtr = [["Three","Four"]]
|
||||
`)
|
||||
|
||||
func TestNestedMarshal(t *testing.T) {
|
||||
result, err := Marshal(nestedTestData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := nestedTestToml
|
||||
if !bytes.Equal(result, expected) {
|
||||
t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedUnmarshal(t *testing.T) {
|
||||
result := nestedMarshalTestStruct{}
|
||||
err := Unmarshal(nestedTestToml, &result)
|
||||
expected := nestedTestData
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
title = "TOML Marshal Testing"
|
||||
|
||||
[basic]
|
||||
bool = true
|
||||
date = 1979-05-27T07:32:00Z
|
||||
float = 123.4
|
||||
int = 5000
|
||||
string = "Bite me"
|
||||
uint = 5001
|
||||
|
||||
[basic_lists]
|
||||
bools = [true,false,true]
|
||||
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
||||
floats = [12.3,45.6,78.9]
|
||||
ints = [8001,8001,8002]
|
||||
strings = ["One","Two","Three"]
|
||||
uints = [5002,5003]
|
||||
|
||||
[basic_map]
|
||||
one = "one"
|
||||
two = "two"
|
||||
|
||||
[subdoc]
|
||||
|
||||
[subdoc.first]
|
||||
name = "First"
|
||||
|
||||
[subdoc.second]
|
||||
name = "Second"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.First"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.Second"
|
||||
|
||||
[[subdocptrs]]
|
||||
name = "Second"
|
||||
Reference in New Issue
Block a user