Skip to content

Commit

Permalink
Support inline flag on a map field.
Browse files Browse the repository at this point in the history
  • Loading branch information
niemeyer committed Jan 19, 2015
1 parent bef53ef commit 5d6f7e0
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 12 deletions.
16 changes: 16 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,15 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
}
name := settableValueOf("")
l := len(n.children)

var inlineMap reflect.Value
var elemType reflect.Type
if sinfo.InlineMap != -1 {
inlineMap = out.Field(sinfo.InlineMap)
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
elemType = inlineMap.Type().Elem()
}

for i := 0; i < l; i += 2 {
ni := n.children[i]
if isMerge(ni) {
Expand All @@ -624,6 +633,13 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
field = out.FieldByIndex(info.Inline)
}
d.unmarshal(n.children[i+1], field)
} else if sinfo.InlineMap != -1 {
if inlineMap.IsNil() {
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.children[i+1], value)
inlineMap.SetMapIndex(name, value)
}
}
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 @@ -473,6 +473,15 @@ var unmarshalTests = []struct {
}{1, inlineB{2, inlineC{3}}},
},

// Map inlining
{
"a: 1\nb: 2\nc: 3\n",
&struct {
A int
C map[string]int `yaml:",inline"`
}{1, map[string]int{"b": 2, "c": 3}},
},

// bug 1243827
{
"a: -b_c",
Expand Down
17 changes: 17 additions & 0 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package yaml

import (
"encoding"
"fmt"
"reflect"
"regexp"
"sort"
Expand Down Expand Up @@ -164,6 +165,22 @@ func (e *encoder) structv(tag string, in reflect.Value) {
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}

Expand Down
15 changes: 15 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ var marshalTests = []struct {
"a: 1\nb: 2\nc: 3\n",
},

// Map inlining
{
&struct {
A int
C map[string]int `yaml:",inline"`
}{1, map[string]int{"b": 2, "c": 3}},
"a: 1\nb: 2\nc: 3\n",
},

// Duration
{
map[string]time.Duration{"a": 3 * time.Second},
Expand Down Expand Up @@ -312,6 +321,12 @@ var marshalErrorTests = []struct {
inlineB ",inline"
}{1, inlineB{2, inlineC{3}}},
panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
}, {
value: &struct {
A int
B map[string]int ",inline"
}{1, map[string]int{"a": 2}},
panic: `Can't have key "a" in inlined map; conflicts with struct field`,
}}

func (s *S) TestMarshalErrors(c *C) {
Expand Down
24 changes: 12 additions & 12 deletions yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +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.
// inline Inline the field, which must be a struct or a map,
// causing all of its fields or keys to be processed as if
// they were part of the outer struct. For maps, keys must
// not conflict with the yaml keys of other struct fields.
//
// In addition, if the key is "-", the field is ignored.
//
Expand Down Expand Up @@ -255,15 +256,14 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {

if inline {
switch field.Type.Kind() {
// TODO: Implement support for inline maps.
//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.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:
sinfo, err := getStructInfo(field.Type)
if err != nil {
Expand Down

0 comments on commit 5d6f7e0

Please sign in to comment.