Skip to content

Commit ba1825d

Browse files
authored
SWIFT-858: Duplicate pure Swift BSON breaking changes in driver (#477)
1 parent f00cb40 commit ba1825d

File tree

12 files changed

+196
-137
lines changed

12 files changed

+196
-137
lines changed

Sources/MongoSwift/BSON/BSON.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,11 @@ public enum BSON {
288288
case let .decimal128(d):
289289
return d
290290
case let .int64(i):
291-
return BSONDecimal128(String(i))
291+
return try? BSONDecimal128(String(i))
292292
case let .int32(i):
293-
return BSONDecimal128(String(i))
293+
return try? BSONDecimal128(String(i))
294294
case let .double(d):
295-
return BSONDecimal128(String(d))
295+
return try? BSONDecimal128(String(d))
296296
default:
297297
return nil
298298
}
@@ -306,7 +306,7 @@ extension BSON {
306306
BSONNull.self,
307307
BSONUndefined.self,
308308
BSONMinKey.self,
309-
MaxKey.self,
309+
BSONMaxKey.self,
310310
BSONSymbol.self,
311311
Double.self,
312312
String.self,
@@ -336,7 +336,7 @@ extension BSON {
336336
case .minKey:
337337
return BSONMinKey()
338338
case .maxKey:
339-
return MaxKey()
339+
return BSONMaxKey()
340340
case let .symbol(v):
341341
return v
342342
case let .double(v):

Sources/MongoSwift/BSON/BSONDecoder.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,10 @@ extension _BSONDecoder {
359359
return try Data(from: self)
360360
case .binary:
361361
let binary = try self.unboxCustom(value) { $0.binaryValue }
362-
return binary.data
362+
guard let data = binary.data.getBytes(at: 0, length: binary.data.writerIndex) else {
363+
throw InternalError(message: "Cannot read \(binary.data.writerIndex) bytes from Binary.data")
364+
}
365+
return Data(data)
363366
case .base64:
364367
let base64Str = try self.unboxCustom(value) { $0.stringValue }
365368

Sources/MongoSwift/BSON/BSONDocument.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,8 @@ extension BSONDocument {
329329
return String(cString: json)
330330
}
331331

332-
/// Returns a copy of the raw BSON data for this `BSONDocument`, represented as `Data`.
333-
public var rawBSON: Data {
332+
/// Returns a copy of the raw BSON data for this `Document`, represented as `Data`.
333+
public func toData() -> Data {
334334
let data = self.withBSONPointer { ptr in
335335
// swiftlint:disable:next force_unwrapping
336336
bson_get_data(ptr)! // documented as always returning a value.
@@ -448,7 +448,7 @@ extension BSONDocument {
448448
self = newSelf
449449
}
450450
} catch {
451-
fatalError("Failed to set the value for key \(key) to \(newValue ?? "nil"): \(error)")
451+
fatalError("Failed to set the value for key \"\(key)\" to \(newValue ?? "nil"): \(error)")
452452
}
453453
}
454454
}

Sources/MongoSwift/BSON/BSONDocumentIterator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public class BSONDocumentIterator: IteratorProtocol {
7979
/// Returns the current value's type. Assumes the iterator is in a valid position.
8080
internal var currentType: BSONType {
8181
self.withBSONIterPointer { iterPtr in
82-
BSONType(rawValue: bson_iter_type(iterPtr).rawValue) ?? .invalid
82+
BSONType(rawValue: UInt8(bson_iter_type(iterPtr).rawValue)) ?? .invalid
8383
}
8484
}
8585

@@ -219,7 +219,7 @@ public class BSONDocumentIterator: IteratorProtocol {
219219
.int64: Int64.self,
220220
.decimal128: BSONDecimal128.self,
221221
.minKey: BSONMinKey.self,
222-
.maxKey: MaxKey.self,
222+
.maxKey: BSONMaxKey.self,
223223
.null: BSONNull.self,
224224
.undefined: BSONUndefined.self
225225
]

Sources/MongoSwift/BSON/BSONValue.swift

Lines changed: 96 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import CLibMongoC
22
import Foundation
3+
import NIO
4+
5+
/// This shared allocator instance should be used for all underlying `ByteBuffer` creation.
6+
private let BSON_ALLOCATOR = ByteBufferAllocator()
37

48
/// The possible types of BSON values and their corresponding integer values.
5-
public enum BSONType: UInt32 {
9+
public enum BSONType: UInt8 {
610
/// An invalid type
711
case invalid = 0x00
812
/// 64-bit binary floating point
@@ -196,27 +200,61 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
196200
internal var bson: BSON { .binary(self) }
197201

198202
/// The binary data.
199-
public let data: Data
203+
public let data: ByteBuffer
200204

201205
/// The binary subtype for this data.
202-
public let subtype: UInt8
206+
public let subtype: Subtype
203207

204208
/// Subtypes for BSON Binary values.
205-
public enum Subtype: UInt8 {
209+
public struct Subtype: Equatable, Codable, Hashable, RawRepresentable {
210+
// swiftlint:disable force_unwrapping
206211
/// Generic binary subtype
207-
case generic,
208-
/// A function
209-
function,
210-
/// Binary (old)
211-
binaryDeprecated,
212-
/// UUID (old)
213-
uuidDeprecated,
214-
/// UUID (RFC 4122)
215-
uuid,
216-
/// MD5
217-
md5,
218-
/// User defined
219-
userDefined = 0x80
212+
public static let generic = Subtype(rawValue: 0x00)!
213+
/// A function
214+
public static let function = Subtype(rawValue: 0x01)!
215+
/// Binary (old)
216+
public static let binaryDeprecated = Subtype(rawValue: 0x02)!
217+
/// UUID (old)
218+
public static let uuidDeprecated = Subtype(rawValue: 0x03)!
219+
/// UUID (RFC 4122)
220+
public static let uuid = Subtype(rawValue: 0x04)!
221+
/// MD5
222+
public static let md5 = Subtype(rawValue: 0x05)!
223+
/// Encrypted BSON value
224+
public static let encryptedValue = Subtype(rawValue: 0x06)!
225+
// swiftlint:enable force_unwrapping
226+
227+
/// Subtype indicator value
228+
public let rawValue: UInt8
229+
230+
/// Initializes a `Subtype` with a custom value.
231+
/// Returns nil if rawValue within reserved range [0x07, 0x80).
232+
public init?(rawValue: UInt8) {
233+
guard !(rawValue > 0x06 && rawValue < 0x80) else {
234+
return nil
235+
}
236+
self.rawValue = rawValue
237+
}
238+
239+
internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) }
240+
241+
/// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF.
242+
/// - Throws:
243+
/// - `InvalidArgumentError` if value passed is outside of the range 0x80-0xFF
244+
public static func userDefined(_ value: Int) throws -> Subtype {
245+
guard let byteValue = UInt8(exactly: value) else {
246+
throw InvalidArgumentError(message: "Cannot represent \(value) as UInt8")
247+
}
248+
guard byteValue >= 0x80 else {
249+
throw InvalidArgumentError(
250+
message: "userDefined value must be greater than or equal to 0x80 got \(byteValue)"
251+
)
252+
}
253+
guard let subtype = Subtype(rawValue: byteValue) else {
254+
throw InvalidArgumentError(message: "Cannot represent \(byteValue) as Subtype")
255+
}
256+
return subtype
257+
}
220258
}
221259

222260
/// Initializes a `BSONBinary` instance from a `UUID`.
@@ -238,29 +276,24 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
238276
/// Initializes a `BSONBinary` instance from a `Data` object and a `UInt8` subtype.
239277
/// - Throws:
240278
/// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype.
241-
public init(data: Data, subtype: UInt8) throws {
242-
if [Subtype.uuid.rawValue, Subtype.uuidDeprecated.rawValue].contains(subtype) && data.count != 16 {
279+
public init(data: Data, subtype: Subtype) throws {
280+
if [Subtype.uuid, Subtype.uuidDeprecated].contains(subtype) && data.count != 16 {
243281
throw InvalidArgumentError(
244282
message:
245283
"Binary data with UUID subtype must be 16 bytes, but data has \(data.count) bytes"
246284
)
247285
}
248286
self.subtype = subtype
249-
self.data = data
250-
}
251-
252-
/// Initializes a `BSONBinary` instance from a `Data` object and a `Subtype`.
253-
/// - Throws:
254-
/// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype.
255-
public init(data: Data, subtype: Subtype) throws {
256-
try self.init(data: data, subtype: subtype.rawValue)
287+
var buffer = BSON_ALLOCATOR.buffer(capacity: data.count)
288+
buffer.writeBytes(data)
289+
self.data = buffer
257290
}
258291

259-
/// Initializes a `BSONBinary` instance from a base64 `String` and a `UInt8` subtype.
292+
/// Initializes a `BSONBinary` instance from a base64 `String` and a `Subtype`.
260293
/// - Throws:
261294
/// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is
262295
/// incompatible with the specified subtype.
263-
public init(base64: String, subtype: UInt8) throws {
296+
public init(base64: String, subtype: Subtype) throws {
264297
guard let dataObj = Data(base64Encoded: base64) else {
265298
throw InvalidArgumentError(
266299
message:
@@ -270,14 +303,6 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
270303
try self.init(data: dataObj, subtype: subtype)
271304
}
272305

273-
/// Initializes a `BSONBinary` instance from a base64 `String` and a `Subtype`.
274-
/// - Throws:
275-
/// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is
276-
/// incompatible with the specified subtype.
277-
public init(base64: String, subtype: Subtype) throws {
278-
try self.init(base64: base64, subtype: subtype.rawValue)
279-
}
280-
281306
public init(from decoder: Decoder) throws {
282307
throw getDecodingError(type: BSONBinary.self, decoder: decoder)
283308
}
@@ -287,9 +312,11 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
287312
}
288313

289314
internal func encode(to document: inout BSONDocument, forKey key: String) throws {
290-
let subtype = bson_subtype_t(UInt32(self.subtype))
291-
let length = self.data.count
292-
let byteArray = [UInt8](self.data)
315+
let subtype = bson_subtype_t(UInt32(self.subtype.rawValue))
316+
let length = self.data.writerIndex
317+
guard let byteArray = self.data.getBytes(at: 0, length: length) else {
318+
throw InternalError(message: "Cannot read \(length) bytes from Binary.data")
319+
}
293320
try document.withMutableBSONPointer { docPtr in
294321
guard bson_append_binary(docPtr, key, Int32(key.utf8.count), subtype, byteArray, UInt32(length)) else {
295322
throw bsonTooLargeError(value: self, forKey: key)
@@ -318,21 +345,24 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
318345
}
319346

320347
let dataObj = Data(bytes: data, count: Int(length))
321-
return try self.init(data: dataObj, subtype: UInt8(subtype.rawValue))
348+
return try self.init(data: dataObj, subtype: Subtype(subtype))
322349
})
323350
}
324351

325352
/// Converts this `BSONBinary` instance to a `UUID`.
326353
/// - Throws:
327354
/// - `InvalidArgumentError` if a non-UUID subtype is set on this `BSONBinary`.
328355
public func toUUID() throws -> UUID {
329-
guard [Subtype.uuid.rawValue, Subtype.uuidDeprecated.rawValue].contains(self.subtype) else {
356+
guard [Subtype.uuid, Subtype.uuidDeprecated].contains(self.subtype) else {
330357
throw InvalidArgumentError(
331358
message: "Expected a UUID binary subtype, got subtype \(self.subtype) instead."
332359
)
333360
}
334361

335-
let data = self.data
362+
guard let data = self.data.getBytes(at: 0, length: 16) else {
363+
throw InternalError(message: "Unable to read 16 bytes from Binary.data")
364+
}
365+
336366
let uuid: uuid_t = (
337367
data[0], data[1], data[2], data[3],
338368
data[4], data[5], data[6], data[7],
@@ -487,16 +517,20 @@ public struct BSONDecimal128: BSONValue, Equatable, Codable, CustomStringConvert
487517
self.decimal128 = bsonDecimal
488518
}
489519

490-
/// Initializes a `BSONDecimal128` value from the provided `String`. Returns `nil` if the input is not a valid
491-
/// Decimal128 string.
492-
/// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst
493-
public init?(_ data: String) {
494-
do {
495-
let bsonType = try BSONDecimal128.toLibBSONType(data)
496-
self.init(bsonDecimal: bsonType)
497-
} catch {
498-
return nil
499-
}
520+
/**
521+
* Initializes a `BSONDecimal128` value from the provided `String`.
522+
*
523+
* - Parameters:
524+
* - a BSONDecimal128 number as a string.
525+
*
526+
* - Throws:
527+
* - A `InvalidArgumentError` if the string does not represent a BSONDecimal128 encodable value.
528+
*
529+
* - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst
530+
*/
531+
public init(_ data: String) throws {
532+
let bsonType = try BSONDecimal128.toLibBSONType(data)
533+
self.init(bsonDecimal: bsonType)
500534
}
501535

502536
public init(from decoder: Decoder) throws {
@@ -731,7 +765,7 @@ public struct BSONCode: BSONValue, Equatable, Codable, Hashable {
731765
}
732766

733767
/// A struct to represent the BSON MaxKey type.
734-
internal struct MaxKey: BSONValue, Equatable, Codable, Hashable {
768+
internal struct BSONMaxKey: BSONValue, Equatable, Codable, Hashable {
735769
internal var bson: BSON { .maxKey }
736770

737771
internal static var bsonType: BSONType { .maxKey }
@@ -748,7 +782,7 @@ internal struct MaxKey: BSONValue, Equatable, Codable, Hashable {
748782
internal init() {}
749783

750784
internal init(from decoder: Decoder) throws {
751-
throw getDecodingError(type: MaxKey.self, decoder: decoder)
785+
throw getDecodingError(type: BSONMaxKey.self, decoder: decoder)
752786
}
753787

754788
internal func encode(to: Encoder) throws {
@@ -757,7 +791,7 @@ internal struct MaxKey: BSONValue, Equatable, Codable, Hashable {
757791

758792
internal static func from(iterator iter: BSONDocumentIterator) throws -> BSON {
759793
guard iter.currentType == .maxKey else {
760-
throw wrongIterTypeError(iter, expected: MaxKey.self)
794+
throw wrongIterTypeError(iter, expected: BSONMaxKey.self)
761795
}
762796
return .maxKey
763797
}
@@ -813,11 +847,6 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab
813847
}
814848
}
815849

816-
/// The timestamp used to create this `BSONObjectID`
817-
public var timestamp: UInt32 {
818-
withUnsafePointer(to: self.oid) { oidPtr in UInt32(bson_oid_get_time_t(oidPtr)) }
819-
}
820-
821850
public var description: String {
822851
self.hex
823852
}
@@ -831,12 +860,13 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab
831860
self.oid = oid
832861
}
833862

834-
/// Initializes an `BSONObjectID` from the provided hex `String`. Returns `nil` if the string is not a valid
835-
/// ObjectID.
863+
/// Initializes an `BSONObjectID` from the provided hex `String`.
864+
/// - Throws:
865+
/// - `InvalidArgumentError` if string passed is not a valid BSONObjectID
836866
/// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/objectid.rst
837-
public init?(_ hex: String) {
867+
public init(_ hex: String) throws {
838868
guard bson_oid_is_valid(hex, hex.utf8.count) else {
839-
return nil
869+
throw InvalidArgumentError(message: "Cannot create ObjectId from \(hex)")
840870
}
841871
var oid_t = bson_oid_t()
842872
bson_oid_init_from_string(&oid_t, hex)
@@ -851,7 +881,7 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab
851881
// assumes that the BSONObjectID is stored as a valid hex string.
852882
let container = try decoder.singleValueContainer()
853883
let hex = try container.decode(String.self)
854-
guard let oid = BSONObjectID(hex) else {
884+
guard let oid = try? BSONObjectID(hex) else {
855885
throw DecodingError.dataCorrupted(
856886
DecodingError.Context(
857887
codingPath: decoder.codingPath,

Sources/MongoSwift/BSON/Overwritable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ internal protocol Overwritable: BSONValue {
88
*
99
* - Throws:
1010
* - `InternalError` if the `BSONValue` is an `Int` and cannot be written to BSON.
11-
* - `LogicError` if the `BSONValue` is a `Decimal128` or `BSONObjectID` and is improperly formatted.
11+
* - `LogicError` if the `BSONValue` is a `BSONDecimal128` or `BSONObjectID` and is improperly formatted.
1212
*/
1313
func writeToCurrentPosition(of iter: BSONDocumentIterator) throws
1414
}

0 commit comments

Comments
 (0)