Merge lp:~niemeyer/goyaml/inline-struct into lp:goyaml
- inline-struct
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 46 |
Proposed branch: | lp:~niemeyer/goyaml/inline-struct |
Merge into: | lp:goyaml |
Diff against target: |
221 lines (+82/-26) 5 files modified
decode.go (+7/-1) decode_test.go (+9/-0) encode.go (+6/-1) encode_test.go (+9/-0) goyaml.go (+51/-24) |
To merge this branch: | bzr merge lp:~niemeyer/goyaml/inline-struct |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
goyaml maintainers | Pending | ||
Review via email: mp+169981@code.launchpad.net |
Commit message
Description of the change
Add support for ,inline flag with struct values.
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.
Gustavo Niemeyer (niemeyer) wrote : | # |
John A Meinel (jameinel) wrote : | # |
The encode side of code looks reasonable, but what happens on decode if
you have ',inline' set ?
Gustavo Niemeyer (niemeyer) wrote : | # |
On 2013/06/19 04:47:34, jameinel wrote:
> The encode side of code looks reasonable, but what happens on decode
if you have
> ',inline' set ?
I don't understand the question. The logic is symmetrical, and tested?
Roger Peppe (rogpeppe) wrote : | # |
LGTM with some more tests and some other thoughts.
https:/
File encode.go (right):
https:/
encode.go:122: value = in.FieldByIndex
cool, i didn't know you could use FieldByIndex on arbitrary
non-embedded structs.
is there a particular reason you can't unify Num and Inline, BTW?
https:/
File encode_test.go (right):
https:/
encode_test.go:211: },
i'd like to see more tests of the inline functionality.
in particular:
- more than one inline struct
- nested inlines.
- errors from field conflicts.
https:/
File goyaml.go (right):
https:/
goyaml.go:124: // struct.
// Any clash of field names will result in an error.
?
https:/
goyaml.go:155: InlineMap int
i'd find some comments on these fields helpful for understanding the
code.
https:/
goyaml.go:163: Inline []int
// Inline holds the set of field indexes for
// looking up the given field.
https:/
goyaml.go:232: case reflect.Struct:
what about pointers to struct?
it's reasonable, i think, to be able to do:
type Foo struct {
One string
Other *Other `goyaml:",inline"`
}
so you can inline the same kinds of things that you can
embed.
also, for the future, you might want to consider allowing
interface types too - the treatment could be similar to
inline maps.
Gustavo Niemeyer (niemeyer) wrote : | # |
Thanks for the review, Roger.
https:/
File encode.go (right):
https:/
encode.go:122: value = in.FieldByIndex
On 2013/06/19 15:11:12, rog wrote:
> is there a particular reason you can't unify Num and Inline, BTW?
No big reason, other than Num being very cheap, both to implement and to
execute, and being the common case by far.
https:/
File encode_test.go (right):
https:/
encode_test.go:211: },
On 2013/06/19 15:11:12, rog wrote:
> i'd like to see more tests of the inline functionality.
> in particular:
> - more than one inline struct
> - nested inlines.
> - errors from field conflicts.
Done.
https:/
File goyaml.go (right):
https:/
goyaml.go:124: // struct.
On 2013/06/19 15:11:12, rog wrote:
> // Any clash of field names will result in an error.
> ?
That's not specific to ,inline. I've added a note both to Marshal and
Unmarshal pointing out that conflicting names result in a runtime error.
https:/
goyaml.go:155: InlineMap int
On 2013/06/19 15:11:12, rog wrote:
> i'd find some comments on these fields helpful for understanding the
code.
Improved the struct names and documentation.
https:/
goyaml.go:163: Inline []int
On 2013/06/19 15:11:12, rog wrote:
> // Inline holds the set of field indexes for
> // looking up the given field.
Not quite, but documented appropriately.
https:/
goyaml.go:232: case reflect.Struct:
On 2013/06/19 15:11:12, rog wrote:
> what about pointers to struct?
We can add support for them later, but I'd prefer to keep it simple at
this time. Pointers makes the implementation more complex because the
struct has to be initialized if one of the fields is found, and it also
means we're creating garbage on every iteration of a set of documents.
We can do it, but I'd prefer to wait until people need it. I've never
had anyone asking for it with bson.
- 46. By Gustavo Niemeyer
-
Review by Roger plus overall naming improvements.
- 47. By Gustavo Niemeyer
-
Merged from tip.
- 48. By Gustavo Niemeyer
-
go fmt
Gustavo Niemeyer (niemeyer) wrote : | # |
*** Submitted:
Add support for ,inline flag with struct values.
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.
R=jameinel, rog
CC=
https:/
Preview Diff
1 | === modified file 'decode.go' |
2 | --- decode.go 2013-04-30 00:42:36 +0000 |
3 | +++ decode.go 2013-06-18 03:47:27 +0000 |
4 | @@ -445,7 +445,13 @@ |
5 | continue |
6 | } |
7 | if info, ok := fieldsMap[name.String()]; ok { |
8 | - d.unmarshal(n.children[i+1], out.Field(info.Num)) |
9 | + var field reflect.Value |
10 | + if info.Inline == nil { |
11 | + field = out.Field(info.Num) |
12 | + } else { |
13 | + field = out.FieldByIndex(info.Inline) |
14 | + } |
15 | + d.unmarshal(n.children[i+1], field) |
16 | } |
17 | } |
18 | return true |
19 | |
20 | === modified file 'decode_test.go' |
21 | --- decode_test.go 2013-05-29 17:23:20 +0000 |
22 | +++ decode_test.go 2013-06-18 03:47:27 +0000 |
23 | @@ -317,6 +317,15 @@ |
24 | B int "-" |
25 | }{1, 0}, |
26 | }, |
27 | + |
28 | + // Struct inlining |
29 | + { |
30 | + "a: 1\nb: 2\n", |
31 | + &struct { |
32 | + A int |
33 | + C struct{ B int } `yaml:",inline"` |
34 | + }{1, struct{ B int }{2}}, |
35 | + }, |
36 | } |
37 | |
38 | func (s *S) TestUnmarshal(c *C) { |
39 | |
40 | === modified file 'encode.go' |
41 | --- encode.go 2013-04-28 04:28:40 +0000 |
42 | +++ encode.go 2013-06-18 03:47:27 +0000 |
43 | @@ -115,7 +115,12 @@ |
44 | } |
45 | e.mappingv(tag, func() { |
46 | for _, info := range fields.List { |
47 | - value := in.Field(info.Num) |
48 | + var value reflect.Value |
49 | + if info.Inline == nil { |
50 | + value = in.Field(info.Num) |
51 | + } else { |
52 | + value = in.FieldByIndex(info.Inline) |
53 | + } |
54 | if info.OmitEmpty && isZero(value) { |
55 | continue |
56 | } |
57 | |
58 | === modified file 'encode_test.go' |
59 | --- encode_test.go 2013-04-29 15:13:04 +0000 |
60 | +++ encode_test.go 2013-06-18 03:47:27 +0000 |
61 | @@ -200,6 +200,15 @@ |
62 | }{1, 2}, |
63 | "a: 1\n", |
64 | }, |
65 | + |
66 | + // Struct inlining |
67 | + { |
68 | + &struct { |
69 | + A int |
70 | + C struct{ B int } `yaml:",inline"` |
71 | + }{1, struct{ B int }{2}}, |
72 | + "a: 1\nb: 2\n", |
73 | + }, |
74 | } |
75 | |
76 | func (s *S) TestMarshal(c *C) { |
77 | |
78 | === modified file 'goyaml.go' |
79 | --- goyaml.go 2013-04-17 18:57:10 +0000 |
80 | +++ goyaml.go 2013-06-18 03:47:27 +0000 |
81 | @@ -119,6 +119,10 @@ |
82 | // flow Marshal using a flow style (useful for structs, |
83 | // sequences and maps. |
84 | // |
85 | +// inline Inline the struct it's applied to, so its fields |
86 | +// are processed as if they were part of the outer |
87 | +// struct. |
88 | +// |
89 | // In addition, if the key is "-", the field is ignored. |
90 | // |
91 | // For example: |
92 | @@ -131,7 +135,7 @@ |
93 | // goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" |
94 | // |
95 | func Marshal(in interface{}) (out []byte, err error) { |
96 | - //defer handleErr(&err) |
97 | + defer handleErr(&err) |
98 | e := newEncoder() |
99 | defer e.destroy() |
100 | e.marshal("", reflect.ValueOf(in)) |
101 | @@ -146,8 +150,9 @@ |
102 | // The code in this section was copied from gobson. |
103 | |
104 | type structFields struct { |
105 | - Map map[string]fieldInfo |
106 | - List []fieldInfo |
107 | + Map map[string]fieldInfo |
108 | + List []fieldInfo |
109 | + InlineMap int |
110 | } |
111 | |
112 | type fieldInfo struct { |
113 | @@ -155,6 +160,7 @@ |
114 | Num int |
115 | OmitEmpty bool |
116 | Flow bool |
117 | + Inline []int |
118 | } |
119 | |
120 | var fieldMap = make(map[reflect.Type]*structFields) |
121 | @@ -176,7 +182,8 @@ |
122 | |
123 | n := st.NumField() |
124 | fieldsMap := make(map[string]fieldInfo) |
125 | - fieldsList := make([]fieldInfo, n) |
126 | + fieldsList := make([]fieldInfo, 0, n) |
127 | + inlineMap := -1 |
128 | for i := 0; i != n; i++ { |
129 | field := st.Field(i) |
130 | if field.PkgPath != "" { |
131 | @@ -193,24 +200,7 @@ |
132 | continue |
133 | } |
134 | |
135 | - // XXX Drop this after a few releases. |
136 | - if s := strings.Index(tag, "/"); s >= 0 { |
137 | - recommend := tag[:s] |
138 | - for _, c := range tag[s+1:] { |
139 | - switch c { |
140 | - case 'c': |
141 | - recommend += ",omitempty" |
142 | - case 'f': |
143 | - recommend += ",flow" |
144 | - default: |
145 | - msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st) |
146 | - panic(externalPanic(msg)) |
147 | - } |
148 | - } |
149 | - msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend) |
150 | - panic(externalPanic(msg)) |
151 | - } |
152 | - |
153 | + inline := false |
154 | fields := strings.Split(tag, ",") |
155 | if len(fields) > 1 { |
156 | for _, flag := range fields[1:] { |
157 | @@ -219,6 +209,8 @@ |
158 | info.OmitEmpty = true |
159 | case "flow": |
160 | info.Flow = true |
161 | + case "inline": |
162 | + inline = true |
163 | default: |
164 | msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) |
165 | panic(externalPanic(msg)) |
166 | @@ -227,6 +219,41 @@ |
167 | tag = fields[0] |
168 | } |
169 | |
170 | + if inline { |
171 | + switch field.Type.Kind() { |
172 | + //case reflect.Map: |
173 | + // if inlineMap >= 0 { |
174 | + // return nil, errors.New("Multiple ,inline maps in struct " + st.String()) |
175 | + // } |
176 | + // if field.Type.Key() != reflect.TypeOf("") { |
177 | + // return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) |
178 | + // } |
179 | + // inlineMap = info.Num |
180 | + case reflect.Struct: |
181 | + sfields, err := getStructFields(field.Type) |
182 | + if err != nil { |
183 | + return nil, err |
184 | + } |
185 | + for _, finfo := range sfields.List { |
186 | + if _, found := fieldsMap[finfo.Key]; found { |
187 | + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() |
188 | + return nil, errors.New(msg) |
189 | + } |
190 | + if finfo.Inline == nil { |
191 | + finfo.Inline = []int{i, finfo.Num} |
192 | + } else { |
193 | + finfo.Inline = append([]int{i}, finfo.Inline...) |
194 | + } |
195 | + fieldsMap[finfo.Key] = finfo |
196 | + fieldsList = append(fieldsList, finfo) |
197 | + } |
198 | + default: |
199 | + //panic("Option ,inline needs a struct value or map field") |
200 | + panic("Option ,inline needs a struct value field") |
201 | + } |
202 | + continue |
203 | + } |
204 | + |
205 | if tag != "" { |
206 | info.Key = tag |
207 | } else { |
208 | @@ -238,11 +265,11 @@ |
209 | return nil, errors.New(msg) |
210 | } |
211 | |
212 | - fieldsList[len(fieldsMap)] = info |
213 | + fieldsList = append(fieldsList, info) |
214 | fieldsMap[info.Key] = info |
215 | } |
216 | |
217 | - fields = &structFields{fieldsMap, fieldsList[:len(fieldsMap)]} |
218 | + fields = &structFields{fieldsMap, fieldsList, inlineMap} |
219 | |
220 | fieldMapMutex.Lock() |
221 | fieldMap[st] = fields |
Reviewers: mp+169981_ code.launchpad. net,
Message:
Please take a look.
Description:
Add support for ,inline flag with struct values.
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.
https:/ /code.launchpad .net/~niemeyer/ goyaml/ inline- struct/ +merge/ 169981
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/10366044/
Affected files:
A [revision details]
M decode.go
M decode_test.go
M encode.go
M encode_test.go
M goyaml.go