Skip to content

Commit

Permalink
Encoder: treat tagged anonymous structs as non-anonymous
Browse files Browse the repository at this point in the history
This matches the encoding/json behavior.

Fixes BurntSushi#127.
  • Loading branch information
cespare committed Mar 16, 2016
1 parent bbd5bb6 commit f42bdee
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 30 deletions.
58 changes: 39 additions & 19 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,16 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
t := f.Type
switch t.Kind() {
case reflect.Struct:
addFields(t, frv, f.Index)
continue
// Treat anonymous struct fields with
// tag names as though they are not
// anonymous, like encoding/json does.
if getOptions(f.Tag).name == "" {
addFields(t, frv, f.Index)
continue
}
case reflect.Ptr:
if t.Elem().Kind() == reflect.Struct {
if t.Elem().Kind() == reflect.Struct &&
getOptions(f.Tag).name == "" {
if !frv.IsNil() {
addFields(t.Elem(), frv.Elem(), f.Index)
}
Expand Down Expand Up @@ -347,17 +353,18 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
continue
}

tag := sft.Tag.Get("toml")
if tag == "-" {
opts := getOptions(sft.Tag)
if opts.skip {
continue
}
keyName, opts := getOptions(tag)
if keyName == "" {
keyName = sft.Name
keyName := sft.Name
if opts.name != "" {
keyName = opts.name
}
if _, ok := opts["omitempty"]; ok && isEmpty(sf) {
if opts.omitempty && isEmpty(sf) {
continue
} else if _, ok := opts["omitzero"]; ok && isZero(sf) {
}
if opts.omitzero && isZero(sf) {
continue
}

Expand Down Expand Up @@ -451,17 +458,30 @@ func tomlArrayType(rv reflect.Value) tomlType {
return firstType
}

func getOptions(keyName string) (string, map[string]struct{}) {
opts := make(map[string]struct{})
ss := strings.Split(keyName, ",")
name := ss[0]
if len(ss) > 1 {
for _, opt := range ss {
opts[opt] = struct{}{}
type tagOptions struct {
skip bool // "-"
name string
omitempty bool
omitzero bool
}

func getOptions(tag reflect.StructTag) tagOptions {
t := tag.Get("toml")
if t == "-" {
return tagOptions{skip: true}
}
var opts tagOptions
parts := strings.Split(t, ",")
opts.name = parts[0]
for _, s := range parts[1:] {
switch s {
case "omitempty":
opts.omitempty = true
case "omitzero":
opts.omitzero = true
}
}

return name, opts
return opts
}

func isZero(rv reflect.Value) bool {
Expand Down
39 changes: 32 additions & 7 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,19 +515,44 @@ func TestEncodeOmitemptyWithEmptyName(t *testing.T) {
v, expected, nil)
}

func TestEncodeAnonymousStruct(t *testing.T) {
type Inner struct{ N int }
type Outer0 struct{ Inner }
type Outer1 struct {
Inner `toml:"inner"`
}

v0 := Outer0{Inner{3}}
expected := "N = 3\n"
encodeExpected(t, "embedded anonymous untagged struct", v0, expected, nil)

v1 := Outer1{Inner{3}}
expected = "[inner]\n N = 3\n"
encodeExpected(t, "embedded anonymous tagged struct", v1, expected, nil)
}

func TestEncodeAnonymousStructPointerField(t *testing.T) {
type Sub struct{}
type simple struct {
*Sub
type Inner struct{ N int }
type Outer0 struct{ *Inner }
type Outer1 struct {
*Inner `toml:"inner"`
}

value := simple{}
v0 := Outer0{}
expected := ""
encodeExpected(t, "nil anonymous struct pointer field", value, expected, nil)
encodeExpected(t, "nil anonymous untagged struct pointer field", v0, expected, nil)

value = simple{Sub: &Sub{}}
v0 = Outer0{&Inner{3}}
expected = "N = 3\n"
encodeExpected(t, "non-nil anonymous untagged struct pointer field", v0, expected, nil)

v1 := Outer1{}
expected = ""
encodeExpected(t, "non-nil anonymous struct pointer field", value, expected, nil)
encodeExpected(t, "nil anonymous tagged struct pointer field", v1, expected, nil)

v1 = Outer1{&Inner{3}}
expected = "[inner]\n N = 3\n"
encodeExpected(t, "non-nil anonymous tagged struct pointer field", v1, expected, nil)
}

func TestEncodeIgnoredFields(t *testing.T) {
Expand Down
9 changes: 5 additions & 4 deletions type_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ func typeFields(t reflect.Type) []field {
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
name, _ := getOptions(sf.Tag.Get("toml"))
if name == "-" {
opts := getOptions(sf.Tag)
if opts.skip {
continue
}
index := make([]int, len(f.index)+1)
Expand All @@ -110,8 +110,9 @@ func typeFields(t reflect.Type) []field {
}

// Record found field and index sequence.
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
tagged := name != ""
if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
tagged := opts.name != ""
name := opts.name
if name == "" {
name = sf.Name
}
Expand Down

0 comments on commit f42bdee

Please sign in to comment.