From a8a5a7d9b32810363fec8032b052d82fe91cd2fe Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 20 May 2020 14:48:19 -0400 Subject: [PATCH 01/11] chore: make breaking changes for swift-bson compat --- Sources/MongoSwift/BSON/BSON.swift | 6 +- Sources/MongoSwift/BSON/BSONDecoder.swift | 5 +- Sources/MongoSwift/BSON/BSONDocument.swift | 6 +- .../BSON/BSONDocumentIterator.swift | 2 +- Sources/MongoSwift/BSON/BSONValue.swift | 126 +++++++++++------- Tests/BSONTests/BSONCorpusTests.swift | 18 +-- Tests/BSONTests/BSONValueTests.swift | 28 ++-- Tests/BSONTests/CodecTests.swift | 8 +- Tests/BSONTests/Document+SequenceTests.swift | 8 +- Tests/BSONTests/DocumentTests.swift | 30 ++--- 10 files changed, 131 insertions(+), 106 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSON.swift b/Sources/MongoSwift/BSON/BSON.swift index fbc56e66d..ea07e9a66 100644 --- a/Sources/MongoSwift/BSON/BSON.swift +++ b/Sources/MongoSwift/BSON/BSON.swift @@ -288,11 +288,11 @@ public enum BSON { case let .decimal128(d): return d case let .int64(i): - return BSONDecimal128(String(i)) + return try? BSONDecimal128(String(i)) case let .int32(i): - return BSONDecimal128(String(i)) + return try? BSONDecimal128(String(i)) case let .double(d): - return BSONDecimal128(String(d)) + return try? BSONDecimal128(String(d)) default: return nil } diff --git a/Sources/MongoSwift/BSON/BSONDecoder.swift b/Sources/MongoSwift/BSON/BSONDecoder.swift index be479f6d7..96351c0ea 100644 --- a/Sources/MongoSwift/BSON/BSONDecoder.swift +++ b/Sources/MongoSwift/BSON/BSONDecoder.swift @@ -359,7 +359,10 @@ extension _BSONDecoder { return try Data(from: self) case .binary: let binary = try self.unboxCustom(value) { $0.binaryValue } - return binary.data + guard let data = binary.data.getBytes(at: 0, length: binary.data.writerIndex) else { + throw InternalError(message: "Cannot read \(binary.data.writerIndex) bytes from Binary.data") + } + return Data(data) case .base64: let base64Str = try self.unboxCustom(value) { $0.stringValue } diff --git a/Sources/MongoSwift/BSON/BSONDocument.swift b/Sources/MongoSwift/BSON/BSONDocument.swift index 78bd293b8..e87340b05 100644 --- a/Sources/MongoSwift/BSON/BSONDocument.swift +++ b/Sources/MongoSwift/BSON/BSONDocument.swift @@ -329,8 +329,8 @@ extension BSONDocument { return String(cString: json) } - /// Returns a copy of the raw BSON data for this `BSONDocument`, represented as `Data`. - public var rawBSON: Data { + /// Returns a copy of the raw BSON data for this `Document`, represented as `Data`. + public func toData() -> Data { let data = self.withBSONPointer { ptr in // swiftlint:disable:next force_unwrapping bson_get_data(ptr)! // documented as always returning a value. @@ -448,7 +448,7 @@ extension BSONDocument { self = newSelf } } catch { - fatalError("Failed to set the value for key \(key) to \(newValue ?? "nil"): \(error)") + fatalError("Failed to set the value for key \"\(key)\" to \(newValue ?? "nil"): \(error)") } } } diff --git a/Sources/MongoSwift/BSON/BSONDocumentIterator.swift b/Sources/MongoSwift/BSON/BSONDocumentIterator.swift index 2a3819d32..bc1287669 100644 --- a/Sources/MongoSwift/BSON/BSONDocumentIterator.swift +++ b/Sources/MongoSwift/BSON/BSONDocumentIterator.swift @@ -79,7 +79,7 @@ public class BSONDocumentIterator: IteratorProtocol { /// Returns the current value's type. Assumes the iterator is in a valid position. internal var currentType: BSONType { self.withBSONIterPointer { iterPtr in - BSONType(rawValue: bson_iter_type(iterPtr).rawValue) ?? .invalid + BSONType(rawValue: UInt8(bson_iter_type(iterPtr).rawValue)) ?? .invalid } } diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index c4163b5b9..5c79a78ec 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -1,8 +1,12 @@ import CLibMongoC import Foundation +import NIO + +/// This shared allocator instance should be used for all underlying `ByteBuffer` creation. +private let BSON_ALLOCATOR = ByteBufferAllocator() /// The possible types of BSON values and their corresponding integer values. -public enum BSONType: UInt32 { +public enum BSONType: UInt8 { /// An invalid type case invalid = 0x00 /// 64-bit binary floating point @@ -196,27 +200,36 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { internal var bson: BSON { .binary(self) } /// The binary data. - public let data: Data + public let data: ByteBuffer /// The binary subtype for this data. - public let subtype: UInt8 + public let subtype: Subtype /// Subtypes for BSON Binary values. - public enum Subtype: UInt8 { + public struct Subtype: Equatable, Codable, Hashable { /// Generic binary subtype - case generic, - /// A function - function, - /// Binary (old) - binaryDeprecated, - /// UUID (old) - uuidDeprecated, - /// UUID (RFC 4122) - uuid, - /// MD5 - md5, - /// User defined - userDefined = 0x80 + public static let generic = Subtype(0x00) + /// A function + public static let function = Subtype(0x01) + /// Binary (old) + public static let binaryDeprecated = Subtype(0x02) + /// UUID (old) + public static let uuidDeprecated = Subtype(0x03) + /// UUID (RFC 4122) + public static let uuid = Subtype(0x04) + /// MD5 + public static let md5 = Subtype(0x05) + /// Encrypted BSON value + public static let encryptedValue = Subtype(0x06) + + public let rawValue: UInt8 + + private init(_ value: UInt8) { self.rawValue = value } + internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) } + + /// Create BSON Binary Subtype + /// Note: subtype 0x80-0xFF "User defined" subtype + public static func other(_ value: Int) -> Subtype { Subtype(UInt8(value)) } } /// Initializes a `BSONBinary` instance from a `UUID`. @@ -238,8 +251,8 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// Initializes a `BSONBinary` instance from a `Data` object and a `UInt8` subtype. /// - Throws: /// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype. - public init(data: Data, subtype: UInt8) throws { - if [Subtype.uuid.rawValue, Subtype.uuidDeprecated.rawValue].contains(subtype) && data.count != 16 { + public init(data: Data, subtype: Subtype) throws { + if [Subtype.uuid, Subtype.uuidDeprecated].contains(subtype) && data.count != 16 { throw InvalidArgumentError( message: "Binary data with UUID subtype must be 16 bytes, but data has \(data.count) bytes" @@ -253,14 +266,16 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// - Throws: /// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype. public init(data: Data, subtype: Subtype) throws { - try self.init(data: data, subtype: subtype.rawValue) + var buffer = BSON_ALLOCATOR.buffer(capacity: data.count) + buffer.writeBytes(data) + self.data = buffer } - /// Initializes a `BSONBinary` instance from a base64 `String` and a `UInt8` subtype. + /// Initializes a `Binary` instance from a base64 `String` and a `Subtype`. /// - Throws: /// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is /// incompatible with the specified subtype. - public init(base64: String, subtype: UInt8) throws { + public init(base64: String, subtype: Subtype) throws { guard let dataObj = Data(base64Encoded: base64) else { throw InvalidArgumentError( message: @@ -270,14 +285,6 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { try self.init(data: dataObj, subtype: subtype) } - /// Initializes a `BSONBinary` instance from a base64 `String` and a `Subtype`. - /// - Throws: - /// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is - /// incompatible with the specified subtype. - public init(base64: String, subtype: Subtype) throws { - try self.init(base64: base64, subtype: subtype.rawValue) - } - public init(from decoder: Decoder) throws { throw getDecodingError(type: BSONBinary.self, decoder: decoder) } @@ -286,10 +293,12 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { throw bsonEncodingUnsupportedError(value: self, at: to.codingPath) } - internal func encode(to document: inout BSONDocument, forKey key: String) throws { - let subtype = bson_subtype_t(UInt32(self.subtype)) - let length = self.data.count - let byteArray = [UInt8](self.data) + internal func encode(to document: inout Document, forKey key: String) throws { + let subtype = bson_subtype_t(UInt32(self.subtype.rawValue)) + let length = self.data.writerIndex + guard let byteArray = self.data.getBytes(at: 0, length: length) else { + throw InternalError(message: "Cannot read \(length) bytes from Binary.data") + } try document.withMutableBSONPointer { docPtr in guard bson_append_binary(docPtr, key, Int32(key.utf8.count), subtype, byteArray, UInt32(length)) else { throw bsonTooLargeError(value: self, forKey: key) @@ -318,7 +327,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { } let dataObj = Data(bytes: data, count: Int(length)) - return try self.init(data: dataObj, subtype: UInt8(subtype.rawValue)) + return try self.init(data: dataObj, subtype: Subtype(subtype)) }) } @@ -490,13 +499,9 @@ public struct BSONDecimal128: BSONValue, Equatable, Codable, CustomStringConvert /// Initializes a `BSONDecimal128` value from the provided `String`. Returns `nil` if the input is not a valid /// Decimal128 string. /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst - public init?(_ data: String) { - do { - let bsonType = try BSONDecimal128.toLibBSONType(data) - self.init(bsonDecimal: bsonType) - } catch { - return nil - } + public init(_ data: String) throws { + let bsonType = try BSONDecimal128.toLibBSONType(data) + self.init(bsonDecimal: bsonType) } public init(from decoder: Decoder) throws { @@ -813,11 +818,6 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab } } - /// The timestamp used to create this `BSONObjectID` - public var timestamp: UInt32 { - withUnsafePointer(to: self.oid) { oidPtr in UInt32(bson_oid_get_time_t(oidPtr)) } - } - public var description: String { self.hex } @@ -834,9 +834,9 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab /// Initializes an `BSONObjectID` from the provided hex `String`. Returns `nil` if the string is not a valid /// ObjectID. /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/objectid.rst - public init?(_ hex: String) { + public init(_ hex: String) throws { guard bson_oid_is_valid(hex, hex.utf8.count) else { - return nil + throw InternalError(message: "Cannot create ObjectId from \(hex)") } var oid_t = bson_oid_t() bson_oid_init_from_string(&oid_t, hex) @@ -907,6 +907,34 @@ extension BSONObjectID: Hashable { } } +/// Extension to allow a `UUID` to be initialized from a `Binary` `BSONValue`. +extension UUID { + /// Initializes a `UUID` instance from a `Binary` `BSONValue`. + /// - Throws: + /// - `InvalidArgumentError` if a non-UUID subtype is set on the `Binary`. + public init(from binary: BSONBinary) throws { + guard [BSONBinary.Subtype.uuid, BSONBinary.Subtype.uuidDeprecated].contains(binary.subtype) else { + throw InvalidArgumentError( + message: "Expected a UUID binary type " + + "(\(BSONBinary.Subtype.uuid)), got \(binary.subtype) instead." + ) + } + + guard let data = binary.data.getBytes(at: 0, length: 16) else { + throw InternalError(message: "Unable to read 16 bytes from Binary.data") + } + + let uuid: uuid_t = ( + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15] + ) + + self.init(uuid: uuid) + } +} + // A mapping of regex option characters to their equivalent `NSRegularExpression` option. // note that there is a BSON regexp option 'l' that `NSRegularExpression` // doesn't support. The flag will be dropped if BSON containing it is parsed, diff --git a/Tests/BSONTests/BSONCorpusTests.swift b/Tests/BSONTests/BSONCorpusTests.swift index 0c63a8d74..0722a3e23 100644 --- a/Tests/BSONTests/BSONCorpusTests.swift +++ b/Tests/BSONTests/BSONCorpusTests.swift @@ -137,8 +137,8 @@ final class BSONCorpusTests: MongoSwiftTestCase { // for cB input: // native_to_bson( bson_to_native(cB) ) = cB - let docFromCB = try BSONDocument(fromBSON: cBData) - expect(docFromCB.rawBSON).to(equal(cBData)) + let docFromCB = try Document(fromBSON: cBData) + expect(docFromCB.toData()).to(equal(cBData)) // test round tripping through documents // We create an array by reading every element out of the document (and therefore out of the @@ -147,8 +147,8 @@ final class BSONCorpusTests: MongoSwiftTestCase { // -> bson_t. At the end, the new bson_t should be identical to the original one. If not, our bson_t // translation layer is lossy and/or buggy. let nativeFromDoc = docFromCB.toArray() - let docFromNative = BSONDocument(fromArray: nativeFromDoc) - expect(docFromNative.rawBSON).to(equal(cBData)) + let docFromNative = Document(fromArray: nativeFromDoc) + expect(docFromNative.toData()).to(equal(cBData)) // native_to_canonical_extended_json( bson_to_native(cB) ) = cEJ expect(docFromCB.canonicalExtendedJSON).to(cleanEqual(test.canonicalExtJSON)) @@ -165,7 +165,7 @@ final class BSONCorpusTests: MongoSwiftTestCase { // native_to_bson( json_to_native(cEJ) ) = cB (unless lossy) if !lossy { - expect(try BSONDocument(fromJSON: cEJData).rawBSON).to(equal(cBData)) + expect(try Document(fromJSON: cEJData).toData()).to(equal(cBData)) } // for dB input (if it exists): @@ -193,7 +193,7 @@ final class BSONCorpusTests: MongoSwiftTestCase { // native_to_bson( json_to_native(dEJ) ) = cB (unless lossy) if !lossy { - expect(try BSONDocument(fromJSON: dEJ).rawBSON).to(equal(cBData)) + expect(try Document(fromJSON: dEJ).toData()).to(equal(cBData)) } } @@ -212,13 +212,13 @@ final class BSONCorpusTests: MongoSwiftTestCase { } let description = "\(testFile.description)-\(test.description)" - switch BSONType(rawValue: UInt32(testFile.bsonType.dropFirst(2), radix: 16)!)! { + switch BSONType(rawValue: UInt8(testFile.bsonType.dropFirst(2), radix: 16)!)! { case .invalid: // "top level document" uses 0x00 for the bson type expect(try BSONDocument(fromJSON: test.string)) .to(throwError(), description: description) case .decimal128: - expect(BSONDecimal128(test.string)) - .to(beNil(), description: description) + expect(try BSONDecimal128(test.string)) + .to(throwError(), description: description) default: throw TestError( message: "\(description): parse error tests not implemented" diff --git a/Tests/BSONTests/BSONValueTests.swift b/Tests/BSONTests/BSONValueTests.swift index d9ac00742..c628be0f5 100644 --- a/Tests/BSONTests/BSONValueTests.swift +++ b/Tests/BSONTests/BSONValueTests.swift @@ -7,9 +7,9 @@ import XCTest final class BSONValueTests: MongoSwiftTestCase { func testInvalidDecimal128() throws { - expect(BSONDecimal128("hi")).to(beNil()) - expect(BSONDecimal128("123.4.5")).to(beNil()) - expect(BSONDecimal128("10")).toNot(beNil()) + expect(try BSONDecimal128("hi")).to(throwError()) + expect(try BSONDecimal128("123.4.5")).to(throwError()) + expect(try BSONDecimal128("10")).toNot(throwError()) } func testUUIDBytes() throws { @@ -40,8 +40,8 @@ final class BSONValueTests: MongoSwiftTestCase { self.checkTrueAndFalse(val: 1.618, alternate: 2.718) // Decimal128 self.checkTrueAndFalse( - val: .decimal128(BSONDecimal128("1.618")!), - alternate: .decimal128(BSONDecimal128("2.718")!) + val: .decimal128(try BSONDecimal128("1.618")), + alternate: .decimal128(try BSONDecimal128("2.718")) ) // Bool self.checkTrueAndFalse(val: true, alternate: false) @@ -119,14 +119,10 @@ final class BSONValueTests: MongoSwiftTestCase { bson_oid_to_string(&oid_t, &oid_c) let oid = String(cString: &oid_c) - // read the timestamp used to create the oid - let timestamp = UInt32(bson_oid_get_time_t(&oid_t)) - // initialize a new oid with the oid_t ptr // expect the values to be equal let objectId = BSONObjectID(bsonOid: oid_t) expect(objectId.hex).to(equal(oid)) - expect(objectId.timestamp).to(equal(timestamp)) // round trip the objectId. // expect the encoded oid to equal the original @@ -140,14 +136,12 @@ final class BSONValueTests: MongoSwiftTestCase { expect(_id).to(equal(objectId)) expect(_id.hex).to(equal(objectId.hex)) - expect(_id.timestamp).to(equal(objectId.timestamp)) // expect that we can pull the correct timestamp if // initialized from the original string let objectIdFromString = BSONObjectID(oid)! expect(objectIdFromString).to(equal(objectId)) expect(objectIdFromString.hex).to(equal(oid)) - expect(objectIdFromString.timestamp).to(equal(timestamp)) } func testObjectIDJSONCodable() throws { @@ -212,24 +206,24 @@ final class BSONValueTests: MongoSwiftTestCase { } func testBSONNumber() throws { - let decimal128 = BSONDecimal128("5.5")! + let decimal128 = try BSONDecimal128("5.5") let double: BSON = 5.5 expect(double.toDouble()).to(equal(5.5)) expect(double.toDecimal128()).to(equal(decimal128)) let cases = [ - BSONNumberTestCase(int: 5, double: 5.0, int32: Int32(5), int64: Int64(5), decimal: BSONDecimal128("5")!), + BSONNumberTestCase(int: 5, double: 5.0, int32: Int32(5), int64: Int64(5), decimal: try BSONDecimal128("5")), BSONNumberTestCase( int: -5, double: -5.0, int32: Int32(-5), int64: Int64(-5), - decimal: BSONDecimal128("-5")! + decimal: try BSONDecimal128("-5") ), - BSONNumberTestCase(int: 0, double: 0.0, int32: Int32(0), int64: Int64(0), decimal: BSONDecimal128("0")!), - BSONNumberTestCase(int: nil, double: 1.234, int32: nil, int64: nil, decimal: BSONDecimal128("1.234")!), - BSONNumberTestCase(int: nil, double: -31.234, int32: nil, int64: nil, decimal: BSONDecimal128("-31.234")!) + BSONNumberTestCase(int: 0, double: 0.0, int32: Int32(0), int64: Int64(0), decimal: try BSONDecimal128("0")), + BSONNumberTestCase(int: nil, double: 1.234, int32: nil, int64: nil, decimal: try BSONDecimal128("1.234")), + BSONNumberTestCase(int: nil, double: -31.234, int32: nil, int64: nil, decimal: try BSONDecimal128("-31.234")) ] cases.forEach { $0.run() } diff --git a/Tests/BSONTests/CodecTests.swift b/Tests/BSONTests/CodecTests.swift index fc8699227..92657d3b6 100644 --- a/Tests/BSONTests/CodecTests.swift +++ b/Tests/BSONTests/CodecTests.swift @@ -305,8 +305,8 @@ final class CodecTests: MongoSwiftTestCase { ts: BSONTimestamp(timestamp: 1, inc: 2), int32: 5, int64: 6, - dec: BSONDecimal128("1.2E+10")!, - minkey: BSONMinKey(), + dec: try BSONDecimal128("1.2E+10"), + minkey: MinKey(), maxkey: MaxKey(), regex: BSONRegularExpression(pattern: "^abc", options: "imx"), symbol: BSONSymbol("i am a symbol"), @@ -412,7 +412,7 @@ final class CodecTests: MongoSwiftTestCase { expect(try decoder.decode( BSONDecimal128.self, from: "{\"$numberDecimal\": \"1.2E+10\"}" - )).to(equal(BSONDecimal128("1.2E+10")!)) + )).to(equal(try BSONDecimal128("1.2E+10"))) let binary = try BSONBinary(base64: "//8=", subtype: .generic) expect( @@ -700,7 +700,7 @@ final class CodecTests: MongoSwiftTestCase { expect(try decoder.decode(AnyBSONStruct.self, from: wrappedInt64.canonicalExtendedJSON).x).to(equal(int64)) // decimal128 - let decimal = BSON.decimal128(BSONDecimal128("1.2E+10")!) + let decimal = BSON.decimal128(try BSONDecimal128("1.2E+10")) expect(try decoder.decode(BSON.self, from: "{ \"$numberDecimal\" : \"1.2E+10\" }")).to(equal(decimal)) diff --git a/Tests/BSONTests/Document+SequenceTests.swift b/Tests/BSONTests/Document+SequenceTests.swift index 15dbb0eaf..138154647 100644 --- a/Tests/BSONTests/Document+SequenceTests.swift +++ b/Tests/BSONTests/Document+SequenceTests.swift @@ -14,7 +14,7 @@ final class Document_SequenceTests: MongoSwiftTestCase { "int32": .int32(5), "int64": .int64(123), "double": .double(15), - "decimal128": .decimal128(BSONDecimal128("1.2E+10")!), + "decimal128": .decimal128(try BSONDecimal128("1.2E+10")), "minkey": .minKey, "maxkey": .maxKey, "date": .datetime(Date(timeIntervalSince1970: 5000)), @@ -54,7 +54,7 @@ final class Document_SequenceTests: MongoSwiftTestCase { let decimalTup = iter.next()! expect(decimalTup.key).to(equal("decimal128")) - expect(decimalTup.value).to(equal(.decimal128(BSONDecimal128("1.2E+10")!))) + expect(decimalTup.value).to(equal(.decimal128(try BSONDecimal128("1.2E+10")))) let minTup = iter.next()! expect(minTup.key).to(equal("minkey")) @@ -81,8 +81,8 @@ final class Document_SequenceTests: MongoSwiftTestCase { ] var expectedValues: [BSON] = [ "test string", true, false, 25, .int32(5), .int64(123), .double(15), - .decimal128(BSONDecimal128("1.2E+10")!), .minKey, .maxKey, .datetime(Date(timeIntervalSince1970: 5000)), - .timestamp(BSONTimestamp(timestamp: 5, inc: 10)) + .decimal128(try BSONDecimal128("1.2E+10")), .minKey, .maxKey, .datetime(Date(timeIntervalSince1970: 5000)), + .timestamp(Timestamp(timestamp: 5, inc: 10)) ] for (k, v) in doc { expect(k).to(equal(expectedKeys.removeFirst())) diff --git a/Tests/BSONTests/DocumentTests.swift b/Tests/BSONTests/DocumentTests.swift index a743135a6..adb144a00 100644 --- a/Tests/BSONTests/DocumentTests.swift +++ b/Tests/BSONTests/DocumentTests.swift @@ -81,7 +81,7 @@ final class DocumentTests: MongoSwiftTestCase { "int32": .int32(5), "int64": .int64(10), "double": .double(15), - "decimal128": .decimal128(BSONDecimal128("1.2E+10")!), + "decimal128": .decimal128(try! BSONDecimal128("1.2E+10")), "minkey": .minKey, "maxkey": .maxKey, "date": .datetime(Date(timeIntervalSince1970: 500.004)), @@ -121,8 +121,8 @@ final class DocumentTests: MongoSwiftTestCase { "binary3": .binary(try BSONBinary(data: uuidData, subtype: .uuidDeprecated)), "binary4": .binary(try BSONBinary(data: uuidData, subtype: .uuid)), "binary5": .binary(try BSONBinary(data: testData, subtype: .md5)), - "binary6": .binary(try BSONBinary(data: testData, subtype: .userDefined)), - "binary7": .binary(try BSONBinary(data: testData, subtype: 200)) + "binary6": .binary(try BSONBinary(data: testData, subtype: .userDefined(0x80))), + "binary7": .binary(try BSONBinary(data: testData, subtype: .userDefined(0xFF))) ] try doc.merge(binaryData) @@ -148,7 +148,7 @@ final class DocumentTests: MongoSwiftTestCase { expect(doc["int32"]).to(equal(.int32(5))) expect(doc["int64"]).to(equal(.int64(10))) expect(doc["double"]).to(equal(15.0)) - expect(doc["decimal128"]).to(equal(.decimal128(BSONDecimal128("1.2E+10")!))) + expect(doc["decimal128"]).to(equal(.decimal128(try BSONDecimal128("1.2E+10")))) expect(doc["minkey"]).to(equal(.minKey)) expect(doc["maxkey"]).to(equal(.maxKey)) expect(doc["date"]).to(equal(.datetime(Date(timeIntervalSince1970: 500.004)))) @@ -179,8 +179,8 @@ final class DocumentTests: MongoSwiftTestCase { expect(doc["binary3"]).to(equal(.binary(try BSONBinary(data: uuidData, subtype: .uuidDeprecated)))) expect(doc["binary4"]).to(equal(.binary(try BSONBinary(data: uuidData, subtype: .uuid)))) expect(doc["binary5"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .md5)))) - expect(doc["binary6"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .userDefined)))) - expect(doc["binary7"]).to(equal(.binary(try BSONBinary(data: testData, subtype: 200)))) + expect(doc["binary6"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .other(0x80))))) + expect(doc["binary7"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .other(0xFF))))) let nestedArray = doc["nestedarray"]?.arrayValue?.compactMap { $0.arrayValue?.compactMap { $0.toInt() } } expect(nestedArray?[0]).to(equal([1, 2])) @@ -198,7 +198,7 @@ final class DocumentTests: MongoSwiftTestCase { expect(DocumentTests.testDoc.int32).to(equal(.int32(5))) expect(DocumentTests.testDoc.int64).to(equal(.int64(10))) expect(DocumentTests.testDoc.double).to(equal(15.0)) - expect(DocumentTests.testDoc.decimal128).to(equal(.decimal128(BSONDecimal128("1.2E+10")!))) + expect(DocumentTests.testDoc.decimal128).to(equal(.decimal128(try BSONDecimal128("1.2E+10")))) expect(DocumentTests.testDoc.minkey).to(equal(.minKey)) expect(DocumentTests.testDoc.maxkey).to(equal(.maxKey)) expect(DocumentTests.testDoc.date).to(equal(.datetime(Date(timeIntervalSince1970: 500.004)))) @@ -258,7 +258,7 @@ final class DocumentTests: MongoSwiftTestCase { func testRawBSON() throws { let doc = try BSONDocument(fromJSON: "{\"a\" : [{\"$numberInt\": \"10\"}]}") - let fromRawBSON = try BSONDocument(fromBSON: doc.rawBSON) + let fromRawBSON = try BSONDocument(fromBSON: doc.toData()) expect(doc).to(equal(fromRawBSON)) } @@ -351,8 +351,8 @@ final class DocumentTests: MongoSwiftTestCase { "int32": .int32(32), "int64": .int64(Int64.max), "bool": false, - "decimal": .decimal128(BSONDecimal128("1.2E+10")!), - "oid": .objectID(), + "decimal": .decimal128(try BSONDecimal128("1.2E+10")), + "oid": .objectID(BSONObjectID()), "timestamp": .timestamp(BSONTimestamp(timestamp: 1, inc: 2)), "datetime": .datetime(Date(msSinceEpoch: 1000)) ] @@ -387,7 +387,7 @@ final class DocumentTests: MongoSwiftTestCase { doc["double"] = 3.0 expect(doc.pointerAddress).to(equal(pointer)) - doc["decimal"] = .decimal128(BSONDecimal128("100")!) + doc["decimal"] = .decimal128(try BSONDecimal128("100")) expect(doc.pointerAddress).to(equal(pointer)) // overwrite int64 with int64 @@ -409,7 +409,7 @@ final class DocumentTests: MongoSwiftTestCase { "int32": .int32(15), "int64": .int64(Int64.min), "bool": true, - "decimal": .decimal128(BSONDecimal128("100")!), + "decimal": .decimal128(try BSONDecimal128("100")), "oid": .objectID(newOid), "timestamp": .timestamp(BSONTimestamp(timestamp: 5, inc: 10)), "datetime": .datetime(Date(msSinceEpoch: 2000)) @@ -430,7 +430,7 @@ final class DocumentTests: MongoSwiftTestCase { "int32": .int32(15), "int64": BSON(integerLiteral: bigInt), "bool": true, - "decimal": .decimal128(BSONDecimal128("100")!), + "decimal": .decimal128(try BSONDecimal128("100")), "oid": .objectID(newOid), "timestamp": .timestamp(BSONTimestamp(timestamp: 5, inc: 10)), "datetime": .datetime(Date(msSinceEpoch: 2000)) @@ -481,7 +481,7 @@ final class DocumentTests: MongoSwiftTestCase { let overwritablePairs: [(String, BSON)] = [ ("double", BSON(10)), ("int32", "hi"), - ("int64", .decimal128(BSONDecimal128("1.0")!)), + ("int64", .decimal128(try BSONDecimal128("1.0"))), ("bool", [1, 2, 3]), ("decimal", 100), ("oid", 25.5), @@ -498,7 +498,7 @@ final class DocumentTests: MongoSwiftTestCase { expect(overwritableDoc).to(equal([ "double": BSON(10), "int32": "hi", - "int64": .decimal128(BSONDecimal128("1.0")!), + "int64": .decimal128(try BSONDecimal128("1.0")), "bool": [1, 2, 3], "decimal": 100, "oid": 25.5, From bcc0e73021a4f36b1b615e3f5cd2ba5f1d9cf551 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 21 May 2020 11:04:08 -0400 Subject: [PATCH 02/11] fix: comments --- Sources/MongoSwift/BSON/BSONValue.swift | 10 ++++++++-- Tests/BSONTests/BSONValueTests.swift | 6 ++++++ Tests/BSONTests/DocumentTests.swift | 8 ++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 5c79a78ec..89135fe8f 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -222,6 +222,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// Encrypted BSON value public static let encryptedValue = Subtype(0x06) + /// Subtype indicator value public let rawValue: UInt8 private init(_ value: UInt8) { self.rawValue = value } @@ -229,7 +230,12 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// Create BSON Binary Subtype /// Note: subtype 0x80-0xFF "User defined" subtype - public static func other(_ value: Int) -> Subtype { Subtype(UInt8(value)) } + public static func userDefined(_ value: Int) throws -> Subtype { + guard value >= 0x80 && value <= 0xFF else { + throw InvalidArgumentError(message: "User defined Binary Subtypes must be between 0x80 and 0xFF") + } + return Subtype(UInt8(value)) + } } /// Initializes a `BSONBinary` instance from a `UUID`. @@ -836,7 +842,7 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/objectid.rst public init(_ hex: String) throws { guard bson_oid_is_valid(hex, hex.utf8.count) else { - throw InternalError(message: "Cannot create ObjectId from \(hex)") + throw InvalidArgumentError(message: "Cannot create ObjectId from \(hex)") } var oid_t = bson_oid_t() bson_oid_init_from_string(&oid_t, hex) diff --git a/Tests/BSONTests/BSONValueTests.swift b/Tests/BSONTests/BSONValueTests.swift index c628be0f5..217a3fbd3 100644 --- a/Tests/BSONTests/BSONValueTests.swift +++ b/Tests/BSONTests/BSONValueTests.swift @@ -228,4 +228,10 @@ final class BSONValueTests: MongoSwiftTestCase { cases.forEach { $0.run() } } + + func testBSONDinarySubtype() { + // Check the subtype bounds are kept + expect(try Binary.Subtype.userDefined(0x100)).to(throwError()) + expect(try Binary.Subtype.userDefined(0x79)).to(throwError()) + } } diff --git a/Tests/BSONTests/DocumentTests.swift b/Tests/BSONTests/DocumentTests.swift index adb144a00..1ec04814e 100644 --- a/Tests/BSONTests/DocumentTests.swift +++ b/Tests/BSONTests/DocumentTests.swift @@ -121,8 +121,8 @@ final class DocumentTests: MongoSwiftTestCase { "binary3": .binary(try BSONBinary(data: uuidData, subtype: .uuidDeprecated)), "binary4": .binary(try BSONBinary(data: uuidData, subtype: .uuid)), "binary5": .binary(try BSONBinary(data: testData, subtype: .md5)), - "binary6": .binary(try BSONBinary(data: testData, subtype: .userDefined(0x80))), - "binary7": .binary(try BSONBinary(data: testData, subtype: .userDefined(0xFF))) + "binary6": .binary(try BSONBinary(data: testData, subtype: try .userDefined(0x80))), + "binary7": .binary(try BSONBinary(data: testData, subtype: try .userDefined(0xFF))) ] try doc.merge(binaryData) @@ -179,8 +179,8 @@ final class DocumentTests: MongoSwiftTestCase { expect(doc["binary3"]).to(equal(.binary(try BSONBinary(data: uuidData, subtype: .uuidDeprecated)))) expect(doc["binary4"]).to(equal(.binary(try BSONBinary(data: uuidData, subtype: .uuid)))) expect(doc["binary5"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .md5)))) - expect(doc["binary6"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .other(0x80))))) - expect(doc["binary7"]).to(equal(.binary(try BSONBinary(data: testData, subtype: .other(0xFF))))) + expect(doc["binary6"]).to(equal(.binary(try BSONBinary(data: testData, subtype: try .userDefined(0x80))))) + expect(doc["binary7"]).to(equal(.binary(try BSONBinary(data: testData, subtype: try .userDefined(0xFF))))) let nestedArray = doc["nestedarray"]?.arrayValue?.compactMap { $0.arrayValue?.compactMap { $0.toInt() } } expect(nestedArray?[0]).to(equal([1, 2])) From de22f583df7586382d6d09bce03fd2a12eb6e17c Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 21 May 2020 11:06:42 -0400 Subject: [PATCH 03/11] fix: docs --- Sources/MongoSwift/BSON/BSONValue.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 89135fe8f..7c30766f0 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -228,8 +228,9 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { private init(_ value: UInt8) { self.rawValue = value } internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) } - /// Create BSON Binary Subtype - /// Note: subtype 0x80-0xFF "User defined" subtype + /// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF. + /// - Throws: + /// - `InvalidArgumentError` if value passed is outside of the range 0x80-0xFF public static func userDefined(_ value: Int) throws -> Subtype { guard value >= 0x80 && value <= 0xFF else { throw InvalidArgumentError(message: "User defined Binary Subtypes must be between 0x80 and 0xFF") From b45b1e3fa677681c8f78c9a9cb7a3af62648f30f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 26 May 2020 10:42:37 -0400 Subject: [PATCH 04/11] fix: comments --- Sources/MongoSwift/BSON/BSONValue.swift | 28 +++++++++++++++++-------- Tests/BSONTests/BSONValueTests.swift | 2 +- Tests/LinuxMain.swift | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 7c30766f0..ea1fcdb3b 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -206,7 +206,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public let subtype: Subtype /// Subtypes for BSON Binary values. - public struct Subtype: Equatable, Codable, Hashable { + public struct Subtype: Equatable, Codable, Hashable, RawRepresentable { /// Generic binary subtype public static let generic = Subtype(0x00) /// A function @@ -223,9 +223,10 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public static let encryptedValue = Subtype(0x06) /// Subtype indicator value - public let rawValue: UInt8 + public var rawValue: UInt8 - private init(_ value: UInt8) { self.rawValue = value } + public init(_ value: UInt8) { self.rawValue = value } + public init?(rawValue: UInt8) { self.rawValue = rawValue } internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) } /// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF. @@ -503,9 +504,17 @@ public struct BSONDecimal128: BSONValue, Equatable, Codable, CustomStringConvert self.decimal128 = bsonDecimal } - /// Initializes a `BSONDecimal128` value from the provided `String`. Returns `nil` if the input is not a valid - /// Decimal128 string. - /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst + /** + * Initializes a `Decimal128` value from the provided `String`. + * + * - Parameters: + * - a Decimal128 number as a string. + * + * - Throws: + * - A `InvalidArgumentError` if the string does not represent a Decimal128 encodable value. + * + * - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst + */ public init(_ data: String) throws { let bsonType = try BSONDecimal128.toLibBSONType(data) self.init(bsonDecimal: bsonType) @@ -838,8 +847,9 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab self.oid = oid } - /// Initializes an `BSONObjectID` from the provided hex `String`. Returns `nil` if the string is not a valid - /// ObjectID. + /// Initializes an `ObjectID` from the provided hex `String`. + /// - Throws: + /// - `InvalidArgumentError` if string passed is not a valid ObjectID /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/objectid.rst public init(_ hex: String) throws { guard bson_oid_is_valid(hex, hex.utf8.count) else { @@ -858,7 +868,7 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab // assumes that the BSONObjectID is stored as a valid hex string. let container = try decoder.singleValueContainer() let hex = try container.decode(String.self) - guard let oid = BSONObjectID(hex) else { + guard let oid = try? ObjectID(hex) else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, diff --git a/Tests/BSONTests/BSONValueTests.swift b/Tests/BSONTests/BSONValueTests.swift index 217a3fbd3..64a9b21a8 100644 --- a/Tests/BSONTests/BSONValueTests.swift +++ b/Tests/BSONTests/BSONValueTests.swift @@ -229,7 +229,7 @@ final class BSONValueTests: MongoSwiftTestCase { cases.forEach { $0.run() } } - func testBSONDinarySubtype() { + func testBSONBinarySubtype() { // Check the subtype bounds are kept expect(try Binary.Subtype.userDefined(0x100)).to(throwError()) expect(try Binary.Subtype.userDefined(0x79)).to(throwError()) diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 0c534466c..fb9d3bb9b 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -39,6 +39,7 @@ extension BSONValueTests { ("testObjectIDRoundTrip", testObjectIDRoundTrip), ("testObjectIDJSONCodable", testObjectIDJSONCodable), ("testBSONNumber", testBSONNumber), + ("testBSONBinarySubtype", testBSONBinarySubtype), ] } From 52653b0172510b838137fea9b78da190f95ca303 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 26 May 2020 17:01:52 -0400 Subject: [PATCH 05/11] fix: tests --- Sources/MongoSwift/BSON/BSONValue.swift | 27 ++++++++------------ Tests/BSONTests/BSONValueTests.swift | 6 ++--- Tests/BSONTests/CodecTests.swift | 6 ++--- Tests/BSONTests/Document+SequenceTests.swift | 2 +- Tests/BSONTests/DocumentTests.swift | 8 +++--- 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index ea1fcdb3b..8b8416506 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -206,7 +206,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public let subtype: Subtype /// Subtypes for BSON Binary values. - public struct Subtype: Equatable, Codable, Hashable, RawRepresentable { + public struct Subtype: Equatable, Codable, Hashable { /// Generic binary subtype public static let generic = Subtype(0x00) /// A function @@ -223,11 +223,10 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public static let encryptedValue = Subtype(0x06) /// Subtype indicator value - public var rawValue: UInt8 + public var value: UInt8 - public init(_ value: UInt8) { self.rawValue = value } - public init?(rawValue: UInt8) { self.rawValue = rawValue } - internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) } + private init(_ value: UInt8) { self.value = value } + internal init(_ value: bson_subtype_t) { self.value = UInt8(value.rawValue) } /// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF. /// - Throws: @@ -267,13 +266,6 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { ) } self.subtype = subtype - self.data = data - } - - /// Initializes a `BSONBinary` instance from a `Data` object and a `Subtype`. - /// - Throws: - /// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype. - public init(data: Data, subtype: Subtype) throws { var buffer = BSON_ALLOCATOR.buffer(capacity: data.count) buffer.writeBytes(data) self.data = buffer @@ -302,7 +294,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { } internal func encode(to document: inout Document, forKey key: String) throws { - let subtype = bson_subtype_t(UInt32(self.subtype.rawValue)) + let subtype = bson_subtype_t(UInt32(self.subtype.value)) let length = self.data.writerIndex guard let byteArray = self.data.getBytes(at: 0, length: length) else { throw InternalError(message: "Cannot read \(length) bytes from Binary.data") @@ -343,13 +335,16 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// - Throws: /// - `InvalidArgumentError` if a non-UUID subtype is set on this `BSONBinary`. public func toUUID() throws -> UUID { - guard [Subtype.uuid.rawValue, Subtype.uuidDeprecated.rawValue].contains(self.subtype) else { + guard [Subtype.uuid, Subtype.uuidDeprecated].contains(self.subtype) else { throw InvalidArgumentError( message: "Expected a UUID binary subtype, got subtype \(self.subtype) instead." ) } - let data = self.data + guard let data = self.data.getBytes(at: 0, length: 16) else { + throw InternalError(message: "Unable to read 16 bytes from Binary.data") + } + let uuid: uuid_t = ( data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], @@ -868,7 +863,7 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab // assumes that the BSONObjectID is stored as a valid hex string. let container = try decoder.singleValueContainer() let hex = try container.decode(String.self) - guard let oid = try? ObjectID(hex) else { + guard let oid = try? BSONObjectID(hex) else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, diff --git a/Tests/BSONTests/BSONValueTests.swift b/Tests/BSONTests/BSONValueTests.swift index 64a9b21a8..44458810b 100644 --- a/Tests/BSONTests/BSONValueTests.swift +++ b/Tests/BSONTests/BSONValueTests.swift @@ -139,7 +139,7 @@ final class BSONValueTests: MongoSwiftTestCase { // expect that we can pull the correct timestamp if // initialized from the original string - let objectIdFromString = BSONObjectID(oid)! + let objectIdFromString = try BSONObjectID(oid) expect(objectIdFromString).to(equal(objectId)) expect(objectIdFromString.hex).to(equal(oid)) } @@ -231,7 +231,7 @@ final class BSONValueTests: MongoSwiftTestCase { func testBSONBinarySubtype() { // Check the subtype bounds are kept - expect(try Binary.Subtype.userDefined(0x100)).to(throwError()) - expect(try Binary.Subtype.userDefined(0x79)).to(throwError()) + expect(try BSONBinary.Subtype.userDefined(0x100)).to(throwError()) + expect(try BSONBinary.Subtype.userDefined(0x79)).to(throwError()) } } diff --git a/Tests/BSONTests/CodecTests.swift b/Tests/BSONTests/CodecTests.swift index 92657d3b6..0b820adfb 100644 --- a/Tests/BSONTests/CodecTests.swift +++ b/Tests/BSONTests/CodecTests.swift @@ -297,7 +297,7 @@ final class CodecTests: MongoSwiftTestCase { doc: ["x": 1], arr: [.int32(1), .int32(2)], binary: try BSONBinary(base64: "//8=", subtype: .generic), - oid: BSONObjectID("507f1f77bcf86cd799439011")!, + oid: try BSONObjectID("507f1f77bcf86cd799439011"), bool: true, date: Date(timeIntervalSinceReferenceDate: 5000), code: BSONCode(code: "hi"), @@ -311,7 +311,7 @@ final class CodecTests: MongoSwiftTestCase { regex: BSONRegularExpression(pattern: "^abc", options: "imx"), symbol: BSONSymbol("i am a symbol"), undefined: BSONUndefined(), - dbpointer: BSONDBPointer(ref: "some.namespace", id: BSONObjectID("507f1f77bcf86cd799439011")!), + dbpointer: DBPointer(ref: "some.namespace", id: try BSONObjectID("507f1f77bcf86cd799439011")), null: BSONNull() ) } @@ -398,7 +398,7 @@ final class CodecTests: MongoSwiftTestCase { expect(try decoder.decode(Int32.self, from: "42")).to(equal(Int32(42))) expect(try decoder.decode(Int32.self, from: "{\"$numberInt\": \"42\"}")).to(equal(Int32(42))) - let oid = BSONObjectID("507f1f77bcf86cd799439011")! + let oid = try BSONObjectID("507f1f77bcf86cd799439011") expect(try decoder.decode(BSONObjectID.self, from: "{\"$oid\": \"507f1f77bcf86cd799439011\"}")).to(equal(oid)) expect(try decoder.decode(String.self, from: "\"somestring\"")).to(equal("somestring")) diff --git a/Tests/BSONTests/Document+SequenceTests.swift b/Tests/BSONTests/Document+SequenceTests.swift index 138154647..f5f32f7d0 100644 --- a/Tests/BSONTests/Document+SequenceTests.swift +++ b/Tests/BSONTests/Document+SequenceTests.swift @@ -82,7 +82,7 @@ final class Document_SequenceTests: MongoSwiftTestCase { var expectedValues: [BSON] = [ "test string", true, false, 25, .int32(5), .int64(123), .double(15), .decimal128(try BSONDecimal128("1.2E+10")), .minKey, .maxKey, .datetime(Date(timeIntervalSince1970: 5000)), - .timestamp(Timestamp(timestamp: 5, inc: 10)) + .timestamp(BSONTimestamp(timestamp: 5, inc: 10)) ] for (k, v) in doc { expect(k).to(equal(expectedKeys.removeFirst())) diff --git a/Tests/BSONTests/DocumentTests.swift b/Tests/BSONTests/DocumentTests.swift index 1ec04814e..044bdec55 100644 --- a/Tests/BSONTests/DocumentTests.swift +++ b/Tests/BSONTests/DocumentTests.swift @@ -88,7 +88,7 @@ final class DocumentTests: MongoSwiftTestCase { "timestamp": .timestamp(BSONTimestamp(timestamp: 5, inc: 10)), "nestedarray": [[1, 2], [.int32(3), .int32(4)]], "nesteddoc": ["a": 1, "b": 2, "c": false, "d": [3, 4]], - "oid": .objectID(BSONObjectID("507f1f77bcf86cd799439011")!), + "oid": .objectID(try! BSONObjectID("507f1f77bcf86cd799439011")), "regex": .regex(BSONRegularExpression(pattern: "^abc", options: "imx")), "array1": [1, 2], "array2": ["string1", "string2"], @@ -153,7 +153,7 @@ final class DocumentTests: MongoSwiftTestCase { expect(doc["maxkey"]).to(equal(.maxKey)) expect(doc["date"]).to(equal(.datetime(Date(timeIntervalSince1970: 500.004)))) expect(doc["timestamp"]).to(equal(.timestamp(BSONTimestamp(timestamp: 5, inc: 10)))) - expect(doc["oid"]).to(equal(.objectID(BSONObjectID("507f1f77bcf86cd799439011")!))) + expect(doc["oid"]).to(equal(.objectID(try BSONObjectID("507f1f77bcf86cd799439011")))) let regex = doc["regex"]?.regexValue expect(regex).to(equal(BSONRegularExpression(pattern: "^abc", options: "imx"))) @@ -203,7 +203,7 @@ final class DocumentTests: MongoSwiftTestCase { expect(DocumentTests.testDoc.maxkey).to(equal(.maxKey)) expect(DocumentTests.testDoc.date).to(equal(.datetime(Date(timeIntervalSince1970: 500.004)))) expect(DocumentTests.testDoc.timestamp).to(equal(.timestamp(BSONTimestamp(timestamp: 5, inc: 10)))) - expect(DocumentTests.testDoc.oid).to(equal(.objectID(BSONObjectID("507f1f77bcf86cd799439011")!))) + expect(DocumentTests.testDoc.oid).to(equal(.objectID(try BSONObjectID("507f1f77bcf86cd799439011")))) let codewscope = DocumentTests.testDoc.codewscope?.codeWithScopeValue expect(codewscope?.code).to(equal("console.log(x);")) @@ -351,7 +351,7 @@ final class DocumentTests: MongoSwiftTestCase { "int32": .int32(32), "int64": .int64(Int64.max), "bool": false, - "decimal": .decimal128(try BSONDecimal128("1.2E+10")), + "decimal": .decimal128(try! BSONDecimal128("1.2E+10")), "oid": .objectID(BSONObjectID()), "timestamp": .timestamp(BSONTimestamp(timestamp: 1, inc: 2)), "datetime": .datetime(Date(msSinceEpoch: 1000)) From 5e9964636115f890b4069cb465af99c92cfbd8e2 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 27 May 2020 12:38:42 -0400 Subject: [PATCH 06/11] fix: let instead of var --- Sources/MongoSwift/BSON/BSONValue.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 8b8416506..0776a4bee 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -223,7 +223,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public static let encryptedValue = Subtype(0x06) /// Subtype indicator value - public var value: UInt8 + public let value: UInt8 private init(_ value: UInt8) { self.value = value } internal init(_ value: bson_subtype_t) { self.value = UInt8(value.rawValue) } From 3b08cd1ed38c58cdcc252b1679016f9867a9d21a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 27 May 2020 13:52:57 -0400 Subject: [PATCH 07/11] fix: rebase issues --- Sources/MongoSwift/BSON/BSON.swift | 4 +- .../BSON/BSONDocumentIterator.swift | 2 +- Sources/MongoSwift/BSON/BSONValue.swift | 48 ++++--------------- Sources/MongoSwift/BSON/Overwritable.swift | 2 +- Tests/BSONTests/BSONCorpusTests.swift | 8 ++-- Tests/BSONTests/BSONValueTests.swift | 38 ++++++++++++--- Tests/BSONTests/CodecTests.swift | 10 ++-- Tests/BSONTests/Document+SequenceTests.swift | 2 +- 8 files changed, 55 insertions(+), 59 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSON.swift b/Sources/MongoSwift/BSON/BSON.swift index ea07e9a66..f7e222be7 100644 --- a/Sources/MongoSwift/BSON/BSON.swift +++ b/Sources/MongoSwift/BSON/BSON.swift @@ -306,7 +306,7 @@ extension BSON { BSONNull.self, BSONUndefined.self, BSONMinKey.self, - MaxKey.self, + BSONMaxKey.self, BSONSymbol.self, Double.self, String.self, @@ -336,7 +336,7 @@ extension BSON { case .minKey: return BSONMinKey() case .maxKey: - return MaxKey() + return BSONMaxKey() case let .symbol(v): return v case let .double(v): diff --git a/Sources/MongoSwift/BSON/BSONDocumentIterator.swift b/Sources/MongoSwift/BSON/BSONDocumentIterator.swift index bc1287669..76e887df8 100644 --- a/Sources/MongoSwift/BSON/BSONDocumentIterator.swift +++ b/Sources/MongoSwift/BSON/BSONDocumentIterator.swift @@ -219,7 +219,7 @@ public class BSONDocumentIterator: IteratorProtocol { .int64: Int64.self, .decimal128: BSONDecimal128.self, .minKey: BSONMinKey.self, - .maxKey: MaxKey.self, + .maxKey: BSONMaxKey.self, .null: BSONNull.self, .undefined: BSONUndefined.self ] diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 0776a4bee..4118296db 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -271,7 +271,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { self.data = buffer } - /// Initializes a `Binary` instance from a base64 `String` and a `Subtype`. + /// Initializes a `BSONBinary` instance from a base64 `String` and a `Subtype`. /// - Throws: /// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is /// incompatible with the specified subtype. @@ -293,7 +293,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { throw bsonEncodingUnsupportedError(value: self, at: to.codingPath) } - internal func encode(to document: inout Document, forKey key: String) throws { + internal func encode(to document: inout BSONDocument, forKey key: String) throws { let subtype = bson_subtype_t(UInt32(self.subtype.value)) let length = self.data.writerIndex guard let byteArray = self.data.getBytes(at: 0, length: length) else { @@ -500,13 +500,13 @@ public struct BSONDecimal128: BSONValue, Equatable, Codable, CustomStringConvert } /** - * Initializes a `Decimal128` value from the provided `String`. + * Initializes a `BSONDecimal128` value from the provided `String`. * * - Parameters: - * - a Decimal128 number as a string. + * - a BSONDecimal128 number as a string. * * - Throws: - * - A `InvalidArgumentError` if the string does not represent a Decimal128 encodable value. + * - A `InvalidArgumentError` if the string does not represent a BSONDecimal128 encodable value. * * - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst */ @@ -747,7 +747,7 @@ public struct BSONCode: BSONValue, Equatable, Codable, Hashable { } /// A struct to represent the BSON MaxKey type. -internal struct MaxKey: BSONValue, Equatable, Codable, Hashable { +internal struct BSONMaxKey: BSONValue, Equatable, Codable, Hashable { internal var bson: BSON { .maxKey } internal static var bsonType: BSONType { .maxKey } @@ -764,7 +764,7 @@ internal struct MaxKey: BSONValue, Equatable, Codable, Hashable { internal init() {} internal init(from decoder: Decoder) throws { - throw getDecodingError(type: MaxKey.self, decoder: decoder) + throw getDecodingError(type: BSONMaxKey.self, decoder: decoder) } internal func encode(to: Encoder) throws { @@ -773,7 +773,7 @@ internal struct MaxKey: BSONValue, Equatable, Codable, Hashable { internal static func from(iterator iter: BSONDocumentIterator) throws -> BSON { guard iter.currentType == .maxKey else { - throw wrongIterTypeError(iter, expected: MaxKey.self) + throw wrongIterTypeError(iter, expected: BSONMaxKey.self) } return .maxKey } @@ -842,9 +842,9 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab self.oid = oid } - /// Initializes an `ObjectID` from the provided hex `String`. + /// Initializes an `BSONObjectID` from the provided hex `String`. /// - Throws: - /// - `InvalidArgumentError` if string passed is not a valid ObjectID + /// - `InvalidArgumentError` if string passed is not a valid BSONObjectID /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/objectid.rst public init(_ hex: String) throws { guard bson_oid_is_valid(hex, hex.utf8.count) else { @@ -919,34 +919,6 @@ extension BSONObjectID: Hashable { } } -/// Extension to allow a `UUID` to be initialized from a `Binary` `BSONValue`. -extension UUID { - /// Initializes a `UUID` instance from a `Binary` `BSONValue`. - /// - Throws: - /// - `InvalidArgumentError` if a non-UUID subtype is set on the `Binary`. - public init(from binary: BSONBinary) throws { - guard [BSONBinary.Subtype.uuid, BSONBinary.Subtype.uuidDeprecated].contains(binary.subtype) else { - throw InvalidArgumentError( - message: "Expected a UUID binary type " + - "(\(BSONBinary.Subtype.uuid)), got \(binary.subtype) instead." - ) - } - - guard let data = binary.data.getBytes(at: 0, length: 16) else { - throw InternalError(message: "Unable to read 16 bytes from Binary.data") - } - - let uuid: uuid_t = ( - data[0], data[1], data[2], data[3], - data[4], data[5], data[6], data[7], - data[8], data[9], data[10], data[11], - data[12], data[13], data[14], data[15] - ) - - self.init(uuid: uuid) - } -} - // A mapping of regex option characters to their equivalent `NSRegularExpression` option. // note that there is a BSON regexp option 'l' that `NSRegularExpression` // doesn't support. The flag will be dropped if BSON containing it is parsed, diff --git a/Sources/MongoSwift/BSON/Overwritable.swift b/Sources/MongoSwift/BSON/Overwritable.swift index 79f94f3a9..269744b16 100644 --- a/Sources/MongoSwift/BSON/Overwritable.swift +++ b/Sources/MongoSwift/BSON/Overwritable.swift @@ -8,7 +8,7 @@ internal protocol Overwritable: BSONValue { * * - Throws: * - `InternalError` if the `BSONValue` is an `Int` and cannot be written to BSON. - * - `LogicError` if the `BSONValue` is a `Decimal128` or `BSONObjectID` and is improperly formatted. + * - `LogicError` if the `BSONValue` is a `BSONDecimal128` or `BSONObjectID` and is improperly formatted. */ func writeToCurrentPosition(of iter: BSONDocumentIterator) throws } diff --git a/Tests/BSONTests/BSONCorpusTests.swift b/Tests/BSONTests/BSONCorpusTests.swift index 0722a3e23..dc328d392 100644 --- a/Tests/BSONTests/BSONCorpusTests.swift +++ b/Tests/BSONTests/BSONCorpusTests.swift @@ -137,7 +137,7 @@ final class BSONCorpusTests: MongoSwiftTestCase { // for cB input: // native_to_bson( bson_to_native(cB) ) = cB - let docFromCB = try Document(fromBSON: cBData) + let docFromCB = try BSONDocument(fromBSON: cBData) expect(docFromCB.toData()).to(equal(cBData)) // test round tripping through documents @@ -147,7 +147,7 @@ final class BSONCorpusTests: MongoSwiftTestCase { // -> bson_t. At the end, the new bson_t should be identical to the original one. If not, our bson_t // translation layer is lossy and/or buggy. let nativeFromDoc = docFromCB.toArray() - let docFromNative = Document(fromArray: nativeFromDoc) + let docFromNative = BSONDocument(fromArray: nativeFromDoc) expect(docFromNative.toData()).to(equal(cBData)) // native_to_canonical_extended_json( bson_to_native(cB) ) = cEJ @@ -165,7 +165,7 @@ final class BSONCorpusTests: MongoSwiftTestCase { // native_to_bson( json_to_native(cEJ) ) = cB (unless lossy) if !lossy { - expect(try Document(fromJSON: cEJData).toData()).to(equal(cBData)) + expect(try BSONDocument(fromJSON: cEJData).toData()).to(equal(cBData)) } // for dB input (if it exists): @@ -193,7 +193,7 @@ final class BSONCorpusTests: MongoSwiftTestCase { // native_to_bson( json_to_native(dEJ) ) = cB (unless lossy) if !lossy { - expect(try Document(fromJSON: dEJ).toData()).to(equal(cBData)) + expect(try BSONDocument(fromJSON: dEJ).toData()).to(equal(cBData)) } } diff --git a/Tests/BSONTests/BSONValueTests.swift b/Tests/BSONTests/BSONValueTests.swift index 44458810b..be0199655 100644 --- a/Tests/BSONTests/BSONValueTests.swift +++ b/Tests/BSONTests/BSONValueTests.swift @@ -38,7 +38,7 @@ final class BSONValueTests: MongoSwiftTestCase { self.checkTrueAndFalse(val: .int64(64), alternate: .int64(65)) // Double self.checkTrueAndFalse(val: 1.618, alternate: 2.718) - // Decimal128 + // BSONDecimal128 self.checkTrueAndFalse( val: .decimal128(try BSONDecimal128("1.618")), alternate: .decimal128(try BSONDecimal128("2.718")) @@ -186,7 +186,7 @@ final class BSONValueTests: MongoSwiftTestCase { ] candidates.compactMap { $0 }.forEach { l in - // Skip the Decimal128 conversions until they're implemented + // Skip the BSONDecimal128 conversions until they're implemented // TODO: don't skip these (SWIFT-367) guard l.decimal128Value == nil else { return @@ -197,7 +197,7 @@ final class BSONValueTests: MongoSwiftTestCase { BSONNumberTestCase.compare(computed: l.toInt64(), expected: self.int64) BSONNumberTestCase.compare(computed: l.toDouble(), expected: self.double) - // Skip double for this conversion since it generates a Decimal128(5.0) =/= Decimal128(5) + // Skip double for this conversion since it generates a BSONDecimal128(5.0) =/= BSONDecimal128(5) if l.doubleValue == nil { BSONNumberTestCase.compare(computed: l.toDecimal128(), expected: self.decimal) } @@ -213,7 +213,13 @@ final class BSONValueTests: MongoSwiftTestCase { expect(double.toDecimal128()).to(equal(decimal128)) let cases = [ - BSONNumberTestCase(int: 5, double: 5.0, int32: Int32(5), int64: Int64(5), decimal: try BSONDecimal128("5")), + BSONNumberTestCase( + int: 5, + double: 5.0, + int32: Int32(5), + int64: Int64(5), + decimal: try BSONDecimal128("5") + ), BSONNumberTestCase( int: -5, double: -5.0, @@ -221,9 +227,27 @@ final class BSONValueTests: MongoSwiftTestCase { int64: Int64(-5), decimal: try BSONDecimal128("-5") ), - BSONNumberTestCase(int: 0, double: 0.0, int32: Int32(0), int64: Int64(0), decimal: try BSONDecimal128("0")), - BSONNumberTestCase(int: nil, double: 1.234, int32: nil, int64: nil, decimal: try BSONDecimal128("1.234")), - BSONNumberTestCase(int: nil, double: -31.234, int32: nil, int64: nil, decimal: try BSONDecimal128("-31.234")) + BSONNumberTestCase( + int: 0, + double: 0.0, + int32: Int32(0), + int64: Int64(0), + decimal: try BSONDecimal128("0") + ), + BSONNumberTestCase( + int: nil, + double: 1.234, + int32: nil, + int64: nil, + decimal: try BSONDecimal128("1.234") + ), + BSONNumberTestCase( + int: nil, + double: -31.234, + int32: nil, + int64: nil, + decimal: try BSONDecimal128("-31.234") + ) ] cases.forEach { $0.run() } diff --git a/Tests/BSONTests/CodecTests.swift b/Tests/BSONTests/CodecTests.swift index 0b820adfb..c40be6310 100644 --- a/Tests/BSONTests/CodecTests.swift +++ b/Tests/BSONTests/CodecTests.swift @@ -283,7 +283,7 @@ final class CodecTests: MongoSwiftTestCase { let int64: Int64 let dec: BSONDecimal128 let minkey: BSONMinKey - let maxkey: MaxKey + let maxkey: BSONMaxKey let regex: BSONRegularExpression let symbol: BSONSymbol let undefined: BSONUndefined @@ -306,12 +306,12 @@ final class CodecTests: MongoSwiftTestCase { int32: 5, int64: 6, dec: try BSONDecimal128("1.2E+10"), - minkey: MinKey(), - maxkey: MaxKey(), + minkey: BSONMinKey(), + maxkey: BSONMaxKey(), regex: BSONRegularExpression(pattern: "^abc", options: "imx"), symbol: BSONSymbol("i am a symbol"), undefined: BSONUndefined(), - dbpointer: DBPointer(ref: "some.namespace", id: try BSONObjectID("507f1f77bcf86cd799439011")), + dbpointer: BSONDBPointer(ref: "some.namespace", id: try BSONObjectID("507f1f77bcf86cd799439011")), null: BSONNull() ) } @@ -459,7 +459,7 @@ final class CodecTests: MongoSwiftTestCase { ).to(equal(regex)) expect(try decoder.decode(BSONMinKey.self, from: "{\"$minKey\": 1}")).to(equal(BSONMinKey())) - expect(try decoder.decode(MaxKey.self, from: "{\"$maxKey\": 1}")).to(equal(MaxKey())) + expect(try decoder.decode(BSONMaxKey.self, from: "{\"$maxKey\": 1}")).to(equal(BSONMaxKey())) expect(try decoder.decode(Bool.self, from: "false")).to(beFalse()) expect(try decoder.decode(Bool.self, from: "true")).to(beTrue()) diff --git a/Tests/BSONTests/Document+SequenceTests.swift b/Tests/BSONTests/Document+SequenceTests.swift index f5f32f7d0..824ef08ca 100644 --- a/Tests/BSONTests/Document+SequenceTests.swift +++ b/Tests/BSONTests/Document+SequenceTests.swift @@ -5,7 +5,7 @@ import TestsCommon import XCTest final class Document_SequenceTests: MongoSwiftTestCase { - func testIterator() { + func testIterator() throws { let doc: BSONDocument = [ "string": "test string", "true": true, From 0e056bf77205a4bf55dd1363571186f12c90ef56 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 27 May 2020 19:06:10 -0400 Subject: [PATCH 08/11] fix: bring back raw representable --- Sources/MongoSwift/BSON/BSONValue.swift | 22 ++++++++++++++++------ Tests/BSONTests/BSONValueTests.swift | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 4118296db..64ee6bf77 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -206,7 +206,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public let subtype: Subtype /// Subtypes for BSON Binary values. - public struct Subtype: Equatable, Codable, Hashable { + public struct Subtype: Equatable, Codable, Hashable, RawRepresentable { /// Generic binary subtype public static let generic = Subtype(0x00) /// A function @@ -223,10 +223,20 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { public static let encryptedValue = Subtype(0x06) /// Subtype indicator value - public let value: UInt8 + public let rawValue: UInt8 - private init(_ value: UInt8) { self.value = value } - internal init(_ value: bson_subtype_t) { self.value = UInt8(value.rawValue) } + /// Initializes a `Subtype` with a custom value. + /// Returns nil if rawValue outside of the range 0x80-0xFF. + public init?(rawValue: UInt8) { + guard rawValue >= 0x80 && rawValue <= 0xFF else { + return nil + } + self.rawValue = rawValue + } + + internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) } + + private init(_ value: Int) { self.rawValue = UInt8(value) } /// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF. /// - Throws: @@ -235,7 +245,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { guard value >= 0x80 && value <= 0xFF else { throw InvalidArgumentError(message: "User defined Binary Subtypes must be between 0x80 and 0xFF") } - return Subtype(UInt8(value)) + return Subtype(value) } } @@ -294,7 +304,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { } internal func encode(to document: inout BSONDocument, forKey key: String) throws { - let subtype = bson_subtype_t(UInt32(self.subtype.value)) + let subtype = bson_subtype_t(UInt32(self.subtype.rawValue)) let length = self.data.writerIndex guard let byteArray = self.data.getBytes(at: 0, length: length) else { throw InternalError(message: "Cannot read \(length) bytes from Binary.data") diff --git a/Tests/BSONTests/BSONValueTests.swift b/Tests/BSONTests/BSONValueTests.swift index be0199655..6acd567f1 100644 --- a/Tests/BSONTests/BSONValueTests.swift +++ b/Tests/BSONTests/BSONValueTests.swift @@ -257,5 +257,6 @@ final class BSONValueTests: MongoSwiftTestCase { // Check the subtype bounds are kept expect(try BSONBinary.Subtype.userDefined(0x100)).to(throwError()) expect(try BSONBinary.Subtype.userDefined(0x79)).to(throwError()) + expect(BSONBinary.Subtype(rawValue: 0x79)).to(beNil()) } } From 1e2f74db9c82f8cfd9658b8cec6469d26b3ff050 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 28 May 2020 13:08:53 -0400 Subject: [PATCH 09/11] fix: subtype range check --- Sources/MongoSwift/BSON/BSONValue.swift | 34 +++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index 64ee6bf77..e8d253331 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -207,28 +207,30 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// Subtypes for BSON Binary values. public struct Subtype: Equatable, Codable, Hashable, RawRepresentable { + // swiftlint:disable force_unwrapping /// Generic binary subtype - public static let generic = Subtype(0x00) + public static let generic = Subtype(rawValue: 0x00)! /// A function - public static let function = Subtype(0x01) + public static let function = Subtype(rawValue: 0x01)! /// Binary (old) - public static let binaryDeprecated = Subtype(0x02) + public static let binaryDeprecated = Subtype(rawValue: 0x02)! /// UUID (old) - public static let uuidDeprecated = Subtype(0x03) + public static let uuidDeprecated = Subtype(rawValue: 0x03)! /// UUID (RFC 4122) - public static let uuid = Subtype(0x04) + public static let uuid = Subtype(rawValue: 0x04)! /// MD5 - public static let md5 = Subtype(0x05) + public static let md5 = Subtype(rawValue: 0x05)! /// Encrypted BSON value - public static let encryptedValue = Subtype(0x06) + public static let encryptedValue = Subtype(rawValue: 0x06)! + // swiftlint:enable force_unwrapping /// Subtype indicator value public let rawValue: UInt8 /// Initializes a `Subtype` with a custom value. - /// Returns nil if rawValue outside of the range 0x80-0xFF. + /// Returns nil if rawValue within reserved range [0x07, 0x80). public init?(rawValue: UInt8) { - guard rawValue >= 0x80 && rawValue <= 0xFF else { + if rawValue > 0x06 && rawValue < 0x80 { return nil } self.rawValue = rawValue @@ -236,16 +238,20 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { internal init(_ value: bson_subtype_t) { self.rawValue = UInt8(value.rawValue) } - private init(_ value: Int) { self.rawValue = UInt8(value) } - /// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF. /// - Throws: /// - `InvalidArgumentError` if value passed is outside of the range 0x80-0xFF public static func userDefined(_ value: Int) throws -> Subtype { - guard value >= 0x80 && value <= 0xFF else { - throw InvalidArgumentError(message: "User defined Binary Subtypes must be between 0x80 and 0xFF") + guard let byteValue = UInt8(exactly: value) else { + throw InvalidArgumentError(message: "Cannot represent \(value) as UInt8") + } + guard byteValue >= 0x80 else { + throw InvalidArgumentError(message: "userDefined value must be greater than 0x80 got \(byteValue)") + } + guard let subtype = Subtype(rawValue: byteValue) else { + throw InvalidArgumentError(message: "Cannot represent \(byteValue) as Subtype") } - return Subtype(value) + return subtype } } From 10cacf36c8fc851eee58864032fb06489c32d081 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 28 May 2020 13:34:18 -0400 Subject: [PATCH 10/11] fix: use guard and error message --- Sources/MongoSwift/BSON/BSONValue.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index e8d253331..e708cc5c2 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -230,7 +230,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { /// Initializes a `Subtype` with a custom value. /// Returns nil if rawValue within reserved range [0x07, 0x80). public init?(rawValue: UInt8) { - if rawValue > 0x06 && rawValue < 0x80 { + guard !(rawValue > 0x06 && rawValue < 0x80) else { return nil } self.rawValue = rawValue @@ -246,7 +246,7 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { throw InvalidArgumentError(message: "Cannot represent \(value) as UInt8") } guard byteValue >= 0x80 else { - throw InvalidArgumentError(message: "userDefined value must be greater than 0x80 got \(byteValue)") + throw InvalidArgumentError(message: "userDefined value must be greater than or equal to 0x80 got \(byteValue)") } guard let subtype = Subtype(rawValue: byteValue) else { throw InvalidArgumentError(message: "Cannot represent \(byteValue) as Subtype") From a2961381fd813c6f73dadce9842f5fb17de0f269 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 28 May 2020 13:46:17 -0400 Subject: [PATCH 11/11] fix: linting --- Sources/MongoSwift/BSON/BSONValue.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index e708cc5c2..9bfdf9ce3 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -246,7 +246,9 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable { throw InvalidArgumentError(message: "Cannot represent \(value) as UInt8") } guard byteValue >= 0x80 else { - throw InvalidArgumentError(message: "userDefined value must be greater than or equal to 0x80 got \(byteValue)") + throw InvalidArgumentError( + message: "userDefined value must be greater than or equal to 0x80 got \(byteValue)" + ) } guard let subtype = Subtype(rawValue: byteValue) else { throw InvalidArgumentError(message: "Cannot represent \(byteValue) as Subtype")