Skip to content

Commit a681184

Browse files
authored
SWIFT-379 Improve error message for type mismatches when decoding driver introduced BSON types (#228)
1 parent 43ebd6c commit a681184

File tree

1 file changed

+46
-58
lines changed

1 file changed

+46
-58
lines changed

Sources/MongoSwift/BSON/BSONValue.swift

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,7 @@ public struct BSONNull: BSONValue, Codable, Equatable {
145145
public init() { }
146146

147147
public init(from decoder: Decoder) throws {
148-
if decoder is _BSONDecoder {
149-
throw bsonDecodingDirectlyError(type: BSONNull.self, at: decoder.codingPath)
150-
}
151-
throw bsonDecodingUnsupportedError(type: BSONNull.self, at: decoder.codingPath)
148+
throw getDecodingError(type: BSONNull.self, decoder: decoder)
152149
}
153150

154151
public func encode(to: Encoder) throws {
@@ -250,10 +247,7 @@ public struct Binary: BSONValue, Equatable, Codable {
250247
}
251248

252249
public init(from decoder: Decoder) throws {
253-
if decoder is _BSONDecoder {
254-
throw bsonDecodingDirectlyError(type: Binary.self, at: decoder.codingPath)
255-
}
256-
throw bsonDecodingUnsupportedError(type: Binary.self, at: decoder.codingPath)
250+
throw getDecodingError(type: Binary.self, decoder: decoder)
257251
}
258252

259253
public func encode(to: Encoder) throws {
@@ -361,10 +355,7 @@ public struct DBPointer: BSONValue, Codable, Equatable {
361355
}
362356

363357
public init(from decoder: Decoder) throws {
364-
if decoder is _BSONDecoder {
365-
throw bsonDecodingDirectlyError(type: DBPointer.self, at: decoder.codingPath)
366-
}
367-
throw bsonDecodingUnsupportedError(type: DBPointer.self, at: decoder.codingPath)
358+
throw getDecodingError(type: DBPointer.self, decoder: decoder)
368359
}
369360

370361
public func encode(to: Encoder) throws {
@@ -439,10 +430,7 @@ public struct Decimal128: BSONValue, Equatable, Codable, CustomStringConvertible
439430
}
440431

441432
public init(from decoder: Decoder) throws {
442-
if decoder is _BSONDecoder {
443-
throw bsonDecodingDirectlyError(type: Decimal128.self, at: decoder.codingPath)
444-
}
445-
throw bsonDecodingUnsupportedError(type: Decimal128.self, at: decoder.codingPath)
433+
throw getDecodingError(type: Decimal128.self, decoder: decoder)
446434
}
447435

448436
public func encode(to: Encoder) throws {
@@ -586,10 +574,7 @@ public struct CodeWithScope: BSONValue, Equatable, Codable {
586574
}
587575

588576
public init(from decoder: Decoder) throws {
589-
if decoder is _BSONDecoder {
590-
throw bsonDecodingDirectlyError(type: CodeWithScope.self, at: decoder.codingPath)
591-
}
592-
throw bsonDecodingUnsupportedError(type: CodeWithScope.self, at: decoder.codingPath)
577+
throw getDecodingError(type: CodeWithScope.self, decoder: decoder)
593578
}
594579

595580
public func encode(to: Encoder) throws {
@@ -657,10 +642,7 @@ public struct MaxKey: BSONValue, Equatable, Codable {
657642
public init() {}
658643

659644
public init(from decoder: Decoder) throws {
660-
if decoder is _BSONDecoder {
661-
throw bsonDecodingDirectlyError(type: MaxKey.self, at: decoder.codingPath)
662-
}
663-
throw bsonDecodingUnsupportedError(type: MaxKey.self, at: decoder.codingPath)
645+
throw getDecodingError(type: MaxKey.self, decoder: decoder)
664646
}
665647

666648
public func encode(to: Encoder) throws {
@@ -693,10 +675,7 @@ public struct MinKey: BSONValue, Equatable, Codable {
693675
public init() {}
694676

695677
public init(from decoder: Decoder) throws {
696-
if decoder is _BSONDecoder {
697-
throw bsonDecodingDirectlyError(type: MinKey.self, at: decoder.codingPath)
698-
}
699-
throw bsonDecodingUnsupportedError(type: MinKey.self, at: decoder.codingPath)
678+
throw getDecodingError(type: MinKey.self, decoder: decoder)
700679
}
701680

702681
public func encode(to: Encoder) throws {
@@ -751,10 +730,7 @@ public struct ObjectId: BSONValue, Equatable, CustomStringConvertible, Codable {
751730
}
752731

753732
public init(from decoder: Decoder) throws {
754-
if decoder is _BSONDecoder {
755-
throw bsonDecodingDirectlyError(type: ObjectId.self, at: decoder.codingPath)
756-
}
757-
throw bsonDecodingUnsupportedError(type: ObjectId.self, at: decoder.codingPath)
733+
throw getDecodingError(type: ObjectId.self, decoder: decoder)
758734
}
759735

760736
public func encode(to: Encoder) throws {
@@ -896,10 +872,7 @@ public struct RegularExpression: BSONValue, Equatable, Codable {
896872
}
897873

898874
public init(from decoder: Decoder) throws {
899-
if decoder is _BSONDecoder {
900-
throw bsonDecodingDirectlyError(type: RegularExpression.self, at: decoder.codingPath)
901-
}
902-
throw bsonDecodingUnsupportedError(type: RegularExpression.self, at: decoder.codingPath)
875+
throw getDecodingError(type: RegularExpression.self, decoder: decoder)
903876
}
904877

905878
public func encode(to: Encoder) throws {
@@ -984,10 +957,7 @@ public struct Symbol: BSONValue, CustomStringConvertible, Codable, Equatable {
984957
public let stringValue: String
985958

986959
public init(from decoder: Decoder) throws {
987-
if decoder is _BSONDecoder {
988-
throw bsonDecodingDirectlyError(type: Symbol.self, at: decoder.codingPath)
989-
}
990-
throw bsonDecodingUnsupportedError(type: Symbol.self, at: decoder.codingPath)
960+
throw getDecodingError(type: Symbol.self, decoder: decoder)
991961
}
992962

993963
internal init(_ stringValue: String) {
@@ -1050,10 +1020,7 @@ public struct Timestamp: BSONValue, Equatable, Codable {
10501020
}
10511021

10521022
public init(from decoder: Decoder) throws {
1053-
if decoder is _BSONDecoder {
1054-
throw bsonDecodingDirectlyError(type: Timestamp.self, at: decoder.codingPath)
1055-
}
1056-
throw bsonDecodingUnsupportedError(type: Timestamp.self, at: decoder.codingPath)
1023+
throw getDecodingError(type: Timestamp.self, decoder: decoder)
10571024
}
10581025

10591026
public func encode(to: Encoder) throws {
@@ -1091,10 +1058,7 @@ public struct BSONUndefined: BSONValue, Equatable, Codable {
10911058
internal init() {}
10921059

10931060
public init(from decoder: Decoder) throws {
1094-
if decoder is _BSONDecoder {
1095-
throw bsonDecodingDirectlyError(type: BSONUndefined.self, at: decoder.codingPath)
1096-
}
1097-
throw bsonDecodingUnsupportedError(type: BSONUndefined.self, at: decoder.codingPath)
1061+
throw getDecodingError(type: BSONUndefined.self, decoder: decoder)
10981062
}
10991063

11001064
public func encode(to: Encoder) throws {
@@ -1205,21 +1169,45 @@ private func bsonDecodingUnsupportedError<T: BSONValue>(type: T.Type, at codingP
12051169
}
12061170

12071171
/**
1208-
* Error thrown when a `BSONValue` type introduced by the driver (e.g. ObjectId) is decoded via the decoder
1209-
* initializer when using `BSONDecoder`. These introduced types are BSON primitives that do not exist in Swift.
1210-
* Since they're BSON primitives, they should be read straight from the document via the underlying `bson_t`,
1211-
* and `BSONDecoder` should never be calling into init(from:Decoder) to initialize them.
1212-
*
1213-
* Example error causes:
1214-
* - Decoding directly from Document: decoder.decode(ObjectId.self, from: doc)
1215-
* - Attempting to decode by field names: decoder.decode(CodeWithScope.self, from: "{\"code": \"code\"}")
1172+
* Error thrown when a `BSONValue` type introduced by the driver (e.g. ObjectId) is decoded directly via the top-level
1173+
* `BSONDecoder`.
12161174
*/
12171175
private func bsonDecodingDirectlyError<T: BSONValue>(type: T.Type, at codingPath: [CodingKey]) -> DecodingError {
1218-
let description = "Cannot initialize a BSONValue type \(T.self) directly from BSONDecoder. It must be a member of" +
1219-
" a struct or a class."
1176+
let description = "Cannot initialize BSONValue type \(T.self) directly from BSONDecoder. It must be decoded as " +
1177+
"a member of a struct or a class."
12201178

12211179
return DecodingError.typeMismatch(
12221180
T.self,
12231181
DecodingError.Context(codingPath: codingPath, debugDescription: description)
12241182
)
12251183
}
1184+
1185+
/**
1186+
* This function determines which error to throw when a driver-introduced BSON type is decoded via its init(decoder).
1187+
* The types that use this function are all BSON primitives, so they should be decoded directly in `_BSONDecoder`. If
1188+
* execution reaches their decoding initializer, it means something went wrong. This function determines an appropriate
1189+
* error to throw for each possible case.
1190+
*
1191+
* Some example cases:
1192+
* - Decoding directly from the BSONDecoder top-level (e.g. BSONDecoder().decode(ObjectId.self, from: ...))
1193+
* - Encountering the wrong type of BSONValue (e.g. expected "_id" to be an `ObjectId`, got a `Document` instead)
1194+
* - Attempting to decode a driver-introduced BSONValue with a non-BSONDecoder
1195+
*/
1196+
internal func getDecodingError<T: BSONValue>(type: T.Type, decoder: Decoder) -> DecodingError {
1197+
if let bsonDecoder = decoder as? _BSONDecoder {
1198+
// Cannot decode driver-introduced BSONValues directly
1199+
if decoder.codingPath.isEmpty {
1200+
return bsonDecodingDirectlyError(type: T.self, at: decoder.codingPath)
1201+
}
1202+
1203+
// Got the wrong BSONValue type
1204+
return DecodingError._typeMismatch(
1205+
at: decoder.codingPath,
1206+
expectation: T.self,
1207+
reality: bsonDecoder.storage.topContainer
1208+
)
1209+
}
1210+
1211+
// Non-BSONDecoders are currently unsupported
1212+
return bsonDecodingUnsupportedError(type: T.self, at: decoder.codingPath)
1213+
}

0 commit comments

Comments
 (0)