Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed write quote in marshal to allow write other types than strings #344

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
218 changes: 218 additions & 0 deletions custom_marshaler_test.go
@@ -0,0 +1,218 @@
package toml_test

import (
"bytes"
"errors"
"fmt"
"github.com/BurntSushi/toml"
"strconv"
"strings"
"testing"
)

// Test for hotfix-341
func TestCustomEncode(t *testing.T) {

var enum = Enum(OtherValue)

outer := Outer{
String: &InnerString{value: "value"},
Int: &InnerInt{value: 10},
Bool: &InnerBool{value: true},
Enum: &enum,
ArrayS: &InnerArrayString{value: []string{"text1", "text2"}},
ArrayI: &InnerArrayInt{value: []int64{5, 7, 3}},
}

var buf bytes.Buffer
err := toml.NewEncoder(&buf).Encode(outer)
if err != nil {
t.Errorf("Encode failed: %s", err)
}

have := strings.TrimSpace(buf.String())
want := strings.TrimSpace("String = \"value\"\nInt = 10\nBool = true\nEnum = \"OTHER_VALUE\"\nArrayS = [\"text1\", \"text2\"]\nArrayI = [5, 7, 3]\n")
if want != have {
t.Errorf("\nhave:\n%s\nwant:\n%s\n", have, want)
}
}

// Test for hotfix-341
func TestCustomDecode(t *testing.T) {

const testToml = "Bool = true\nString = \"test\"\nInt = 10\nEnum = \"OTHER_VALUE\"\nArrayS = [\"text1\", \"text2\"]\nArrayI = [5, 7, 3]"

outer := Outer{}
_, err := toml.Decode(testToml, &outer)

if err != nil {
t.Fatal(fmt.Sprintf("Decode failed: %s", err))
}
if outer.String.value != "test" {
t.Errorf("\nhave:\n%s\nwant:\n%s\n", outer.String.value, "test")
}
if outer.Bool.value != true {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Bool.value, true)
}
if outer.Int.value != 10 {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Int.value, 10)
}

if *outer.Enum != OtherValue {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Enum, OtherValue)
}
if fmt.Sprint(outer.ArrayS.value) != fmt.Sprint([]string{"text1", "text2"}) {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.ArrayS.value, []string{"text1", "text2"})
}
if fmt.Sprint(outer.ArrayI.value) != fmt.Sprint([]int64{5, 7, 3}) {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.ArrayI.value, []int64{5, 7, 3})
}
}

/* Implementing MarshalTOML and UnmarshalTOML structs
An useful use could be to map a TOML value to an internal value, like emuns.
*/

type Enum int

const (
NoValue Enum = iota
SomeValue
OtherValue
)

func (e *Enum) Value() string {
switch *e {
case SomeValue:
return "SOME_VALUE"
case OtherValue:
return "OTHER_VALUE"
case NoValue:
return ""
}
return ""
}

func (e *Enum) MarshalTOML() ([]byte, error) {
return []byte("\"" + e.Value() + "\""), nil
}

func (e *Enum) UnmarshalTOML(value interface{}) error {
sValue, ok := value.(string)
if !ok {
return fmt.Errorf("value %v is not a string type", value)
}
for _, enum := range []Enum{NoValue, SomeValue, OtherValue} {
if enum.Value() == sValue {
*e = enum
return nil
}
}
return errors.New("invalid enum value")
}

type InnerString struct {
value string
}

func (s *InnerString) MarshalTOML() ([]byte, error) {
return []byte("\"" + s.value + "\""), nil
}
func (s *InnerString) UnmarshalTOML(value interface{}) error {
sValue, ok := value.(string)
if !ok {
return fmt.Errorf("value %v is not a string type", value)
}
s.value = sValue
return nil
}

type InnerInt struct {
value int
}

func (i *InnerInt) MarshalTOML() ([]byte, error) {
return []byte(strconv.Itoa(i.value)), nil
}
func (i *InnerInt) UnmarshalTOML(value interface{}) error {
iValue, ok := value.(int64)
if !ok {
return fmt.Errorf("value %v is not a int type", value)
}
i.value = int(iValue)
return nil
}

type InnerBool struct {
value bool
}

func (b *InnerBool) MarshalTOML() ([]byte, error) {
return []byte(strconv.FormatBool(b.value)), nil
}
func (b *InnerBool) UnmarshalTOML(value interface{}) error {
bValue, ok := value.(bool)
if !ok {
return fmt.Errorf("value %v is not a bool type", value)
}
b.value = bValue
return nil
}


type InnerArrayString struct {
value []string
}

func (as *InnerArrayString) MarshalTOML() ([]byte, error) {
return []byte("[\"" + strings.Join(as.value, "\", \"") + "\"]"), nil
}

func (as *InnerArrayString) UnmarshalTOML(value interface{}) error {
if value != nil {
asValue, ok := value.([]interface{})
if !ok {
return fmt.Errorf("value %v is not a [] type", value)
}
as.value = []string{}
for _, value := range asValue {
as.value = append(as.value, value.(string))
}
}
return nil
}

type InnerArrayInt struct {
value []int64
}

func (ai *InnerArrayInt) MarshalTOML() ([]byte, error) {
strArr := []string{}
for _, intV := range ai.value {
strArr = append(strArr, strconv.FormatInt(intV, 10))
}
return []byte("[" + strings.Join(strArr, ", ") + "]"), nil
}

func (ai *InnerArrayInt) UnmarshalTOML(value interface{}) error {
if value != nil {
asValue, ok := value.([]interface{})
if !ok {
return fmt.Errorf("value %v is not a [] type", value)
}
ai.value = []int64{}
for _, value := range asValue {
ai.value = append(ai.value, value.(int64))
}
}
return nil
}

type Outer struct {
String *InnerString
Int *InnerInt
Bool *InnerBool
Enum *Enum
ArrayS *InnerArrayString
ArrayI *InnerArrayInt
}
2 changes: 1 addition & 1 deletion encode.go
Expand Up @@ -212,7 +212,7 @@ func (enc *Encoder) eElement(rv reflect.Value) {
if err != nil {
encPanic(err)
}
enc.writeQuoted(string(s))
enc.w.Write(s)
return
case encoding.TextMarshaler:
s, err := v.MarshalText()
Expand Down
14 changes: 8 additions & 6 deletions encode_test.go
Expand Up @@ -347,12 +347,14 @@ func (c cplx) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("(%f+%fi)", real(cplx), imag(cplx))), nil
}

func (s *sound2) MarshalTOML() ([]byte, error) { return []byte(s.S), nil }
func (f food2) MarshalTOML() ([]byte, error) { return []byte(strings.Join(f.F, ", ")), nil }
func (f fun2) MarshalTOML() ([]byte, error) { return []byte("why would you do this?"), nil }
func (s *sound2) MarshalTOML() ([]byte, error) { return []byte("\"" + s.S + "\""), nil }
func (f food2) MarshalTOML() ([]byte, error) {
return []byte("[\"" + strings.Join(f.F, "\", \"") + "\"]"), nil
}
func (f fun2) MarshalTOML() ([]byte, error) { return []byte("\"why would you do this?\""), nil }
func (c cplx2) MarshalTOML() ([]byte, error) {
cplx := complex128(c)
return []byte(fmt.Sprintf("(%f+%fi)", real(cplx), imag(cplx))), nil
return []byte(fmt.Sprintf("\"(%f+%fi)\"", real(cplx), imag(cplx))), nil
}

func TestEncodeTextMarshaler(t *testing.T) {
Expand Down Expand Up @@ -435,8 +437,8 @@ func TestEncodeTOMLMarshaler(t *testing.T) {

want := `Name = "Goblok"
Sound2 = "miauw"
Food = "chicken, fish"
Food2 = "chicken, fish"
Food = ["chicken", "fish"]
Food2 = ["chicken", "fish"]
Complex = "(42.000000+666.000000i)"
Fun = "why would you do this?"

Expand Down