Skip to content

Commit

Permalink
Add support for ,inline flag with struct values.
Browse files Browse the repository at this point in the history
This also adds the code to do inlining of maps, as supported by bson
(the code was copied from mgo/bson), but it's disabled for the moment
as I'll need more time to implement it.
  • Loading branch information
niemeyer committed Jun 18, 2013
1 parent 53087c1 commit 8691640
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 26 deletions.
8 changes: 7 additions & 1 deletion decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,13 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
continue
}
if info, ok := fieldsMap[name.String()]; ok {
d.unmarshal(n.children[i+1], out.Field(info.Num))
var field reflect.Value
if info.Inline == nil {
field = out.Field(info.Num)
} else {
field = out.FieldByIndex(info.Inline)
}
d.unmarshal(n.children[i+1], field)
}
}
return true
Expand Down
9 changes: 9 additions & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,15 @@ var unmarshalTests = []struct {
B int "-"
}{1, 0},
},

// Struct inlining
{
"a: 1\nb: 2\n",
&struct {
A int
C struct{ B int } `yaml:",inline"`
}{1, struct{ B int }{2}},
},
}

func (s *S) TestUnmarshal(c *C) {
Expand Down
7 changes: 6 additions & 1 deletion encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ func (e *encoder) structv(tag string, in reflect.Value) {
}
e.mappingv(tag, func() {
for _, info := range fields.List {
value := in.Field(info.Num)
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = in.FieldByIndex(info.Inline)
}
if info.OmitEmpty && isZero(value) {
continue
}
Expand Down
9 changes: 9 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,15 @@ var marshalTests = []struct {
}{1, 2},
"a: 1\n",
},

// Struct inlining
{
&struct {
A int
C struct{ B int } `yaml:",inline"`
}{1, struct{ B int }{2}},
"a: 1\nb: 2\n",
},
}

func (s *S) TestMarshal(c *C) {
Expand Down
75 changes: 51 additions & 24 deletions goyaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ func Unmarshal(in []byte, out interface{}) (err error) {
// flow Marshal using a flow style (useful for structs,
// sequences and maps.
//
// inline Inline the struct it's applied to, so its fields
// are processed as if they were part of the outer
// struct.
//
// In addition, if the key is "-", the field is ignored.
//
// For example:
Expand All @@ -131,7 +135,7 @@ func Unmarshal(in []byte, out interface{}) (err error) {
// goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
//
func Marshal(in interface{}) (out []byte, err error) {
//defer handleErr(&err)
defer handleErr(&err)
e := newEncoder()
defer e.destroy()
e.marshal("", reflect.ValueOf(in))
Expand All @@ -146,15 +150,17 @@ func Marshal(in interface{}) (out []byte, err error) {
// The code in this section was copied from gobson.

type structFields struct {
Map map[string]fieldInfo
List []fieldInfo
Map map[string]fieldInfo
List []fieldInfo
InlineMap int
}

type fieldInfo struct {
Key string
Num int
OmitEmpty bool
Flow bool
Inline []int
}

var fieldMap = make(map[reflect.Type]*structFields)
Expand All @@ -176,7 +182,8 @@ func getStructFields(st reflect.Type) (*structFields, error) {

n := st.NumField()
fieldsMap := make(map[string]fieldInfo)
fieldsList := make([]fieldInfo, n)
fieldsList := make([]fieldInfo, 0, n)
inlineMap := -1
for i := 0; i != n; i++ {
field := st.Field(i)
if field.PkgPath != "" {
Expand All @@ -193,24 +200,7 @@ func getStructFields(st reflect.Type) (*structFields, error) {
continue
}

// XXX Drop this after a few releases.
if s := strings.Index(tag, "/"); s >= 0 {
recommend := tag[:s]
for _, c := range tag[s+1:] {
switch c {
case 'c':
recommend += ",omitempty"
case 'f':
recommend += ",flow"
default:
msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
panic(externalPanic(msg))
}
}
msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
panic(externalPanic(msg))
}

inline := false
fields := strings.Split(tag, ",")
if len(fields) > 1 {
for _, flag := range fields[1:] {
Expand All @@ -219,6 +209,8 @@ func getStructFields(st reflect.Type) (*structFields, error) {
info.OmitEmpty = true
case "flow":
info.Flow = true
case "inline":
inline = true
default:
msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
panic(externalPanic(msg))
Expand All @@ -227,6 +219,41 @@ func getStructFields(st reflect.Type) (*structFields, error) {
tag = fields[0]
}

if inline {
switch field.Type.Kind() {
//case reflect.Map:
// if inlineMap >= 0 {
// return nil, errors.New("Multiple ,inline maps in struct " + st.String())
// }
// if field.Type.Key() != reflect.TypeOf("") {
// return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
// }
// inlineMap = info.Num
case reflect.Struct:
sfields, err := getStructFields(field.Type)
if err != nil {
return nil, err
}
for _, finfo := range sfields.List {
if _, found := fieldsMap[finfo.Key]; found {
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
}
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
default:
//panic("Option ,inline needs a struct value or map field")
panic("Option ,inline needs a struct value field")
}
continue
}

if tag != "" {
info.Key = tag
} else {
Expand All @@ -238,11 +265,11 @@ func getStructFields(st reflect.Type) (*structFields, error) {
return nil, errors.New(msg)
}

fieldsList[len(fieldsMap)] = info
fieldsList = append(fieldsList, info)
fieldsMap[info.Key] = info
}

fields = &structFields{fieldsMap, fieldsList[:len(fieldsMap)]}
fields = &structFields{fieldsMap, fieldsList, inlineMap}

fieldMapMutex.Lock()
fieldMap[st] = fields
Expand Down

0 comments on commit 8691640

Please sign in to comment.