Skip to content

Commit 894900d

Browse files
author
iwysiu
authored
GODRIVER-1305 allow inline fields to be pointers to structs (#211)
1 parent b79f27b commit 894900d

File tree

3 files changed

+169
-8
lines changed

3 files changed

+169
-8
lines changed

bson/bsoncodec/default_value_decoders_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,62 @@ func TestDefaultValueDecoders(t *testing.T) {
22112211
buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
22122212
nil,
22132213
},
2214+
{
2215+
"inline struct pointer",
2216+
struct {
2217+
Foo *struct {
2218+
A int64 `bson:",minsize"`
2219+
} `bson:",inline"`
2220+
Bar *struct {
2221+
B int64
2222+
} `bson:",inline"`
2223+
}{
2224+
Foo: &struct {
2225+
A int64 `bson:",minsize"`
2226+
}{
2227+
A: 12345,
2228+
},
2229+
Bar: nil,
2230+
},
2231+
buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
2232+
nil,
2233+
},
2234+
{
2235+
"nested inline struct pointer",
2236+
struct {
2237+
Foo *struct {
2238+
Bar *struct {
2239+
A int64 `bson:",minsize"`
2240+
} `bson:",inline"`
2241+
} `bson:",inline"`
2242+
}{
2243+
Foo: &struct {
2244+
Bar *struct {
2245+
A int64 `bson:",minsize"`
2246+
} `bson:",inline"`
2247+
}{
2248+
Bar: &struct {
2249+
A int64 `bson:",minsize"`
2250+
}{
2251+
A: 12345,
2252+
},
2253+
},
2254+
},
2255+
buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
2256+
nil,
2257+
},
2258+
{
2259+
"inline nil struct pointer",
2260+
struct {
2261+
Foo *struct {
2262+
A int64 `bson:",minsize"`
2263+
} `bson:",inline"`
2264+
}{
2265+
Foo: nil,
2266+
},
2267+
buildDocument([]byte{}),
2268+
nil,
2269+
},
22142270
{
22152271
"inline map",
22162272
struct {

bson/bsoncodec/default_value_encoders_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,62 @@ func TestDefaultValueEncoders(t *testing.T) {
11561156
buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
11571157
nil,
11581158
},
1159+
{
1160+
"inline struct pointer",
1161+
struct {
1162+
Foo *struct {
1163+
A int64 `bson:",minsize"`
1164+
} `bson:",inline"`
1165+
Bar *struct {
1166+
B int64
1167+
} `bson:",inline"`
1168+
}{
1169+
Foo: &struct {
1170+
A int64 `bson:",minsize"`
1171+
}{
1172+
A: 12345,
1173+
},
1174+
Bar: nil,
1175+
},
1176+
buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
1177+
nil,
1178+
},
1179+
{
1180+
"nested inline struct pointer",
1181+
struct {
1182+
Foo *struct {
1183+
Bar *struct {
1184+
A int64 `bson:",minsize"`
1185+
} `bson:",inline"`
1186+
} `bson:",inline"`
1187+
}{
1188+
Foo: &struct {
1189+
Bar *struct {
1190+
A int64 `bson:",minsize"`
1191+
} `bson:",inline"`
1192+
}{
1193+
Bar: &struct {
1194+
A int64 `bson:",minsize"`
1195+
}{
1196+
A: 12345,
1197+
},
1198+
},
1199+
},
1200+
buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
1201+
nil,
1202+
},
1203+
{
1204+
"inline nil struct pointer",
1205+
struct {
1206+
Foo *struct {
1207+
A int64 `bson:",minsize"`
1208+
} `bson:",inline"`
1209+
}{
1210+
Foo: nil,
1211+
},
1212+
buildDocument([]byte{}),
1213+
nil,
1214+
},
11591215
{
11601216
"inline map",
11611217
struct {

bson/bsoncodec/struct_codec.go

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ func (sc *StructCodec) EncodeValue(r EncodeContext, vw bsonrw.ValueWriter, val r
7171
if desc.inline == nil {
7272
rv = val.Field(desc.idx)
7373
} else {
74-
rv = val.FieldByIndex(desc.inline)
74+
rv, err = fieldByIndexErr(val, desc.inline)
75+
if err != nil {
76+
continue
77+
}
7578
}
7679

7780
desc.encoder, rv, err = defaultValueEncoders.lookupElementEncoder(r, desc.encoder, rv)
@@ -194,7 +197,10 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r
194197
if fd.inline == nil {
195198
field = val.Field(fd.idx)
196199
} else {
197-
field = val.FieldByIndex(fd.inline)
200+
field, err = getInlineField(val, fd.inline)
201+
if err != nil {
202+
return err
203+
}
198204
}
199205

200206
if !field.CanSet() { // Being settable is a super set of being addressable.
@@ -297,11 +303,12 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
297303
continue
298304
}
299305

300-
encoder, err := r.LookupEncoder(sf.Type)
306+
sfType := sf.Type
307+
encoder, err := r.LookupEncoder(sfType)
301308
if err != nil {
302309
encoder = nil
303310
}
304-
decoder, err := r.LookupDecoder(sf.Type)
311+
decoder, err := r.LookupDecoder(sfType)
305312
if err != nil {
306313
decoder = nil
307314
}
@@ -321,17 +328,23 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
321328
description.truncate = stags.Truncate
322329

323330
if stags.Inline {
324-
switch sf.Type.Kind() {
331+
switch sfType.Kind() {
325332
case reflect.Map:
326333
if sd.inlineMap >= 0 {
327334
return nil, errors.New("(struct " + t.String() + ") multiple inline maps")
328335
}
329-
if sf.Type.Key() != tString {
336+
if sfType.Key() != tString {
330337
return nil, errors.New("(struct " + t.String() + ") inline map must have a string keys")
331338
}
332339
sd.inlineMap = description.idx
340+
case reflect.Ptr:
341+
sfType = sfType.Elem()
342+
if sfType.Kind() != reflect.Struct {
343+
return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String())
344+
}
345+
fallthrough
333346
case reflect.Struct:
334-
inlinesf, err := sc.describeStruct(r, sf.Type)
347+
inlinesf, err := sc.describeStruct(r, sfType)
335348
if err != nil {
336349
return nil, err
337350
}
@@ -348,7 +361,7 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
348361
sd.fl = append(sd.fl, fd)
349362
}
350363
default:
351-
return nil, fmt.Errorf("(struct %s) inline fields must be either a struct or a map", t.String())
364+
return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String())
352365
}
353366
continue
354367
}
@@ -367,3 +380,39 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
367380

368381
return sd, nil
369382
}
383+
384+
func fieldByIndexErr(v reflect.Value, index []int) (result reflect.Value, err error) {
385+
defer func() {
386+
if recovered := recover(); recovered != nil {
387+
switch r := recovered.(type) {
388+
case string:
389+
err = fmt.Errorf("%s", r)
390+
case error:
391+
err = r
392+
}
393+
}
394+
}()
395+
396+
result = v.FieldByIndex(index)
397+
return
398+
}
399+
400+
func getInlineField(val reflect.Value, index []int) (reflect.Value, error) {
401+
field, err := fieldByIndexErr(val, index)
402+
if err == nil {
403+
return field, nil
404+
}
405+
406+
// if parent of this element doesn't exist, fix its parent
407+
inlineParent := index[:len(index)-1]
408+
var fParent reflect.Value
409+
if fParent, err = fieldByIndexErr(val, inlineParent); err != nil {
410+
fParent, err = getInlineField(val, inlineParent)
411+
if err != nil {
412+
return fParent, err
413+
}
414+
}
415+
fParent.Set(reflect.New(fParent.Type().Elem()))
416+
417+
return fieldByIndexErr(val, index)
418+
}

0 commit comments

Comments
 (0)