Skip to content

Commit 324f6a9

Browse files
author
Divjot Arora
authored
GODRIVER-1627 Export keys slice from bsoncodec.DecodeError (#421)
1 parent b98d4f9 commit 324f6a9

File tree

2 files changed

+25
-23
lines changed

2 files changed

+25
-23
lines changed

bson/bsoncodec/default_value_decoders_test.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3385,11 +3385,11 @@ func TestDefaultValueDecoders(t *testing.T) {
33853385
Foo string
33863386
}
33873387
emptyInterfaceStructErr := &DecodeError{
3388-
keys: []string{"foo(field Foo)"},
3388+
keys: []string{"foo"},
33893389
wrapped: decodeValueError,
33903390
}
33913391
stringStructErr := &DecodeError{
3392-
keys: []string{"foo(field Foo)"},
3392+
keys: []string{"foo"},
33933393
wrapped: ErrNoDecoder{reflect.TypeOf("")},
33943394
}
33953395

@@ -3420,7 +3420,7 @@ func TestDefaultValueDecoders(t *testing.T) {
34203420
RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode)).
34213421
Build()
34223422
nestedErr := &DecodeError{
3423-
keys: []string{"fourth(field Fourth)", "1", "third(field Third)", "randomKey", "second(field Second)", "first(field First)"},
3423+
keys: []string{"fourth", "1", "third", "randomKey", "second", "first"},
34243424
wrapped: decodeValueError,
34253425
}
34263426

@@ -3546,9 +3546,12 @@ func TestDefaultValueDecoders(t *testing.T) {
35463546

35473547
decodeErr, ok := err.(*DecodeError)
35483548
assert.True(t, ok, "expected DecodeError, got %v of type %T", err, err)
3549-
keyPattern := "foo(field Foo).bar(field Bar)"
3550-
assert.True(t, strings.Contains(decodeErr.Error(), keyPattern),
3551-
"expected error %v to contain key pattern %s", decodeErr, keyPattern)
3549+
expectedKeys := []string{"foo", "bar"}
3550+
assert.Equal(t, expectedKeys, decodeErr.Keys(), "expected keys slice %v, got %v", expectedKeys,
3551+
decodeErr.Keys())
3552+
keyPath := strings.Join(expectedKeys, ".")
3553+
assert.True(t, strings.Contains(decodeErr.Error(), keyPath),
3554+
"expected error %v to contain key pattern %s", decodeErr, keyPath)
35523555
})
35533556
})
35543557
}

bson/bsoncodec/struct_codec.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package bsoncodec
88

99
import (
10-
"bytes"
1110
"errors"
1211
"fmt"
1312
"reflect"
@@ -39,16 +38,21 @@ func (de *DecodeError) Unwrap() error {
3938
// Error implements the error interface.
4039
func (de *DecodeError) Error() string {
4140
// The keys are stored in reverse order because the de.keys slice is builtup while propagating the error up the
42-
// stack of BSON keys. Reverse the keys and join them with "." as they're reversed.
43-
var keyPattern bytes.Buffer
41+
// stack of BSON keys, so we call de.Keys(), which reverses them.
42+
keyPath := strings.Join(de.Keys(), ".")
43+
return fmt.Sprintf("error decoding key %s: %v", keyPath, de.wrapped)
44+
}
45+
46+
// Keys returns the BSON key path that caused an error as a slice of strings. The keys in the slice are in top-down
47+
// order. For example, if the document being unmarshalled was {a: {b: {c: 1}}} and the value for c was supposed to be
48+
// a string, the keys slice will be ["a", "b", "c"].
49+
func (de *DecodeError) Keys() []string {
50+
reversedKeys := make([]string, 0, len(de.keys))
4451
for idx := len(de.keys) - 1; idx >= 0; idx-- {
45-
keyPattern.WriteString(de.keys[idx])
46-
if idx != 0 {
47-
keyPattern.WriteByte('.')
48-
}
52+
reversedKeys = append(reversedKeys, de.keys[idx])
4953
}
5054

51-
return fmt.Sprintf("error decoding key %s: %v", keyPattern.String(), de.wrapped)
55+
return reversedKeys
5256
}
5357

5458
// Zeroer allows custom struct types to implement a report of zero
@@ -206,11 +210,6 @@ func newDecodeError(key string, original error) error {
206210
return de
207211
}
208212

209-
func newDecodeErrorFromFieldDescription(fd fieldDescription, original error) error {
210-
fullName := fmt.Sprintf("%s(field %s)", fd.name, fd.fieldName)
211-
return newDecodeError(fullName, original)
212-
}
213-
214213
// DecodeValue implements the Codec interface.
215214
// By default, map types in val will not be cleared. If a map has existing key/value pairs, it will be extended with the new ones from vr.
216215
// For slices, the decoder will set the length of the slice to zero and append all elements. The underlying array will not be cleared.
@@ -320,7 +319,7 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r
320319

321320
if !field.CanSet() { // Being settable is a super set of being addressable.
322321
innerErr := fmt.Errorf("field %v is not settable", field)
323-
return newDecodeErrorFromFieldDescription(fd, innerErr)
322+
return newDecodeError(fd.name, innerErr)
324323
}
325324
if field.Kind() == reflect.Ptr && field.IsNil() {
326325
field.Set(reflect.New(field.Type().Elem()))
@@ -329,19 +328,19 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r
329328

330329
dctx := DecodeContext{Registry: r.Registry, Truncate: fd.truncate || r.Truncate}
331330
if fd.decoder == nil {
332-
return newDecodeErrorFromFieldDescription(fd, ErrNoDecoder{Type: field.Elem().Type()})
331+
return newDecodeError(fd.name, ErrNoDecoder{Type: field.Elem().Type()})
333332
}
334333

335334
if decoder, ok := fd.decoder.(ValueDecoder); ok {
336335
err = decoder.DecodeValue(dctx, vr, field.Elem())
337336
if err != nil {
338-
return newDecodeErrorFromFieldDescription(fd, err)
337+
return newDecodeError(fd.name, err)
339338
}
340339
continue
341340
}
342341
err = fd.decoder.DecodeValue(dctx, vr, field)
343342
if err != nil {
344-
return newDecodeErrorFromFieldDescription(fd, err)
343+
return newDecodeError(fd.name, err)
345344
}
346345
}
347346

0 commit comments

Comments
 (0)