diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 00000000..a391513b --- /dev/null +++ b/.swiftformat @@ -0,0 +1,4 @@ +--rules braces,redundantSelf,duplicateImports,linebreakAtEndOfFile,redundantReturn,sortImports +--swiftversion 5.9.1 +--allman true +--self insert diff --git a/Benchmarks/Package.resolved b/Benchmarks/Package.resolved index 5c275dd9..da62c520 100644 --- a/Benchmarks/Package.resolved +++ b/Benchmarks/Package.resolved @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-atomics.git", "state" : { - "revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10", - "version" : "1.1.0" + "revision" : "cd142fd2f64be2100422d658e7411e39489da985", + "version" : "1.2.0" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", - "version" : "1.0.4" + "revision" : "a902f1823a7ff3c9ab2fba0f992396b948eda307", + "version" : "1.0.5" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "2d8e6ca36fe3e8ed74b0883f593757a45463c34d", - "version" : "2.53.0" + "revision" : "702cd7c56d5d44eeba73fdf83918339b26dc855c", + "version" : "2.62.0" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-ssl.git", "state" : { - "revision" : "e866a626e105042a6a72a870c88b4c531ba05f83", - "version" : "2.24.0" + "revision" : "320bd978cceb8e88c125dcbb774943a92f6286e9", + "version" : "2.25.0" } }, { diff --git a/Package.resolved b/Package.resolved index d3c0e64a..ef288f0f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tayloraswift/swift-grammar", "state" : { - "revision" : "163ce5d4d88db7d94f2f4ca1cabcb2ae65af8af7", - "version" : "0.3.3" + "revision" : "6db3dd8912fc7acb821021cd96677b2e0eeb09f0", + "version" : "0.3.4" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "3db5c4aeee8100d2db6f1eaf3864afdad5dc68fd", - "version" : "2.59.0" + "revision" : "702cd7c56d5d44eeba73fdf83918339b26dc855c", + "version" : "2.62.0" } }, { diff --git a/Package.swift b/Package.swift index 3076ed2c..f3c3a76a 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package:Package = .init(name: "swift-mongodb", .library(name: "BSONDecoding", targets: ["BSONDecoding"]), .library(name: "BSONEncoding", targets: ["BSONEncoding"]), .library(name: "BSONTesting", targets: ["BSONTesting"]), - .library(name: "BSONTypes", targets: ["BSONTypes"]), + .library(name: "BSONABI", targets: ["BSONABI"]), .library(name: "BSON_Durations", targets: ["BSON_Durations"]), .library(name: "BSON_OrderedCollections", targets: ["BSON_OrderedCollections"]), @@ -25,7 +25,6 @@ let package:Package = .init(name: "swift-mongodb", .library(name: "Durations", targets: ["Durations"]), .library(name: "Durations_Atomics", targets: ["Durations_Atomics"]), - .library(name: "Mongo", targets: ["Mongo"]), .library(name: "MongoDB", targets: ["MongoDB"]), .library(name: "MongoQL", targets: ["MongoQL"]), .library(name: "MongoTesting", targets: ["MongoTesting"]), @@ -36,7 +35,7 @@ let package:Package = .init(name: "swift-mongodb", dependencies: [ .package(url: "https://github.com/tayloraswift/swift-grammar", .upToNextMinor( - from: "0.3.2")), + from: "0.3.4")), .package(url: "https://github.com/tayloraswift/swift-hash", .upToNextMinor( from: "0.5.0")), @@ -47,7 +46,7 @@ let package:Package = .init(name: "swift-mongodb", /// swift-nio has a low rate of breakage, and can be trusted with a major-only /// version requirement. - .package(url: "https://github.com/apple/swift-nio.git", from: "2.59.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"), /// swift-nio-ssl has a low rate of breakage, and can be trusted with a /// major-only version requirement. .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.25.0"), @@ -63,21 +62,39 @@ let package:Package = .init(name: "swift-mongodb", .target(name: "BSON", dependencies: [ - .target(name: "BSONStreaming"), + .target(name: "BSONDecoding"), + .target(name: "BSONEncoding"), + ], + exclude: + [ + "README.md", ]), - .target(name: "BSONTraversal"), + .target(name: "BSONABI", + exclude: + [ + "README.md", + ]), - .target(name: "BSONTypes", + .target(name: "BSONDecoding", dependencies: [ - .target(name: "BSONTraversal"), + .target(name: "BSONABI"), + .product(name: "TraceableErrors", package: "swift-grammar"), + ], + exclude: + [ + "README.md", ]), - .target(name: "BSONStreaming", + .target(name: "BSONEncoding", dependencies: [ - .target(name: "BSONTypes"), + .target(name: "BSONABI"), + ], + exclude: + [ + "README.md", ]), @@ -87,48 +104,31 @@ let package:Package = .init(name: "swift-mongodb", .target(name: "BSON"), ]), - .target(name: "BSONDecoding", - dependencies: - [ - .target(name: "BSON"), - .product(name: "TraceableErrors", package: "swift-grammar"), - ]), - - .target(name: "BSONEncoding", - dependencies: - [ - .target(name: "BSON"), - ]), - .target(name: "BSONTesting", dependencies: [ - .target(name: "BSONEncoding"), - .target(name: "BSONDecoding"), + .target(name: "BSON"), .product(name: "Testing", package: "swift-grammar"), ]), .target(name: "BSON_UUID", dependencies: [ - .target(name: "BSONDecoding"), - .target(name: "BSONEncoding"), + .target(name: "BSON"), .target(name: "UUID"), ]), .target(name: "BSON_Durations", dependencies: [ - .target(name: "BSONDecoding"), - .target(name: "BSONEncoding"), + .target(name: "BSON"), .target(name: "Durations"), ]), .target(name: "BSON_OrderedCollections", dependencies: [ - .target(name: "BSONDecoding"), - .target(name: "BSONEncoding"), + .target(name: "BSON"), .product(name: "OrderedCollections", package: "swift-collections"), ]), @@ -166,17 +166,23 @@ let package:Package = .init(name: "swift-mongodb", .target(name: "Mongo"), + .target(name: "MongoABI", + dependencies: + [ + .target(name: "BSON"), + .target(name: "Mongo"), + ]), + .target(name: "MongoBuiltins", dependencies: [ - .target(name: "MongoSchema"), + .target(name: "MongoABI"), ]), .target(name: "MongoClusters", dependencies: [ - .target(name: "BSONDecoding"), - .target(name: "BSONEncoding"), + .target(name: "BSON"), .target(name: "Durations"), .target(name: "Mongo"), .product(name: "TraceableErrors", package: "swift-grammar"), @@ -186,7 +192,7 @@ let package:Package = .init(name: "swift-mongodb", dependencies: [ .target(name: "MongoClusters"), - .target(name: "MongoSchema"), + .target(name: "MongoABI"), .product(name: "NIOCore", package: "swift-nio"), ]), @@ -197,6 +203,7 @@ let package:Package = .init(name: "swift-mongodb", .target(name: "BSON_OrderedCollections"), .target(name: "BSON_UUID"), .target(name: "Durations_Atomics"), + .target(name: "MongoABI"), .target(name: "MongoConfiguration"), .target(name: "MongoExecutor"), .target(name: "OnlineCDF"), @@ -216,6 +223,7 @@ let package:Package = .init(name: "swift-mongodb", .target(name: "MongoIO", dependencies: [ + .target(name: "Mongo"), .target(name: "MongoWire"), .product(name: "NIOCore", package: "swift-nio"), ]), @@ -228,14 +236,6 @@ let package:Package = .init(name: "swift-mongodb", .target(name: "MongoQL"), ]), - .target(name: "MongoSchema", - dependencies: - [ - .target(name: "BSONDecoding"), - .target(name: "BSONEncoding"), - .target(name: "Mongo"), - ]), - .target(name: "MongoTesting", dependencies: [ @@ -249,6 +249,7 @@ let package:Package = .init(name: "swift-mongodb", dependencies: [ .target(name: "BSON"), + .target(name: "Mongo"), .product(name: "CRC", package: "swift-hash"), ]), @@ -277,9 +278,8 @@ let package:Package = .init(name: "swift-mongodb", .executableTarget(name: "BSONIntegrationTests", dependencies: [ + .target(name: "BSON"), .target(name: "BSONReflection"), - .target(name: "BSONDecoding"), - .target(name: "BSONEncoding"), .product(name: "Testing", package: "swift-grammar"), ]), diff --git a/README.md b/README.md index 4dd1e8b6..22760d46 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-***`mongodb`***
`0.8` +***`mongodb`***
`0.9` [![ci status](https://github.com/tayloraswift/swift-mongodb/actions/workflows/build.yml/badge.svg)](https://github.com/tayloraswift/swift-mongodb/actions/workflows/build.yml) diff --git a/Sources/BSON/README.md b/Sources/BSON/README.md new file mode 100644 index 00000000..e050b045 --- /dev/null +++ b/Sources/BSON/README.md @@ -0,0 +1,9 @@ +# ``/BSON`` + +An umbrella module providing a BSON parser, encoder, and decoder. + +## Linking the BSON libraries + +This module re-exports the ``BSONABI``, ``BSONEncoding``, and ``BSONDecoding`` modules. Importing them directly is discouraged. + +Some BSON modules (currently ``BSONReflection`` and ``BSONTesting``) are considered ancillary and are not included in this umbrella module. diff --git a/Sources/BSON/exports.swift b/Sources/BSON/exports.swift index 0342acab..bf97bf35 100644 --- a/Sources/BSON/exports.swift +++ b/Sources/BSON/exports.swift @@ -1,2 +1,3 @@ -@_exported import BSONStreaming -@_exported import BSONTypes +@_exported import BSONABI +@_exported import BSONDecoding +@_exported import BSONEncoding diff --git a/Sources/BSONTypes/BSON.swift b/Sources/BSONABI/BSON.AnyType.swift similarity index 54% rename from Sources/BSONTypes/BSON.swift rename to Sources/BSONABI/BSON.AnyType.swift index f8bde6e0..7282c4ef 100644 --- a/Sources/BSONTypes/BSON.swift +++ b/Sources/BSONABI/BSON.AnyType.swift @@ -1,34 +1,38 @@ -/// A BSON metatype. This enumeration also serves as a namespace for all -/// other types in this module. The raw value of this enumeration is the -/// type code of the associated case in BSON’s ABI. -@frozen public -enum BSON:UInt8 +extension BSON { - case double = 0x01 - case string = 0x02 - case document = 0x03 - case list = 0x04 - case binary = 0x05 - - case id = 0x07 - case bool = 0x08 - case millisecond = 0x09 - case null = 0x0A - case regex = 0x0B - case pointer = 0x0C - case javascript = 0x0D + /// A BSON metatype. The raw value of this enumeration is the type code of the associated + /// case in BSON’s ABI. + @frozen public + enum AnyType:UInt8, Equatable, Hashable, Sendable + { + case double = 0x01 + case string = 0x02 + case document = 0x03 + case list = 0x04 + case binary = 0x05 - case javascriptScope = 0x0F - case int32 = 0x10 - case uint64 = 0x11 - case int64 = 0x12 - case decimal128 = 0x13 + case id = 0x07 + case bool = 0x08 + case millisecond = 0x09 + case null = 0x0A + case regex = 0x0B + case pointer = 0x0C + case javascript = 0x0D - case min = 0xFF - case max = 0x7F + case javascriptScope = 0x0F + case int32 = 0x10 + case uint64 = 0x11 + case int64 = 0x12 + case decimal128 = 0x13 + case min = 0xFF + case max = 0x7F + } +} +extension BSON.AnyType +{ /// Calls ``init(rawValue:)``, but throws a ``TypeError`` instead of returning - /// [`nil`](). + /// nil. @inlinable public init(code:UInt8) throws { @@ -38,7 +42,7 @@ enum BSON:UInt8 } else { - throw TypeError.init(invalid: code) + throw BSON.TypeError.init(invalid: code) } } /// Converts the given type code to a variant type. This function will canonicalize @@ -75,3 +79,8 @@ enum BSON:UInt8 } } } +extension BSON.AnyType:Comparable +{ + @inlinable public + static func < (a:Self, b:Self) -> Bool { a.rawValue < b.rawValue } +} diff --git a/Sources/BSONTypes/BSON.AnyValue.swift b/Sources/BSONABI/BSON.AnyValue.swift similarity index 57% rename from Sources/BSONTypes/BSON.AnyValue.swift rename to Sources/BSONABI/BSON.AnyValue.swift index c96d32bb..553cc152 100644 --- a/Sources/BSONTypes/BSON.AnyValue.swift +++ b/Sources/BSONABI/BSON.AnyValue.swift @@ -12,7 +12,8 @@ extension BSON case binary(BSON.BinaryView) /// A boolean. case bool(Bool) - /// An [IEEE 754-2008 128-bit decimal](https://en.wikipedia.org/wiki/Decimal128_floating-point_format). + /// An [IEEE 754-2008 128-bit + /// decimal](https://en.wikipedia.org/wiki/Decimal128_floating-point_format). case decimal128(BSON.Decimal128) /// A double-precision float. case double(Double) @@ -25,8 +26,8 @@ extension BSON /// Javascript code. /// The payload is a library type to permit efficient document traversal. case javascript(BSON.UTF8View) - /// A javascript scope containing code. This variant is maintained for - /// backward-compatibility with older versions of BSON and + /// A javascript scope containing code. This variant is maintained for + /// backward-compatibility with older versions of BSON and /// should not be generated. (Prefer ``javascript(_:)``.) case javascriptScope(BSON.DocumentView, BSON.UTF8View) /// The MongoDB max-key. @@ -69,29 +70,29 @@ extension BSON.AnyValue { /// The type of this variant value. @inlinable public - var type:BSON + var type:BSON.AnyType { switch self { - case .document: return .document - case .list: return .list - case .binary: return .binary - case .bool: return .bool - case .decimal128: return .decimal128 - case .double: return .double - case .id: return .id - case .int32: return .int32 - case .int64: return .int64 - case .javascript: return .javascript - case .javascriptScope: return .javascriptScope - case .max: return .max - case .millisecond: return .millisecond - case .min: return .min - case .null: return .null - case .pointer: return .pointer - case .regex: return .regex - case .string: return .string - case .uint64: return .uint64 + case .document: .document + case .list: .list + case .binary: .binary + case .bool: .bool + case .decimal128: .decimal128 + case .double: .double + case .id: .id + case .int32: .int32 + case .int64: .int64 + case .javascript: .javascript + case .javascriptScope: .javascriptScope + case .max: .max + case .millisecond: .millisecond + case .min: .min + case .null: .null + case .pointer: .pointer + case .regex: .regex + case .string: .string + case .uint64: .uint64 } } /// The size of this variant value when encoded. @@ -101,63 +102,63 @@ extension BSON.AnyValue switch self { case .document(let document): - return document.size + document.size case .list(let list): - return list.size + list.size case .binary(let binary): - return binary.size + binary.size case .bool: - return 1 + 1 case .decimal128: - return 16 + 16 case .double: - return 8 + 8 case .id: - return 12 + 12 case .int32: - return 4 + 4 case .int64: - return 8 + 8 case .javascript(let utf8): - return utf8.size + utf8.size case .javascriptScope(let scope, let utf8): - return 4 + utf8.size + scope.size + 4 + utf8.size + scope.size case .max: - return 0 + 0 case .millisecond: - return 8 + 8 case .min: - return 0 + 0 case .null: - return 0 + 0 case .pointer(let database, _): - return 12 + database.size + 12 + database.size case .regex(let regex): - return regex.size + regex.size case .string(let string): - return string.size + string.size case .uint64: - return 8 + 8 } } } extension BSON.AnyValue { - /// Promotes a [`nil`]() result to a thrown ``TypecastError``. - /// + /// Promotes a nil result to a thrown ``TypecastError``. + /// /// If `T` conforms to ``BSONDecodable``, prefer calling its throwing - /// ``BSONDecodable/.init(bson:)`` to calling this method directly. + /// ``BSONDecodable/init(bson:) [7O4O3]`` to calling this method directly. /// - /// > Throws: A ``TypecastError`` if the given curried method returns [`nil`](). + /// > Throws: A ``TypecastError`` if the given curried method returns nil. @inline(__always) - @inlinable public + @inlinable public func cast(with cast:(Self) throws -> T?) throws -> T { if let value:T = try cast(self) { - return value + return value } - else + else { throw BSON.TypecastError.init(invalid: self.type) } @@ -166,35 +167,35 @@ extension BSON.AnyValue extension BSON.AnyValue { /// Attempts to load an instance of ``Bool`` from this variant. - /// + /// /// - Returns: - /// The payload of this variant if it matches ``case bool(_:)``, - /// [`nil`]() otherwise. - @inlinable public + /// The payload of this variant if it matches ``bool(_:)``, + /// nil otherwise. + @inlinable public func `as`(_:Bool.Type) -> Bool? { - switch self + switch self { - case .bool(let bool): return bool - default: return nil + case .bool(let bool): bool + default: nil } } /// Attempts to load an instance of some ``FixedWidthInteger`` from this variant. - /// + /// /// - Returns: /// An integer derived from the payload of this variant - /// if it matches one of ``case int32(_:)``, ``case int64(_:)``, or - /// ``case uint64(_:)``, and it can be represented exactly by [`T`](); - /// [`nil`]() otherwise. + /// if it matches one of ``int32(_:)``, ``int64(_:)``, or + /// ``uint64(_:)``, and it can be represented exactly by `T`; + /// nil otherwise. /// - /// The ``case decimal128(_:)``, ``case double(_:)``, and ``case millisecond(_:)`` + /// The ``decimal128(_:)``, ``double(_:)``, and ``millisecond(_:)`` /// variants will *not* match. /// - /// This method reports failure in two ways — it returns [`nil`]() on a type - /// mismatch, and it [`throws`]() an ``IntegerOverflowError`` if this variant - /// was an integer, but it could not be represented exactly by [`T`](). - @inlinable public - func `as`(_:Integer.Type) throws -> Integer? + /// This method reports failure in two ways — it returns nil on a type + /// mismatch, and it throws an ``IntegerOverflowError`` if this variant + /// was an integer, but it could not be represented exactly by `T`. + @inlinable public + func `as`(_:Integer.Type) throws -> Integer? where Integer:FixedWidthInteger { switch self @@ -232,95 +233,95 @@ extension BSON.AnyValue } /// Attempts to load an instance of some ``BinaryFloatingPoint`` type from /// this variant. - /// + /// /// - Returns: - /// The closest value of [`T`]() to the payload of this - /// variant if it matches ``case double(_:)``, [`nil`]() otherwise. - @inlinable public + /// The closest value of `T` to the payload of this + /// variant if it matches ``double(_:)``, nil otherwise. + @inlinable public func `as`(_:Fraction.Type) -> Fraction? where Fraction:BinaryFloatingPoint { - switch self + switch self { case .double(let double): return .init(double) - default: return nil + default: return nil } } /// Attempts to load an instance of ``Decimal128`` from this variant. - /// + /// /// - Returns: - /// The payload of this variant if it matches ``case decimal128(_:)``, - /// [`nil`]() otherwise. - @inlinable public + /// The payload of this variant if it matches ``decimal128(_:)``, + /// nil otherwise. + @inlinable public func `as`(_:BSON.Decimal128.Type) -> BSON.Decimal128? { - switch self + switch self { - case .decimal128(let decimal): return decimal - default: return nil + case .decimal128(let decimal): decimal + default: nil } } /// Attempts to load an instance of ``Identifier`` from this variant. - /// + /// /// - Returns: - /// The payload of this variant if it matches ``case id(_:)`` or - /// ``case pointer(_:_:)``, [`nil`]() otherwise. - @inlinable public + /// The payload of this variant if it matches ``id(_:)`` or + /// ``pointer(_:_:)``, nil otherwise. + @inlinable public func `as`(_:BSON.Identifier.Type) -> BSON.Identifier? { - switch self + switch self { case .id(let id): - return id + id case .pointer(_, let id): - return id + id default: - return nil + nil } } /// Attempts to load an instance of ``Millisecond`` from this variant. - /// + /// /// - Returns: - /// The payload of this variant if it matches ``case millisecond(_:)``, - /// [`nil`]() otherwise. - @inlinable public + /// The payload of this variant if it matches ``millisecond(_:)``, + /// nil otherwise. + @inlinable public func `as`(_:BSON.Millisecond.Type) -> BSON.Millisecond? { - switch self + switch self { case .millisecond(let millisecond): - return millisecond + millisecond default: - return nil + nil } } /// Attempts to load an instance of ``Regex`` from this variant. - /// + /// /// - Returns: - /// The payload of this variant if it matches ``case regex(_:)``, - /// [`nil`]() otherwise. - @inlinable public + /// The payload of this variant if it matches ``regex(_:)``, + /// nil otherwise. + @inlinable public func `as`(_:BSON.Regex.Type) -> BSON.Regex? { - switch self + switch self { case .regex(let regex): - return regex + regex default: - return nil + nil } } /// Attempts to load an instance of ``String`` from this variant. /// Its UTF-8 code units will be validated (and repaired if needed). - /// + /// /// - Returns: /// The payload of this variant, decoded to a ``String``, if it matches - /// either ``case string(_:)`` or ``case javascript(_:)``, [`nil`]() + /// either ``string(_:)`` or ``javascript(_:)``, nil /// otherwise. /// - /// > Complexity: + /// > Complexity: /// O(*n*), where *n* is the length of the string. - @inlinable public + @inlinable public func `as`(_:String.Type) -> String? { self.utf8.map(String.init(bson:)) @@ -329,121 +330,186 @@ extension BSON.AnyValue extension BSON.AnyValue { /// Attempts to load an explicit ``null`` from this variant. - /// + /// /// - Returns: - /// [`nil`]() in the inner optional this variant is ``null``, - // [`nil`]() in the outer optional otherwise. - @inlinable public + /// nil in the inner optional this variant is ``null``, + // nil in the outer optional otherwise. + @inlinable public func `as`(_:Never?.Type) -> Never?? { - switch self + switch self { - case .null: return (nil as Never?) as Never?? - default: return nil as Never?? + case .null: (nil as Never?) as Never?? + default: nil as Never?? } } /// Attempts to load a ``max`` key from this variant. - /// + /// /// - Returns: - /// ``Max.max`` if this variant is ``max``, [`nil`]() otherwise. - @inlinable public + /// A (the) instance of ``Max`` if this variant is ``max``, nil otherwise. + @inlinable public func `as`(_:BSON.Max.Type) -> BSON.Max? { - switch self + switch self { - case .max: return .init() - default: return nil + case .max: .init() + default: nil } } /// Attempts to load a ``min`` key from this variant. - /// + /// /// - Returns: - /// ``Min.min`` if this variant is ``min``, [`nil`]() otherwise. - @inlinable public + /// A (the) instance of ``min`` if this variant is ``min``, nil otherwise. + @inlinable public func `as`(_:BSON.Min.Type) -> BSON.Min? { - switch self + switch self { - case .min: return .init() - default: return nil + case .min: .init() + default: nil } } } extension BSON.AnyValue { /// Attempts to unwrap a binary array from this variant. - /// - /// - Returns: The payload of this variant if it matches ``case binary(_:)``, - /// [`nil`]() otherwise. - /// + /// + /// - Returns: The payload of this variant if it matches ``binary(_:)``, + /// nil otherwise. + /// /// > Complexity: O(1). - @inlinable public + @inlinable public var binary:BSON.BinaryView? { - switch self + switch self { case .binary(let binary): - return binary + binary default: - return nil + nil } } /// Attempts to unwrap a document from this variant. - /// - /// - Returns: The payload of this variant if it matches ``case document(_:)`` - /// or ``case list(_:)``, [`nil`]() otherwise. - /// + /// + /// - Returns: The payload of this variant if it matches ``document(_:)`` + /// **or** ``list(_:)``, nil otherwise. + /// /// If the variant was a list, the string keys of the returned document are likely /// (but not guaranteed) to be the list indices encoded as base-10 strings, without /// leading zeros. - /// + /// /// > Complexity: O(1). - @inlinable public + @inlinable public var document:BSON.DocumentView? { - switch self + switch self { case .document(let document): - return document + document case .list(let list): - return list.document + list.document default: - return nil + nil } } /// Attempts to unwrap a list from this variant. - /// + /// /// - Returns: - /// The payload of this variant if it matches ``case list(_:)``, - /// [`nil`]() otherwise. + /// The payload of this variant if it matches ``list(_:)``, + /// nil otherwise. /// /// > Complexity: O(1). - @inlinable public + @inlinable public var list:BSON.ListView? { - switch self + switch self { - case .list(let list): return list - default: return nil + case .list(let list): list + default: nil } } /// Attempts to unwrap an instance of ``UTF8View`` from this variant. Its UTF-8 /// code units will *not* be validated, which allowes this method to return /// in constant time. - /// + /// /// - Returns: - /// The payload of this variant if it matches either ``case string(_:)`` - /// or ``case javascript(_:)``, [`nil`]() otherwise. + /// The payload of this variant if it matches either ``string(_:)`` + /// or ``javascript(_:)``, nil otherwise. /// /// > Complexity: O(1). - @inlinable public + @inlinable public var utf8:BSON.UTF8View? { - switch self + switch self { - case .javascript(let code): return code - case .string(let code): return code - default: return nil + case .javascript(let code): code + case .string(let code): code + default: nil } } } + +extension BSON.AnyValue:ExpressibleByStringLiteral, + ExpressibleByArrayLiteral, + ExpressibleByExtendedGraphemeClusterLiteral, + ExpressibleByUnicodeScalarLiteral, + ExpressibleByDictionaryLiteral + where Bytes:RangeReplaceableCollection, + Bytes:RandomAccessCollection, + Bytes.Index == Int +{ + @inlinable public + init(stringLiteral:String) + { + self = .string(.init(from: stringLiteral)) + } + @inlinable public + init(arrayLiteral:Self...) + { + self = .list(.init(elements: arrayLiteral)) + } + @inlinable public + init(dictionaryLiteral:(BSON.Key, Self)...) + { + self = .document(.init(fields: dictionaryLiteral)) + } +} + +extension BSON.AnyValue:ExpressibleByFloatLiteral +{ + @inlinable public + init(floatLiteral:Double) + { + self = .double(floatLiteral) + } +} +extension BSON.AnyValue:ExpressibleByIntegerLiteral +{ + /// Creates an instance initialized to the specified integer value. + /// It will be an ``int32(_:)`` value if it fits, otherwise it will + /// be an ``int64(_:)``. + /// + /// Although MongoDB uses ``Int32`` as its default integer type, + /// this library infers integer literals to be of type ``Int`` for + /// consistency with the rest of the Swift language. + @inlinable public + init(integerLiteral:Int) + { + if let int32:Int32 = .init(exactly: integerLiteral) + { + self = .int32(int32) + } + else + { + self = .int64(Int64.init(integerLiteral)) + } + } +} +extension BSON.AnyValue:ExpressibleByBooleanLiteral +{ + @inlinable public + init(booleanLiteral:Bool) + { + self = .bool(booleanLiteral) + } +} diff --git a/Sources/BSONABI/BSON.Decoder.swift b/Sources/BSONABI/BSON.Decoder.swift new file mode 100644 index 00000000..e7313fe4 --- /dev/null +++ b/Sources/BSONABI/BSON.Decoder.swift @@ -0,0 +1,22 @@ +extension BSON +{ + public + typealias Decoder = _BSONDecoder +} + +/// The name of this protocol is ``BSON.Decoder``. +public +protocol _BSONDecoder +{ + associatedtype Storage:RandomAccessCollection + + init(parsing bson:__shared BSON.AnyValue) throws +} + +extension BSON.Decoder +{ + /// Decoder elements are indices over fragments of BSON parsed from a larger allocation, + /// like ``Substring``s from a larger parent ``String``. + public + typealias Bytes = Storage.SubSequence +} diff --git a/Sources/BSONABI/BSON.Encoder.swift b/Sources/BSONABI/BSON.Encoder.swift new file mode 100644 index 00000000..ffc4c7b1 --- /dev/null +++ b/Sources/BSONABI/BSON.Encoder.swift @@ -0,0 +1,18 @@ +extension BSON +{ + public + typealias Encoder = _BSONEncoder +} + +/// The name of this protocol is ``BSON.Encoder``. +public +protocol _BSONEncoder +{ + init(_:consuming BSON.Output<[UInt8]>) + + consuming + func move() -> BSON.Output<[UInt8]> + + static + var type:BSON.AnyType { get } +} diff --git a/Sources/BSONTypes/BSON.TypeError.swift b/Sources/BSONABI/BSON.TypeError.swift similarity index 100% rename from Sources/BSONTypes/BSON.TypeError.swift rename to Sources/BSONABI/BSON.TypeError.swift diff --git a/Sources/BSONTypes/BSON.TypecastError.swift b/Sources/BSONABI/BSON.TypecastError.swift similarity index 78% rename from Sources/BSONTypes/BSON.TypecastError.swift rename to Sources/BSONABI/BSON.TypecastError.swift index 4d4c2663..108c8eea 100644 --- a/Sources/BSONTypes/BSON.TypecastError.swift +++ b/Sources/BSONABI/BSON.TypecastError.swift @@ -1,14 +1,14 @@ extension BSON { /// A decoder failed to cast a variant to an expected value type. - @frozen public + @frozen public struct TypecastError:Equatable, Error { public - let variant:BSON + let variant:AnyType @inlinable public - init(invalid variant:BSON) + init(invalid variant:AnyType) { self.variant = variant } @@ -17,7 +17,7 @@ extension BSON extension BSON.TypecastError:CustomStringConvertible { public - var description:String + var description:String { "cannot cast variant of type '\(self.variant)' to type '\(Value.self)'" } diff --git a/Sources/BSONABI/BSON.swift b/Sources/BSONABI/BSON.swift new file mode 100644 index 00000000..35da0206 --- /dev/null +++ b/Sources/BSONABI/BSON.swift @@ -0,0 +1,5 @@ +/// The namespace for all BSON types. +@frozen public +enum BSON +{ +} diff --git a/Sources/BSONStreaming/BSONRepresentable.swift b/Sources/BSONABI/BSONRepresentable.swift similarity index 90% rename from Sources/BSONStreaming/BSONRepresentable.swift rename to Sources/BSONABI/BSONRepresentable.swift index a095b83a..b84ea8f8 100644 --- a/Sources/BSONStreaming/BSONRepresentable.swift +++ b/Sources/BSONABI/BSONRepresentable.swift @@ -1,5 +1,3 @@ -import BSONTypes - public protocol BSONRepresentable { diff --git a/Sources/BSONStreaming/BSON.Field.swift b/Sources/BSONABI/Fields/BSON.FieldEncoder.swift similarity index 91% rename from Sources/BSONStreaming/BSON.Field.swift rename to Sources/BSONABI/Fields/BSON.FieldEncoder.swift index 664596a4..833b473d 100644 --- a/Sources/BSONStreaming/BSON.Field.swift +++ b/Sources/BSONABI/Fields/BSON.FieldEncoder.swift @@ -1,11 +1,14 @@ -import BSONTypes -import BSONTraversal - +extension BSON +{ + @available(*, deprecated, renamed: "FieldEncoder") + public + typealias Field = BSON.FieldEncoder +} extension BSON { /// A type that can serialize any BSON container element. @frozen public - struct Field + struct FieldEncoder { public let key:Key @@ -20,7 +23,7 @@ extension BSON } } } -extension BSON.Field +extension BSON.FieldEncoder { @inlinable public mutating func encode(double:Double) @@ -117,10 +120,10 @@ extension BSON.Field self[.javascript].serialize(utf8: javascript) } } -extension BSON.Field +extension BSON.FieldEncoder { @inlinable internal - subscript(type:BSON) -> Void + subscript(type:BSON.AnyType) -> Void { mutating get { @@ -129,7 +132,7 @@ extension BSON.Field } } @inlinable internal - subscript(type:BSON) -> BSON.Output<[UInt8]> + subscript(type:BSON.AnyType) -> BSON.Output<[UInt8]> { mutating get { @@ -143,14 +146,14 @@ extension BSON.Field } } } -extension BSON.Field +extension BSON.FieldEncoder { /// Writes the stored type code and ``key`` to the output buffer, temporarily rebinds /// the output’s storage buffer to an encoder of the specified type, and brackets any /// newly-written bytes with the appropriate headers or trailers, if performing a /// mutation. The getter has no effect. @inlinable public - subscript(as _:Encoder.Type = Encoder.self) -> Encoder where Encoder:BSONEncoder + subscript(as _:Encoder.Type = Encoder.self) -> Encoder where Encoder:BSON.Encoder { get { diff --git a/Sources/BSONTypes/BSON.Key.swift b/Sources/BSONABI/Fields/BSON.Key.swift similarity index 100% rename from Sources/BSONTypes/BSON.Key.swift rename to Sources/BSONABI/Fields/BSON.Key.swift diff --git a/Sources/BSONTraversal/VariableLengthBSON.swift b/Sources/BSONABI/IO/BSON.FrameTraversable.swift similarity index 60% rename from Sources/BSONTraversal/VariableLengthBSON.swift rename to Sources/BSONABI/IO/BSON.FrameTraversable.swift index 11029e2e..0c82392c 100644 --- a/Sources/BSONTraversal/VariableLengthBSON.swift +++ b/Sources/BSONABI/IO/BSON.FrameTraversable.swift @@ -1,20 +1,26 @@ -/// A BSON view that parsers can traverse in constant time. -/// -/// BSON parsers typically parse conforming types by reading a length -/// header from raw input data, and using it to slice the input, -/// ideally without copying backing storage. The interpretation of the -/// length header is specified by the ``Frame`` type requirement, and -/// the exact slicing behavior is determined by the implementation’s -/// ``init(slicing:)`` witness. +extension BSON +{ + /// A framed type that BSON parsers can traverse in constant time. + /// + /// BSON parsers typically parse conforming types by reading a length + /// header from raw input data, and using it to slice the input, + /// ideally without copying backing storage. The interpretation of the + /// length header is specified by the ``FrameTraversable/Frame`` type requirement, and + /// the exact slicing behavior is determined by the implementation’s + /// ``init(slicing:)`` witness. + public + typealias FrameTraversable = _BSONFrameTraversable +} + public -protocol VariableLengthBSON +protocol _BSONFrameTraversable { /// The backing storage used by this type. I recommend satisfying /// this with generics, to avoid copying input data. associatedtype Bytes:RandomAccessCollection /// The type specifying how parsers should interpret the conforming /// type’s inline frame header when it appears in raw input data. - associatedtype Frame:VariableLengthBSONFrame + associatedtype Frame:BSON.FrameType /// Receives a collection of bytes encompassing the bytes backing /// this value, after stripping the length header and frame suffix, diff --git a/Sources/BSONTraversal/VariableLengthBSONFrame.swift b/Sources/BSONABI/IO/BSON.FrameType.swift similarity index 61% rename from Sources/BSONTraversal/VariableLengthBSONFrame.swift rename to Sources/BSONABI/IO/BSON.FrameType.swift index 6eaf391f..b99c0c85 100644 --- a/Sources/BSONTraversal/VariableLengthBSONFrame.swift +++ b/Sources/BSONABI/IO/BSON.FrameType.swift @@ -1,8 +1,15 @@ -/// A type that specifies the layout of a variable-length BSON view. -/// Parsers use conforming types to decide how to interpret BSON -/// length headers read from input data. +extension BSON +{ + /// A type that specifies the layout of a variable-length BSON view. + /// Parsers use conforming types to decide how to interpret BSON + /// length headers read from input data. + public + typealias FrameType = _BSONFrameType +} + +/// The name of this protocol is ``BSON.FrameType``. public -protocol VariableLengthBSONFrame +protocol _BSONFrameType { /// The number of (conceptual) bytes in the frame prefix of the type /// this frame type is associated with. @@ -16,18 +23,19 @@ protocol VariableLengthBSONFrame static var trailer:UInt8? { get } } -extension VariableLengthBSONFrame + +extension BSON.FrameType { /// The number of (conceptual) bytes in the frame suffix of the type /// this frame type is associated with. This is 0 if ``trailer`` is /// nil, and 1 otherwise. - @inlinable public static + @inlinable internal static var suffix:Int { switch self.trailer { - case nil: return 0 - case _?: return 1 + case nil: 0 + case _?: 1 } } } diff --git a/Sources/BSONABI/IO/BSON.FrameView.swift b/Sources/BSONABI/IO/BSON.FrameView.swift new file mode 100644 index 00000000..79279971 --- /dev/null +++ b/Sources/BSONABI/IO/BSON.FrameView.swift @@ -0,0 +1,20 @@ +extension BSON +{ + /// A type that can be (efficiently) decoded from a BSON variant value + /// backed by a preferred type of storage particular to the decoded type. + /// + /// This protocol is parallel and unrelated to ``BSONDecodable`` to + /// emphasize the performance characteristics of types that conform to + /// this protocol and not ``BSONDecodable``. + public + typealias FrameView = _BSONFrameView +} + +/// The name of this protocol is ``BSON.FrameView``. +public +protocol _BSONFrameView:BSON.FrameTraversable, Equatable +{ + /// Attempts to cast a BSON variant backed by ``Bytes`` to an instance + /// of this view type without copying the contents of the backing storage. + init(_:BSON.AnyValue) throws +} diff --git a/Sources/BSONTypes/BSON.HeaderError.swift b/Sources/BSONABI/IO/BSON.HeaderError.swift similarity index 76% rename from Sources/BSONTypes/BSON.HeaderError.swift rename to Sources/BSONABI/IO/BSON.HeaderError.swift index 2ad21366..4f0e235f 100644 --- a/Sources/BSONTypes/BSON.HeaderError.swift +++ b/Sources/BSONABI/IO/BSON.HeaderError.swift @@ -1,14 +1,12 @@ -import BSONTraversal - extension BSON { - public - struct HeaderError:Equatable, Error where Frame:VariableLengthBSONFrame + @frozen public + struct HeaderError:Equatable, Error where Frame:FrameType { public let length:Int - public + @inlinable public init(length:Int) { self.length = length diff --git a/Sources/BSONStreaming/BSON.Input.swift b/Sources/BSONABI/IO/BSON.Input.swift similarity index 97% rename from Sources/BSONStreaming/BSON.Input.swift rename to Sources/BSONABI/IO/BSON.Input.swift index 2db18442..61e02d45 100644 --- a/Sources/BSONStreaming/BSON.Input.swift +++ b/Sources/BSONABI/IO/BSON.Input.swift @@ -1,6 +1,3 @@ -import BSONTraversal -import BSONTypes - extension BSON { /// A type for managing BSON parsing state. Most users of this module @@ -153,7 +150,7 @@ extension BSON.Input @inlinable public mutating func parse(_:Frame.Type) throws -> Source.SubSequence - where Frame:VariableLengthBSONFrame + where Frame:BSON.FrameType { let header:Int = .init(try self.parse(as: Int32.self)) let stride:Int = header + Frame.skipped @@ -179,7 +176,7 @@ extension BSON.Input /// which allows decoders to skip over regions of a BSON document. @inlinable public mutating func parse(as _:View.Type = View.self) throws -> View - where View:VariableLengthBSON + where View:BSON.FrameTraversable { try .init(slicing: try self.parse(View.Frame.self)) } @@ -216,7 +213,7 @@ extension BSON.Input { /// Parses a variant BSON value, assuming it is of the specified `variant` type. @inlinable public mutating - func parse(variant:BSON) throws -> BSON.AnyValue + func parse(variant:BSON.AnyType) throws -> BSON.AnyValue { switch variant { diff --git a/Sources/BSONStreaming/BSON.InputError.Expectation.swift b/Sources/BSONABI/IO/BSON.InputError.Expectation.swift similarity index 81% rename from Sources/BSONStreaming/BSON.InputError.Expectation.swift rename to Sources/BSONABI/IO/BSON.InputError.Expectation.swift index 8bd8dda1..09702b77 100644 --- a/Sources/BSONStreaming/BSON.InputError.Expectation.swift +++ b/Sources/BSONABI/IO/BSON.InputError.Expectation.swift @@ -1,5 +1,3 @@ -import BSONTypes - extension BSON.InputError { @frozen public @@ -21,11 +19,11 @@ extension BSON.InputError.Expectation:CustomStringConvertible switch self { case .end: - return "end-of-input" + "end-of-input" case .byte(let byte): - return "terminator byte (\(byte))" + "terminator byte (\(byte))" case .bytes(let count): - return "\(count) byte(s)" + "\(count) byte(s)" } } } diff --git a/Sources/BSONStreaming/BSON.InputError.swift b/Sources/BSONABI/IO/BSON.InputError.swift similarity index 97% rename from Sources/BSONStreaming/BSON.InputError.swift rename to Sources/BSONABI/IO/BSON.InputError.swift index e75a68c6..7c1c9244 100644 --- a/Sources/BSONStreaming/BSON.InputError.swift +++ b/Sources/BSONABI/IO/BSON.InputError.swift @@ -1,5 +1,3 @@ -import BSONTypes - extension BSON { /// A parser did not receive the expected amount of input. diff --git a/Sources/BSONStreaming/BSON.Output.swift b/Sources/BSONABI/IO/BSON.Output.swift similarity index 95% rename from Sources/BSONStreaming/BSON.Output.swift rename to Sources/BSONABI/IO/BSON.Output.swift index ec3fd5a4..69c63396 100644 --- a/Sources/BSONStreaming/BSON.Output.swift +++ b/Sources/BSONABI/IO/BSON.Output.swift @@ -1,6 +1,3 @@ -import BSONTypes -import BSONTraversal - extension BSON { @frozen public @@ -24,7 +21,8 @@ extension BSON /// number of bytes in the destination buffer. /// /// The size hint is only effective if `Destination` provides a real, - /// non-defaulted witness for ``RangeReplaceableCollection.reserveCapacity(_:)``. + /// non-defaulted witness for + /// ``RangeReplaceableCollection.reserveCapacity(_:) [2YKV1]``. @inlinable public init(capacity:Int) { @@ -54,7 +52,7 @@ extension BSON.Output extension BSON.Output { @inlinable public mutating - func serialize(type:BSON) + func serialize(type:BSON.AnyType) { self.append(type.rawValue) } @@ -213,7 +211,7 @@ extension BSON.Output<[UInt8]> /// - See also: ``subscript(as:)``. @inlinable public subscript(as _:Encoder.Type, in frame:BSON.DocumentFrame.Type) -> Encoder - where Encoder:BSONEncoder + where Encoder:BSON.Encoder { get { @@ -230,7 +228,7 @@ extension BSON.Output<[UInt8]> /// /// - See also: ``subscript(with:)``. @inlinable public - subscript(as _:Encoder.Type) -> Encoder where Encoder:BSONEncoder + subscript(as _:Encoder.Type) -> Encoder where Encoder:BSON.Encoder { _read { @@ -245,7 +243,7 @@ extension BSON.Output<[UInt8]> } } @inlinable public - subscript(with key:BSON.Key) -> BSON.Field + subscript(with key:BSON.Key) -> BSON.FieldEncoder { get { @@ -253,14 +251,14 @@ extension BSON.Output<[UInt8]> } _modify { - var field:BSON.Field = self[with: key] + var field:BSON.FieldEncoder = self[with: key] self = .init(preallocated: []) defer { self = field.output } yield &field } } @inlinable internal - subscript(in frame:(some VariableLengthBSONFrame).Type) -> Self + subscript(in frame:(some BSON.FrameType).Type) -> Self { get { diff --git a/Sources/BSONTraversal/BufferSlice.swift b/Sources/BSONABI/IO/BufferSlice.swift similarity index 100% rename from Sources/BSONTraversal/BufferSlice.swift rename to Sources/BSONABI/IO/BufferSlice.swift diff --git a/Sources/BSONTypes/BSON.BooleanSubtypeError.swift b/Sources/BSONABI/Primitives/BSON.BooleanSubtypeError.swift similarity index 100% rename from Sources/BSONTypes/BSON.BooleanSubtypeError.swift rename to Sources/BSONABI/Primitives/BSON.BooleanSubtypeError.swift diff --git a/Sources/BSONTypes/BSON.Decimal128.swift b/Sources/BSONABI/Primitives/BSON.Decimal128.swift similarity index 91% rename from Sources/BSONTypes/BSON.Decimal128.swift rename to Sources/BSONABI/Primitives/BSON.Decimal128.swift index e119afbb..972272e5 100644 --- a/Sources/BSONTypes/BSON.Decimal128.swift +++ b/Sources/BSONABI/Primitives/BSON.Decimal128.swift @@ -8,15 +8,15 @@ extension BSON /// /// Take caution when using this type’s ``Hashable`` and ``Equatable`` conformances. /// Two `Decimal128` values can encode the same numeric value, yet compare unequal - /// under ``Equatable/.==(_:_:)``. + /// under ``Equatable.==(_:_:)``. @frozen public struct Decimal128:Hashable, Equatable, Sendable { /// The low 64 bits of this decimal value. - public + public var low:UInt64 /// The high 64 bits of this decimal value. - public + public var high:UInt64 @inlinable public diff --git a/Sources/BSONStreaming/BSON.Document.swift b/Sources/BSONABI/Primitives/BSON.Document.swift similarity index 96% rename from Sources/BSONStreaming/BSON.Document.swift rename to Sources/BSONABI/Primitives/BSON.Document.swift index 8fcaa61e..3887a94a 100644 --- a/Sources/BSONStreaming/BSON.Document.swift +++ b/Sources/BSONABI/Primitives/BSON.Document.swift @@ -1,5 +1,3 @@ -import BSONTypes - extension BSON { /// The `Document` type models the “universal” BSON DSL. @@ -82,7 +80,7 @@ extension BSON.Document self.output.append(other.bytes) } @inlinable public mutating - func append(_ key:String, with encode:(inout BSON.Field) -> ()) + func append(_ key:String, with encode:(inout BSON.FieldEncoder) -> ()) { encode(&self.output[with: .init(rawValue: key)]) } diff --git a/Sources/BSONTypes/BSON.Identifier.swift b/Sources/BSONABI/Primitives/BSON.Identifier.swift similarity index 100% rename from Sources/BSONTypes/BSON.Identifier.swift rename to Sources/BSONABI/Primitives/BSON.Identifier.swift diff --git a/Sources/BSONTypes/BSON.IntegerOverflowError.swift b/Sources/BSONABI/Primitives/BSON.IntegerOverflowError.swift similarity index 74% rename from Sources/BSONTypes/BSON.IntegerOverflowError.swift rename to Sources/BSONABI/Primitives/BSON.IntegerOverflowError.swift index 0bea0c5a..d90fce4b 100644 --- a/Sources/BSONTypes/BSON.IntegerOverflowError.swift +++ b/Sources/BSONABI/Primitives/BSON.IntegerOverflowError.swift @@ -23,11 +23,11 @@ extension BSON.IntegerOverflowError:CustomStringConvertible switch self { case .int32 (let value): - return "value '\(value)' of type 'int32' overflows decoded type '\(Overflowed.self)'" + "value '\(value)' of type 'int32' overflows decoded type '\(Overflowed.self)'" case .int64 (let value): - return "value '\(value)' of type 'int64' overflows decoded type '\(Overflowed.self)'" + "value '\(value)' of type 'int64' overflows decoded type '\(Overflowed.self)'" case .uint64(let value): - return "value '\(value)' of type 'uint64' overflows decoded type '\(Overflowed.self)'" + "value '\(value)' of type 'uint64' overflows decoded type '\(Overflowed.self)'" } } } diff --git a/Sources/BSONStreaming/BSON.List.swift b/Sources/BSONABI/Primitives/BSON.List.swift similarity index 98% rename from Sources/BSONStreaming/BSON.List.swift rename to Sources/BSONABI/Primitives/BSON.List.swift index 67093cde..be275024 100644 --- a/Sources/BSONStreaming/BSON.List.swift +++ b/Sources/BSONABI/Primitives/BSON.List.swift @@ -1,5 +1,3 @@ -import BSONTypes - extension BSON { @frozen public diff --git a/Sources/BSONTypes/BSON.Max.swift b/Sources/BSONABI/Primitives/BSON.Max.swift similarity index 100% rename from Sources/BSONTypes/BSON.Max.swift rename to Sources/BSONABI/Primitives/BSON.Max.swift diff --git a/Sources/BSONTypes/BSON.Millisecond.swift b/Sources/BSONABI/Primitives/BSON.Millisecond.swift similarity index 100% rename from Sources/BSONTypes/BSON.Millisecond.swift rename to Sources/BSONABI/Primitives/BSON.Millisecond.swift diff --git a/Sources/BSONTypes/BSON.Min.swift b/Sources/BSONABI/Primitives/BSON.Min.swift similarity index 100% rename from Sources/BSONTypes/BSON.Min.swift rename to Sources/BSONABI/Primitives/BSON.Min.swift diff --git a/Sources/BSONTypes/BSON.Regex.OptionError.swift b/Sources/BSONABI/Primitives/BSON.Regex.OptionError.swift similarity index 100% rename from Sources/BSONTypes/BSON.Regex.OptionError.swift rename to Sources/BSONABI/Primitives/BSON.Regex.OptionError.swift diff --git a/Sources/BSONTypes/BSON.Regex.Options.swift b/Sources/BSONABI/Primitives/BSON.Regex.Options.swift similarity index 94% rename from Sources/BSONTypes/BSON.Regex.Options.swift rename to Sources/BSONABI/Primitives/BSON.Regex.Options.swift index 6f96e7fb..53633767 100644 --- a/Sources/BSONTypes/BSON.Regex.Options.swift +++ b/Sources/BSONABI/Primitives/BSON.Regex.Options.swift @@ -59,12 +59,12 @@ extension BSON.Regex.Option { switch self { - case .i: return "i" - case .l: return "l" - case .m: return "m" - case .s: return "s" - case .u: return "u" - case .x: return "x" + case .i: "i" + case .l: "l" + case .m: "m" + case .s: "s" + case .u: "u" + case .x: "x" } } @inlinable public diff --git a/Sources/BSONTypes/BSON.Regex.swift b/Sources/BSONABI/Primitives/BSON.Regex.swift similarity index 100% rename from Sources/BSONTypes/BSON.Regex.swift rename to Sources/BSONABI/Primitives/BSON.Regex.swift diff --git a/Sources/BSONABI/README.md b/Sources/BSONABI/README.md new file mode 100644 index 00000000..ea369640 --- /dev/null +++ b/Sources/BSONABI/README.md @@ -0,0 +1,66 @@ +# ``/BSONABI`` + +Models the BSON type system and the binary interface of the BSON serialization format. + +External users should avoid importing this module directly. Instead, import ``/BSON``. + +## Topics + +### Type system + +- ``BSON.AnyValue`` +- ``BSON.AnyType`` + +### Primitive types + +- ``BSON.Min`` +- ``BSON.Max`` +- ``BSON.Identifier`` +- ``BSON.Millisecond`` +- ``BSON.Decimal128`` +- ``BSON.Regex`` + +### String-like types + +- ``BSON.BinaryView`` +- ``BSON.UTF8View`` + +### Container types + +- ``BSON.List`` +- ``BSON.ListView`` +- ``BSON.Document`` +- ``BSON.DocumentView`` + +### Container fields + +- ``BSON.Key`` +- ``BSON.FieldEncoder`` + +### Binary interface + +- ``BSON.FrameTraversable`` +- ``BSON.FrameType`` +- ``BSON.FrameView`` + +### Binary frame types + +- ``BSON.BinaryFrame`` +- ``BSON.DocumentFrame`` +- ``BSON.UTF8Frame`` + +### Parsing and decoding + +This module only implements the basic infrastructure for BSON decoding. Most of the public decoding interface is in ``BSONDecoding``. + +- ``BSON.Decoder`` +- ``BSON.Input`` +- ``BSON.TypeError`` +- ``BSON.TypecastError`` + +### Serialization and encoding + +This module only implements the basic infrastructure for BSON encoding. Most of the public encoding interface is in ``BSONEncoding``. + +- ``BSON.Encoder`` +- ``BSON.Output`` diff --git a/Sources/BSONTypes/BSON.BinaryFrame.swift b/Sources/BSONABI/Views/BSON.BinaryFrame.swift similarity index 84% rename from Sources/BSONTypes/BSON.BinaryFrame.swift rename to Sources/BSONABI/Views/BSON.BinaryFrame.swift index 2abb3488..190c70e6 100644 --- a/Sources/BSONTypes/BSON.BinaryFrame.swift +++ b/Sources/BSONABI/Views/BSON.BinaryFrame.swift @@ -1,11 +1,9 @@ -import BSONTraversal - extension BSON { - /// Specifies the interpretation of a length header attached to a ``binary`` + /// Specifies the interpretation of a length header attached to a ``BSON.AnyType/binary`` /// array. @frozen public - enum BinaryFrame:VariableLengthBSONFrame + enum BinaryFrame:FrameType { /// A binary array header starts its count after skipping the interceding /// subtype byte. diff --git a/Sources/BSONTypes/BSON.BinarySubtype.swift b/Sources/BSONABI/Views/BSON.BinarySubtype.swift similarity index 100% rename from Sources/BSONTypes/BSON.BinarySubtype.swift rename to Sources/BSONABI/Views/BSON.BinarySubtype.swift diff --git a/Sources/BSONTypes/BSON.BinarySubtypeError.swift b/Sources/BSONABI/Views/BSON.BinarySubtypeError.swift similarity index 100% rename from Sources/BSONTypes/BSON.BinarySubtypeError.swift rename to Sources/BSONABI/Views/BSON.BinarySubtypeError.swift diff --git a/Sources/BSONTypes/BSON.BinaryView.swift b/Sources/BSONABI/Views/BSON.BinaryView.swift similarity index 93% rename from Sources/BSONTypes/BSON.BinaryView.swift rename to Sources/BSONABI/Views/BSON.BinaryView.swift index 08a9ab8f..edcad569 100644 --- a/Sources/BSONTypes/BSON.BinaryView.swift +++ b/Sources/BSONABI/Views/BSON.BinaryView.swift @@ -1,5 +1,3 @@ -import BSONTraversal - extension BSON { /// A BSON binary array. @@ -8,7 +6,7 @@ extension BSON { /// The contents of this binary array. This collection does *not* /// include the leading subtype byte. - public + public let slice:Bytes /// The subtype of this binary array. public @@ -36,11 +34,11 @@ extension BSON.BinaryView:Equatable extension BSON.BinaryView:Sendable where Bytes:Sendable { } -extension BSON.BinaryView:VariableLengthBSON where Bytes.SubSequence == Bytes +extension BSON.BinaryView:BSON.FrameTraversable where Bytes.SubSequence == Bytes { public typealias Frame = BSON.BinaryFrame - + /// Removes the first element of the argument, attempts to cast it to a /// ``BinarySubtype``, and stores the remainder in ``slice``. /// @@ -79,7 +77,10 @@ extension BSON.BinaryView:VariableLengthBSON where Bytes.SubSequence == Bytes } } } -extension BSON.BinaryView:BSONView +extension BSON.BinaryView:BSON.FrameView where Bytes.SubSequence == Bytes +{ +} +extension BSON.BinaryView { @inlinable public init(_ value:BSON.AnyValue) throws diff --git a/Sources/BSONTypes/BSON.BinaryViewError.Expectation.swift b/Sources/BSONABI/Views/BSON.BinaryViewError.Expectation.swift similarity index 85% rename from Sources/BSONTypes/BSON.BinaryViewError.Expectation.swift rename to Sources/BSONABI/Views/BSON.BinaryViewError.Expectation.swift index f45795d6..1895afe7 100644 --- a/Sources/BSONTypes/BSON.BinaryViewError.Expectation.swift +++ b/Sources/BSONABI/Views/BSON.BinaryViewError.Expectation.swift @@ -17,9 +17,9 @@ extension BSON.BinaryViewError.Expectation:CustomStringConvertible switch self { case .subtype: - return "subtype (1 byte)" + "subtype (1 byte)" case .subheader: - return "subheader (4 bytes)" + "subheader (4 bytes)" } } } diff --git a/Sources/BSONTypes/BSON.BinaryViewError.swift b/Sources/BSONABI/Views/BSON.BinaryViewError.swift similarity index 100% rename from Sources/BSONTypes/BSON.BinaryViewError.swift rename to Sources/BSONABI/Views/BSON.BinaryViewError.swift diff --git a/Sources/BSONTypes/BSON.DocumentFrame.swift b/Sources/BSONABI/Views/BSON.DocumentFrame.swift similarity index 78% rename from Sources/BSONTypes/BSON.DocumentFrame.swift rename to Sources/BSONABI/Views/BSON.DocumentFrame.swift index 5f0cad6c..5e1fc03a 100644 --- a/Sources/BSONTypes/BSON.DocumentFrame.swift +++ b/Sources/BSONABI/Views/BSON.DocumentFrame.swift @@ -1,11 +1,9 @@ -import BSONTraversal - extension BSON { - /// Specifies the interpretation of a length header attached to a ``document``, - /// or a ``list`` document. + /// Specifies the interpretation of a length header attached to a ``BSON.AnyType/document``, + /// or a ``BSON.AnyType/list`` document. @frozen public - enum DocumentFrame:VariableLengthBSONFrame + enum DocumentFrame:FrameType { /// A document’s length header counts its own length. In other words, /// it skips -4 bytes. diff --git a/Sources/BSONStreaming/BSON.DocumentView (ext).swift b/Sources/BSONABI/Views/BSON.DocumentView.swift similarity index 50% rename from Sources/BSONStreaming/BSON.DocumentView (ext).swift rename to Sources/BSONABI/Views/BSON.DocumentView.swift index 5738815c..85f4dfb6 100644 --- a/Sources/BSONStreaming/BSON.DocumentView (ext).swift +++ b/Sources/BSONABI/Views/BSON.DocumentView.swift @@ -1,4 +1,77 @@ -import BSONTypes +extension BSON +{ + /// A BSON document. The backing storage of this type is opaque, + /// permitting lazy parsing of its inline content. + @frozen public + struct DocumentView where Bytes:RandomAccessCollection + { + /// The raw data backing this document. This collection *does not* + /// include the trailing null byte that typically appears after its + /// inline field list. + public + let slice:Bytes + + /// Stores the argument in ``slice`` unchanged. + /// + /// > Complexity: O(1) + @inlinable public + init(slice:Bytes) + { + self.slice = slice + } + } +} +extension BSON.DocumentView:Equatable +{ + /// Performs an exact byte-wise comparison on two lists. + /// Does not parse or validate the operands. + @inlinable public static + func == (lhs:Self, rhs:BSON.DocumentView>) -> Bool + { + lhs.slice.elementsEqual(rhs.slice) + } +} +extension BSON.DocumentView:Sendable where Bytes:Sendable +{ +} +extension BSON.DocumentView:BSON.FrameTraversable +{ + public + typealias Frame = BSON.DocumentFrame + + /// Stores the argument in ``slice`` unchanged. Equivalent to ``init(slice:)``. + /// + /// > Complexity: O(1) + @inlinable public + init(slicing bytes:Bytes) + { + self.init(slice: bytes) + } +} +extension BSON.DocumentView:BSON.FrameView +{ + @inlinable public + init(_ value:BSON.AnyValue) throws + { + self = try value.cast(with: \.document) + } +} +extension BSON.DocumentView +{ + /// Indicates if this document contains no fields. + @inlinable public + var isEmpty:Bool { self.slice.isEmpty } + + /// The length that would be encoded in this document’s prefixed header. + /// Equal to ``size``. + @inlinable public + var header:Int32 { .init(self.size) } + + /// The size of this document when encoded with its header and trailing + /// null byte. This *is* the same as the length encoded in the header itself. + @inlinable public + var size:Int { 5 + self.slice.count } +} extension BSON.DocumentView<[UInt8]> { @@ -12,12 +85,13 @@ extension BSON.DocumentView<[UInt8]> self.init(slice: document.bytes) } } + extension BSON.DocumentView { /// Parses this document into key-value pairs in order, yielding each key-value /// pair to the provided closure. /// - /// Unlike ``parse``, this method does not allocate storage for the parsed key-value + /// Unlike ``parse(_:)``, this method does not allocate storage for the parsed key-value /// pairs. (But it does allocate storage to provide a ``String`` representation for /// each visited key.) /// @@ -25,13 +99,13 @@ extension BSON.DocumentView /// O(*n*), where *n* is the size of this document’s backing storage. @inlinable public func parse( - to decode:(_ key:CodingKey, _ value:BSON.AnyValue) throws -> ()) throws + to decode:(CodingKey, BSON.AnyValue) throws -> ()) throws where CodingKey:RawRepresentable { var input:BSON.Input = .init(self.slice) while let code:UInt8 = input.next() { - let type:BSON = try .init(code: code) + let type:BSON.AnyType = try .init(code: code) let key:String = try input.parse(as: String.self) // We must parse the value always, even if we are ignoring the key let value:BSON.AnyValue = try input.parse(variant: type) @@ -63,7 +137,7 @@ extension BSON.DocumentView return elements } } -extension BSON.DocumentView:ExpressibleByDictionaryLiteral +extension BSON.DocumentView:ExpressibleByDictionaryLiteral where Bytes:RangeReplaceableCollection, Bytes:RandomAccessCollection, Bytes.Index == Int @@ -72,14 +146,19 @@ extension BSON.DocumentView:ExpressibleByDictionaryLiteral /// the list of fields in order to encode the output without reallocations. /// The order of the fields will be preserved. @inlinable public - init(fields:some Collection<(key:BSON.Key, value:BSON.AnyValue>)>) + init(fields:some Collection< + ( + key:BSON.Key, + value:BSON.AnyValue> + )>) { let size:Int = fields.reduce(0) { $0 + 2 + $1.key.rawValue.utf8.count + $1.value.size } var output:BSON.Output = .init(capacity: size) output.serialize(fields: fields) - - assert(output.destination.count == size, - "precomputed size (\(size)) does not match output size (\(output.destination.count))") + + assert(output.destination.count == size, """ + precomputed size (\(size)) does not match output size (\(output.destination.count)) + """) self.init(slice: output.destination) } @@ -89,8 +168,8 @@ extension BSON.DocumentView:ExpressibleByDictionaryLiteral init(key:BSON.Key, value:BSON.AnyValue) where Other:RandomAccessCollection { - self.init( - fields: CollectionOfOne<(key:BSON.Key, value:BSON.AnyValue)>.init((key, value))) + self.init(fields: + CollectionOfOne<(key:BSON.Key, value:BSON.AnyValue)>.init((key, value))) } @inlinable public diff --git a/Sources/BSONStreaming/BSON.ListView (ext).swift b/Sources/BSONABI/Views/BSON.ListView.swift similarity index 56% rename from Sources/BSONStreaming/BSON.ListView (ext).swift rename to Sources/BSONABI/Views/BSON.ListView.swift index d62f553e..f956517b 100644 --- a/Sources/BSONStreaming/BSON.ListView (ext).swift +++ b/Sources/BSONABI/Views/BSON.ListView.swift @@ -1,4 +1,78 @@ -import BSONTypes +extension BSON +{ + /// A BSON list. The backing storage of this type is opaque, + /// permitting lazy parsing of its inline content. + @frozen public + struct ListView where Bytes:RandomAccessCollection + { + public + let document:BSON.DocumentView + + @inlinable public + init(slice:Bytes) + { + self.document = .init(slice: slice) + } + } +} +extension BSON.ListView:Equatable +{ + /// Performs an exact byte-wise comparison on two lists. + /// Does not parse or validate the operands. + @inlinable public static + func == (lhs:Self, rhs:BSON.ListView>) -> Bool + { + lhs.document == rhs.document + } +} +extension BSON.ListView:Sendable where Bytes:Sendable +{ +} +extension BSON.ListView:BSON.FrameTraversable +{ + public + typealias Frame = BSON.DocumentFrame + + /// Stores the argument in ``slice`` unchanged. Equivalent to ``init(slice:)``. + /// + /// > Complexity: O(1) + @inlinable public + init(slicing bytes:Bytes) + { + self.init(slice: bytes) + } +} +extension BSON.ListView:BSON.FrameView +{ + @inlinable public + init(_ value:BSON.AnyValue) throws + { + self = try value.cast(with: \.list) + } +} +extension BSON.ListView +{ + /// The raw data backing this list. This collection *does not* + /// include the trailing null byte that appears after its inline + /// elements list. + @inlinable public + var slice:Bytes { self.document.slice } + + /// Indicates if this list contains no elements. + @inlinable public + var isEmpty:Bool { self.slice.isEmpty } + + /// The length that would be encoded in this list’s prefixed header. + /// Equal to ``size``. + @inlinable public + var header:Int32 { .init(self.size) } + + /// The size of this list when encoded with its header and trailing + /// null byte. This *is* the same as the length encoded in the header + /// itself. + @inlinable public + var size:Int { 5 + self.slice.count } +} extension BSON.ListView<[UInt8]> { @@ -12,10 +86,11 @@ extension BSON.ListView<[UInt8]> self.init(slice: list.bytes) } } + extension BSON.ListView { /// Parses this list into key-value pairs in order, yielding each value to the - /// provided closure. Parsing a list is slightly faster than parsing a general + /// provided closure. Parsing a list is slightly faster than parsing a general /// ``DocumentView``, because this method ignores the document keys. /// /// This method does *not* perform any key validation. @@ -31,7 +106,7 @@ extension BSON.ListView var input:BSON.Input = .init(self.slice) while let code:UInt8 = input.next() { - let type:BSON = try .init(code: code) + let type:BSON.AnyType = try .init(code: code) try input.parse(through: 0x00) try decode(try input.parse(variant: type)) } @@ -48,7 +123,7 @@ extension BSON.ListView return elements } /// Splits this list’s inline key-value pairs into an array containing the - /// values only. Parsing a list is slightly faster than parsing a general + /// values only. Parsing a list is slightly faster than parsing a general /// ``DocumentView``, because this method ignores the document keys. /// /// This method does *not* perform any key validation. @@ -82,7 +157,7 @@ extension BSON.ListView:ExpressibleByArrayLiteral self.init(slice: document.slice) } - @inlinable public + @inlinable public init(arrayLiteral:BSON.AnyValue...) { self.init(elements: arrayLiteral) diff --git a/Sources/BSONTypes/BSON.UTF8Frame.swift b/Sources/BSONABI/Views/BSON.UTF8Frame.swift similarity index 86% rename from Sources/BSONTypes/BSON.UTF8Frame.swift rename to Sources/BSONABI/Views/BSON.UTF8Frame.swift index 478d2b11..8166a4dd 100644 --- a/Sources/BSONTypes/BSON.UTF8Frame.swift +++ b/Sources/BSONABI/Views/BSON.UTF8Frame.swift @@ -1,10 +1,8 @@ -import BSONTraversal - extension BSON { /// Specifies the interpretation of a length header attached to UTF-8 ``string``. @frozen public - enum UTF8Frame:VariableLengthBSONFrame + enum UTF8Frame:FrameType { /// A UTF-8 string’s length header does not count its own length. @inlinable public static diff --git a/Sources/BSONTypes/BSON.UTF8View.swift b/Sources/BSONABI/Views/BSON.UTF8View.swift similarity index 93% rename from Sources/BSONTypes/BSON.UTF8View.swift rename to Sources/BSONABI/Views/BSON.UTF8View.swift index 0bd6f428..b83b8a7b 100644 --- a/Sources/BSONTypes/BSON.UTF8View.swift +++ b/Sources/BSONABI/Views/BSON.UTF8View.swift @@ -1,12 +1,10 @@ -import BSONTraversal - extension BSON { /// A BSON UTF-8 string. This string is allowed to contain null bytes. /// /// This type can wrap potentially-invalid UTF-8 data, therefore it /// is not backed by an instance of ``String``. Moreover, it (and not ``String``) - /// is the payload of ``BSON.Value.string(_:)`` to ensure that long string + /// is the payload of ``BSON/AnyValue.string(_:)`` to ensure that long string /// fields can be traversed in constant time. /// /// To convert a UTF-8 string to a native Swift ``String`` (repairing invalid UTF-8), @@ -17,7 +15,7 @@ extension BSON /// The UTF-8 code units backing this string. This collection does *not* /// include the trailing null byte that typically appears when this value /// occurs inline in a document. - public + public let slice:Bytes @inlinable public @@ -45,7 +43,7 @@ extension BSON.UTF8View where Bytes:RangeReplaceableCollection } } extension BSON.UTF8View:ExpressibleByStringLiteral, - ExpressibleByExtendedGraphemeClusterLiteral, + ExpressibleByExtendedGraphemeClusterLiteral, ExpressibleByUnicodeScalarLiteral { @inlinable public @@ -119,7 +117,7 @@ extension BSON.UTF8View:CustomStringConvertible .init(bson: self) } } -extension BSON.UTF8View:VariableLengthBSON where Bytes:RandomAccessCollection +extension BSON.UTF8View:BSON.FrameTraversable where Bytes:RandomAccessCollection { public typealias Frame = BSON.UTF8Frame @@ -133,7 +131,7 @@ extension BSON.UTF8View:VariableLengthBSON where Bytes:RandomAccessCollection +extension BSON.UTF8View:BSON.FrameView where Bytes:RandomAccessCollection { @inlinable public init(_ value:BSON.AnyValue) throws diff --git a/Sources/BSONDecoding/Containers/BSON.ExplicitField.swift b/Sources/BSONDecoding/BSON.FieldDecoder.swift similarity index 73% rename from Sources/BSONDecoding/Containers/BSON.ExplicitField.swift rename to Sources/BSONDecoding/BSON.FieldDecoder.swift index f949b0a8..18d7c6fa 100644 --- a/Sources/BSONDecoding/Containers/BSON.ExplicitField.swift +++ b/Sources/BSONDecoding/BSON.FieldDecoder.swift @@ -1,7 +1,13 @@ extension BSON +{ + @available(*, deprecated, renamed: "FieldDecoder") + public + typealias ExplicitField = FieldDecoder +} +extension BSON { @frozen public - struct ExplicitField where Bytes:RandomAccessCollection, Key:Sendable + struct FieldDecoder where Bytes:RandomAccessCollection, Key:Sendable { public let key:Key @@ -16,7 +22,7 @@ extension BSON } } } -extension BSON.ExplicitField:BSONScope +extension BSON.FieldDecoder:BSON.TraceableDecoder { /// Decodes the value of this field with the given decoder. /// Throws a ``BSON/DecodingError`` wrapping the underlying diff --git a/Sources/BSONDecoding/Containers/BSON.ImplicitField.swift b/Sources/BSONDecoding/BSON.OptionalDecoder.swift similarity index 81% rename from Sources/BSONDecoding/Containers/BSON.ImplicitField.swift rename to Sources/BSONDecoding/BSON.OptionalDecoder.swift index 5196dc81..499aa7f0 100644 --- a/Sources/BSONDecoding/Containers/BSON.ImplicitField.swift +++ b/Sources/BSONDecoding/BSON.OptionalDecoder.swift @@ -1,11 +1,17 @@ extension BSON +{ + @available(*, deprecated, renamed: "OptionalDecoder") + public + typealias ImplicitField = OptionalDecoder +} +extension BSON { /// A field that may or may not exist in a document. This type is /// the return value of ``Dictionary``’s non-optional subscript, and /// is useful for obtaining structured diagnostics for “key-not-found” /// scenarios. @frozen public - struct ImplicitField where Bytes:RandomAccessCollection, Key:Sendable + struct OptionalDecoder where Bytes:RandomAccessCollection, Key:Sendable { public let key:Key @@ -20,25 +26,25 @@ extension BSON } } } -extension BSON.ImplicitField +extension BSON.OptionalDecoder { @inlinable public static func ?? (lhs:Self, rhs:@autoclosure () -> Self) -> Self { if case nil = lhs.value { - return rhs() + rhs() } else { - return lhs + lhs } } } -extension BSON.ImplicitField +extension BSON.OptionalDecoder { /// Gets the value of this key, throwing a ``BSON.DocumentKeyError`` - /// if it is [`nil`](). This is a distinct condition from an explicit + /// if it is nil. This is a distinct condition from an explicit /// ``BSON.null`` value, which will be returned without throwing an error. @inlinable public func decode() throws -> BSON.AnyValue @@ -53,7 +59,7 @@ extension BSON.ImplicitField } } } -extension BSON.ImplicitField:BSONScope +extension BSON.OptionalDecoder:BSON.TraceableDecoder { /// Decodes the value of this implicit field with the given decoder, throwing a /// ``BSON.DocumentKeyError`` if it does not exist. Throws a diff --git a/Sources/BSONDecoding/Decoding/BSONScope.swift b/Sources/BSONDecoding/BSON.TraceableDecoder.swift similarity index 85% rename from Sources/BSONDecoding/Decoding/BSONScope.swift rename to Sources/BSONDecoding/BSON.TraceableDecoder.swift index 79717e7b..6756eece 100644 --- a/Sources/BSONDecoding/Decoding/BSONScope.swift +++ b/Sources/BSONDecoding/BSON.TraceableDecoder.swift @@ -1,6 +1,13 @@ -/// A type that represents a scope for decoding operations. +extension BSON +{ + /// A type that represents a scope for decoding operations. + public + typealias TraceableDecoder = _BSONTraceableDecoder +} + +/// The name of this protocol is ``BSON.TraceableDecoder``. public -protocol BSONScope +protocol _BSONTraceableDecoder { associatedtype Bytes:RandomAccessCollection @@ -9,7 +16,8 @@ protocol BSONScope /// should annotate the error with appropriate context and re-throw it. func decode(with decode:(BSON.AnyValue) throws -> T) throws -> T } -extension BSONScope + +extension BSON.TraceableDecoder { @inlinable public func decode(using _:CodingKey.Type = CodingKey.self, @@ -31,7 +39,7 @@ extension BSONScope // We should probably replace this with a dedicated API for binary subschema. @inlinable public func decode(as _:View.Type, - with decode:(View) throws -> T) throws -> T where View:BSONView + with decode:(View) throws -> T) throws -> T where View:BSON.FrameView { try self.decode { try decode(try .init($0)) } } diff --git a/Sources/BSONDecoding/Containers/BSON.DocumentView (ext).swift b/Sources/BSONDecoding/Containers/BSON.DocumentView (ext).swift index 62e6bb2e..6d4321de 100644 --- a/Sources/BSONDecoding/Containers/BSON.DocumentView (ext).swift +++ b/Sources/BSONDecoding/Containers/BSON.DocumentView (ext).swift @@ -1,11 +1,10 @@ extension BSON.DocumentView { - /// @import(BSONView) /// Decorates the ``BSON.AnyValue``-yielding overload of this method with one that /// yields the key-value pairs as fields. @inlinable public func parse( - to decode:(_ field:BSON.ExplicitField) throws -> ()) throws + to decode:(_ field:BSON.FieldDecoder) throws -> ()) throws { try self.parse { diff --git a/Sources/BSONDecoding/Containers/BSON.ListView (ext).swift b/Sources/BSONDecoding/Containers/BSON.ListView (ext).swift index 466d7def..11c0f0bf 100644 --- a/Sources/BSONDecoding/Containers/BSON.ListView (ext).swift +++ b/Sources/BSONDecoding/Containers/BSON.ListView (ext).swift @@ -1,11 +1,10 @@ extension BSON.ListView { - /// @import(BSONView) /// Decorates the ``BSON.AnyValue``-yielding overload of this method with one that /// enumerates the elements and yields them as fields. @inlinable public func parse( - to decode:(_ field:BSON.ExplicitField) throws -> ()) throws + to decode:(_ field:BSON.FieldDecoder) throws -> ()) throws { var index:Int = 0 try self.parse diff --git a/Sources/BSONDecoding/Decodability/BSONBinaryViewDecodable.swift b/Sources/BSONDecoding/Decodability/BSONBinaryViewDecodable.swift index 68c5899e..aec582ce 100644 --- a/Sources/BSONDecoding/Decodability/BSONBinaryViewDecodable.swift +++ b/Sources/BSONDecoding/Decodability/BSONBinaryViewDecodable.swift @@ -9,7 +9,7 @@ protocol BSONBinaryViewDecodable:BSONDecodable extension BSONBinaryViewDecodable { /// Attempts to cast the given variant value to a binary array, and then - /// delegates to this type’s ``init(bson:)`` witness. + /// delegates to this type’s ``init(bson:) [56R02]`` witness. @inlinable public init(bson:BSON.AnyValue>) throws { diff --git a/Sources/BSONDecoding/Decodability/BSONDecodable.swift b/Sources/BSONDecoding/Decodability/BSONDecodable.swift index e15a9382..239f9c67 100644 --- a/Sources/BSONDecoding/Decodability/BSONDecodable.swift +++ b/Sources/BSONDecoding/Decodability/BSONDecodable.swift @@ -1,9 +1,10 @@ +import BSONABI /// A type that can be decoded from a BSON variant value backed by /// some type of storage not particular to the decoded type. /// -/// This protocol is parallel and unrelated to ``BSONDecodableView`` to +/// This protocol is parallel and unrelated to ``BSON.FrameView`` to /// emphasize the performance characteristics of types that conform to -/// this protocol and not ``BSONDecodableView``. +/// this protocol and not ``BSON.FrameView``. public protocol BSONDecodable { diff --git a/Sources/BSONDecoding/Decodability/BSONStringDecodable.swift b/Sources/BSONDecoding/Decodability/BSONStringDecodable.swift index 96cdf67a..e0e3a145 100644 --- a/Sources/BSONDecoding/Decodability/BSONStringDecodable.swift +++ b/Sources/BSONDecoding/Decodability/BSONStringDecodable.swift @@ -14,7 +14,7 @@ protocol BSONStringDecodable:BSONDecodable extension BSONStringDecodable { /// Attempts to cast the given variant value to a string, and then - /// delegates to this type’s ``init(bson:)`` witness. + /// delegates to this type’s ``init(bson:) [6DO67]`` witness. @inlinable public init(bson:BSON.AnyValue>) throws { diff --git a/Sources/BSONDecoding/Decoding/BSON.DecodingError.swift b/Sources/BSONDecoding/Decoding/BSON.DecodingError.swift index 49b39cc9..90101b99 100644 --- a/Sources/BSONDecoding/Decoding/BSON.DecodingError.swift +++ b/Sources/BSONDecoding/Decoding/BSON.DecodingError.swift @@ -24,8 +24,8 @@ extension BSON extension BSON.DecodingError:Equatable where Location:Equatable { /// Compares the ``location`` properties and the ``underlying`` - /// errors of the operands for equality, returning [`true`]() - /// if they are equal. Always returns [`false`]() if (any of) + /// errors of the operands for equality, returning `true` + /// if they are equal. Always returns `false` if (any of) /// the underlying ``Error`` existentials are not ``Equatable``. public static func == (lhs:Self, rhs:Self) -> Bool diff --git a/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.Iterator.swift b/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.Iterator.swift index a0f1c660..6e7832e5 100644 --- a/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.Iterator.swift +++ b/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.Iterator.swift @@ -16,7 +16,7 @@ extension BSON.DocumentDecoder extension BSON.DocumentDecoder.Iterator:IteratorProtocol { @inlinable public mutating - func next() -> BSON.ExplicitField? + func next() -> BSON.FieldDecoder? { self.base.next().map { .init(key: $0.key, value: $0.value) } } diff --git a/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.swift b/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.swift index f71cca17..bbc43317 100644 --- a/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.swift +++ b/Sources/BSONDecoding/Decoding/BSON.DocumentDecoder.swift @@ -19,13 +19,13 @@ extension BSON } } } -extension BSON.DocumentDecoder:BSONDecoder +extension BSON.DocumentDecoder:BSON.Decoder { /// Attempts to load a document decoder from the given variant. /// /// - Returns: /// A document decoder derived from the payload of this variant if it matches - /// ``case document(_:)`` or ``case list(_:)``, [`nil`]() otherwise. + /// ``document(_:)`` **or** ``list(_:)``, nil otherwise. @inlinable public init(parsing bson:__shared BSON.AnyValue) throws { @@ -53,7 +53,7 @@ extension BSON.DocumentDecoder /// comparison would drop one of the values. /// /// To get a plain array of key-value pairs with no decoding interface, call the - /// document view’s ``BSON.DocumentView parse()`` method instead. + /// document view’s ``BSON.DocumentView/parse()`` method instead. /// /// > Complexity: /// O(*n*), where *n* is the number of fields in the source document. @@ -94,10 +94,10 @@ extension BSON.DocumentDecoder:Sequence extension BSON.DocumentDecoder { @inlinable public __consuming - func single() throws -> BSON.ExplicitField + func single() throws -> BSON.FieldDecoder { - var single:BSON.ExplicitField? = nil - for field:BSON.ExplicitField in self + var single:BSON.FieldDecoder? = nil + for field:BSON.FieldDecoder in self { if case nil = single { @@ -117,13 +117,13 @@ extension BSON.DocumentDecoder } @inlinable public - subscript(key:CodingKey) -> BSON.ExplicitField? + subscript(key:CodingKey) -> BSON.OptionalDecoder { - self.index[key].map { .init(key: key, value: $0) } + .init(key: key, value: self.index[key]) } @inlinable public - subscript(key:CodingKey) -> BSON.ImplicitField + subscript(key:CodingKey) -> BSON.FieldDecoder? { - .init(key: key, value: self.index[key]) + self.index[key].map { .init(key: key, value: $0) } } } diff --git a/Sources/BSONDecoding/Decoding/BSON.DocumentKeyError.swift b/Sources/BSONDecoding/Decoding/BSON.DocumentKeyError.swift index 777974e9..8c230556 100644 --- a/Sources/BSONDecoding/Decoding/BSON.DocumentKeyError.swift +++ b/Sources/BSONDecoding/Decoding/BSON.DocumentKeyError.swift @@ -32,9 +32,9 @@ extension BSON.DocumentKeyError:NamedError switch self { case .duplicate(let key): - return "duplicate key '\(key)'" + "duplicate key '\(key)'" case .undefined(let key): - return "undefined key '\(key)'" + "undefined key '\(key)'" } } } diff --git a/Sources/BSONDecoding/Decoding/BSON.ListDecoder.swift b/Sources/BSONDecoding/Decoding/BSON.ListDecoder.swift index 85ae7583..103f441e 100644 --- a/Sources/BSONDecoding/Decoding/BSON.ListDecoder.swift +++ b/Sources/BSONDecoding/Decoding/BSON.ListDecoder.swift @@ -15,7 +15,7 @@ extension BSON } } } -extension BSON.ListDecoder:BSONDecoder +extension BSON.ListDecoder:BSON.Decoder { /// Attempts to unwrap and parse an array-decoder from the given variant. /// @@ -24,7 +24,7 @@ extension BSON.ListDecoder:BSONDecoder /// /// - Returns: /// The payload of the variant, parsed to a list decoder, if it matches - /// ``case list(_:)`` and could be successfully parsed, [`nil`]() otherwise. + /// ``case list(_:)`` and could be successfully parsed, nil otherwise. /// /// > Complexity: // O(*n*), where *n* is the number of elements in the source list. @@ -70,7 +70,7 @@ extension BSON.ListDecoder:RandomAccessCollection self.elements.endIndex } @inlinable public - subscript(index:Int) -> BSON.ExplicitField + subscript(index:Int) -> BSON.FieldDecoder { .init(key: index, value: self.elements[index]) } diff --git a/Sources/BSONDecoding/Decoding/BSON.ShapeError.swift b/Sources/BSONDecoding/Decoding/BSON.ShapeError.swift index 9b04c355..025c15fd 100644 --- a/Sources/BSONDecoding/Decoding/BSON.ShapeError.swift +++ b/Sources/BSONDecoding/Decoding/BSON.ShapeError.swift @@ -26,13 +26,13 @@ extension BSON.ShapeError:CustomStringConvertible switch self.expected { case nil: - return "Invalid length (\(self.length))." + "Invalid length (\(self.length))." case .length(let length)?: - return "Invalid length (\(self.length)), expected \(length) elements." + "Invalid length (\(self.length)), expected \(length) elements." case .multiple(of: let stride)?: - return "Invalid length (\(self.length)), expected multiple of \(stride)." + "Invalid length (\(self.length)), expected multiple of \(stride)." } } } diff --git a/Sources/BSONDecoding/Decoding/BSON.SingleKeyError.swift b/Sources/BSONDecoding/Decoding/BSON.SingleKeyError.swift index 0b589b15..454ac834 100644 --- a/Sources/BSONDecoding/Decoding/BSON.SingleKeyError.swift +++ b/Sources/BSONDecoding/Decoding/BSON.SingleKeyError.swift @@ -26,9 +26,9 @@ extension BSON.SingleKeyError:NamedError switch self { case .none: - return "no keys in single-field document" + "no keys in single-field document" case .multiple: - return "multiple keys in single-field document" + "multiple keys in single-field document" } } } diff --git a/Sources/BSONDecoding/Dictionary (ext).swift b/Sources/BSONDecoding/Dictionary (ext).swift index cf1dbaf4..369e3340 100644 --- a/Sources/BSONDecoding/Dictionary (ext).swift +++ b/Sources/BSONDecoding/Dictionary (ext).swift @@ -10,7 +10,7 @@ extension Dictionary:BSONDocumentViewDecodable, BSONDecodable self.init() try bson.parse { - (field:BSON.ExplicitField) in + (field:BSON.FieldDecoder) in if case _? = self.updateValue(try field.decode(to: Value.self), forKey: field.key.rawValue) diff --git a/Sources/BSONDecoding/Extensions/Double (ext).swift b/Sources/BSONDecoding/Extensions/Double (ext).swift index 0b69cef5..d5dfb02c 100644 --- a/Sources/BSONDecoding/Extensions/Double (ext).swift +++ b/Sources/BSONDecoding/Extensions/Double (ext).swift @@ -1,3 +1,3 @@ extension Double:BSONDecodable { -} \ No newline at end of file +} diff --git a/Sources/BSONDecoding/Legacy/BSON.KeyedDecoder.swift b/Sources/BSONDecoding/Legacy/BSON.KeyedDecoder.swift index 481cc1d8..e65ef100 100644 --- a/Sources/BSONDecoding/Legacy/BSON.KeyedDecoder.swift +++ b/Sources/BSONDecoding/Legacy/BSON.KeyedDecoder.swift @@ -66,7 +66,7 @@ extension BSON.KeyedDecoder:KeyedDecodingContainerProtocol public func decode(_:T.Type, forKey key:Key) throws -> T where T:Decodable { - return try .init(from: try self.singleValueContainer(forKey: key)) + try .init(from: try self.singleValueContainer(forKey: key)) } func decodeNil(forKey key:Key) throws -> Bool { diff --git a/Sources/BSONDecoding/README.md b/Sources/BSONDecoding/README.md new file mode 100644 index 00000000..cd4aae9f --- /dev/null +++ b/Sources/BSONDecoding/README.md @@ -0,0 +1 @@ +# ``/BSONDecoding`` diff --git a/Sources/BSONDecoding/exports.swift b/Sources/BSONDecoding/exports.swift index 16493bb2..39f8f8b5 100644 --- a/Sources/BSONDecoding/exports.swift +++ b/Sources/BSONDecoding/exports.swift @@ -1 +1 @@ -@_exported import BSON +@_exported import enum BSONABI.BSON diff --git a/Sources/BSONDecodingTests/Main.DecodeBinary.swift b/Sources/BSONDecodingTests/Main.DecodeBinary.swift new file mode 100644 index 00000000..1b25951e --- /dev/null +++ b/Sources/BSONDecodingTests/Main.DecodeBinary.swift @@ -0,0 +1,37 @@ +import BSONDecoding +import Testing + +extension Main +{ + enum DecodeBinary + { + } +} +extension Main.DecodeBinary:TestBattery +{ + static + func run(tests:TestGroup) + { + if let tests:TestGroup = tests / "md5" + { + let md5:BSON.BinaryView> = .init(subtype: .md5, + slice: [0xff, 0xfe, 0xfd]) + let bson:BSON.DocumentView> = + [ + "md5": .binary(md5), + ] + + tests.do + { + let bson:BSON.DocumentDecoder> = try .init( + parsing: bson) + let decoded:BSON.BinaryView> = try bson["md5"].decode( + as: BSON.BinaryView>.self) + { + $0 + } + tests.expect(true: md5 == decoded) + } + } + } +} diff --git a/Sources/BSONDecodingTests/Main.DecodeDocument.swift b/Sources/BSONDecodingTests/Main.DecodeDocument.swift new file mode 100644 index 00000000..200c66a8 --- /dev/null +++ b/Sources/BSONDecodingTests/Main.DecodeDocument.swift @@ -0,0 +1,100 @@ +import BSONDecoding +import Testing + +extension Main +{ + enum DecodeDocument + { + } +} +extension Main.DecodeDocument:TestBattery +{ + static + func run(tests:TestGroup) + { + + let degenerate:BSON.DocumentView<[UInt8]> = + [ + "present": .null, + "present": true, + ] + let bson:BSON.DocumentView<[UInt8]> = + [ + "present": .null, + "inhabited": true, + ] + + Self.run(tests / "key-not-unique", bson: degenerate, + catching: BSON.DocumentKeyError.duplicate("present")) + { + try $0["not-present"].decode(to: Bool.self) + } + + Self.run(tests / "key-not-present", bson: bson, + catching: BSON.DocumentKeyError.undefined("not-present")) + { + try $0["not-present"].decode(to: Bool.self) + } + + Self.run(tests / "key-matching", bson: bson, + to: true) + { + try $0["inhabited"].decode(to: Bool.self) + } + + Self.run(tests / "key-not-matching", bson: bson, + catching: BSON.DecodingError.init( + BSON.TypecastError>>.init(invalid: .bool), + in: "inhabited")) + { + try $0["inhabited"].decode(to: String.self) + } + + Self.run(tests / "key-not-matching-inhabited", bson: bson, + catching: BSON.DecodingError.init( + BSON.TypecastError.init(invalid: .null), + in: "present")) + { + try $0["present"].decode(to: Bool.self) + } + + Self.run(tests / "key-inhabited", bson: bson, + to: .some(true)) + { + try $0["inhabited"].decode(to: Bool?.self) + } + + Self.run(tests / "key-null", bson: bson, + to: nil) + { + try $0["present"].decode(to: Bool?.self) + } + + Self.run(tests / "key-optional", bson: bson, + to: nil) + { + try $0["not-present"]?.decode(to: Bool.self) + } + + Self.run(tests / "key-optional-null", bson: bson, + to: .some(.none)) + { + try $0["present"]?.decode(to: Bool?.self) + } + + Self.run(tests / "key-optional-inhabited", bson: bson, + to: .some(.some(true))) + { + try $0["inhabited"]?.decode(to: Bool?.self) + } + + // should throw an error instead of returning nil. + Self.run(tests / "key-optional-not-inhabited", bson: bson, + catching: BSON.DecodingError.init( + BSON.TypecastError.init(invalid: .null), + in: "present")) + { + try $0["present"]?.decode(to: Bool.self) + } + } +} diff --git a/Sources/BSONDecodingTests/Main.DecodeList.swift b/Sources/BSONDecodingTests/Main.DecodeList.swift new file mode 100644 index 00000000..425e1939 --- /dev/null +++ b/Sources/BSONDecodingTests/Main.DecodeList.swift @@ -0,0 +1,119 @@ +import BSONDecoding +import Testing + +extension Main +{ + enum DecodeList + { + } +} +extension Main.DecodeList:TestBattery +{ + static + func run(tests:TestGroup) + { + let bson:BSON.DocumentView<[UInt8]> = + [ + "none": [], + "two": ["a", "b"], + "three": ["a", "b", "c"], + "four": ["a", "b", "c", "d"], + + "heterogenous": ["a", "b", 0, "d"], + ] + + Self.run(tests / "none-to-two", bson: bson, + catching: BSON.DecodingError.init( + BSON.ShapeError.init(invalid: 0, expected: .length(2)), + in: "none")) + { + try $0["none"].decode + { + try $0.shape.expect(length: 2) + } + } + + Self.run(tests / "two-to-two", bson: bson, + to: ["a", "b"]) + { + try $0["two"].decode + { + try $0.shape.expect(length: 2) + return try $0.map { try $0.decode(to: String.self) } + } + } + + Self.run(tests / "three-to-two", bson: bson, + catching: BSON.DecodingError.init( + BSON.ShapeError.init(invalid: 3, expected: .length(2)), + in: "three")) + { + try $0["three"].decode + { + try $0.shape.expect(length: 2) + } + } + + Self.run(tests / "three-by-two", bson: bson, + catching: BSON.DecodingError.init( + BSON.ShapeError.init(invalid: 3, expected: .multiple(of: 2)), + in: "three")) + { + try $0["three"].decode + { + try $0.shape.expect(multipleOf: 2) + } + } + + Self.run(tests / "four-by-two", bson: bson, + to: ["a", "b", "c", "d"]) + { + try $0["four"].decode + { + try $0.shape.expect(multipleOf: 2) + return try $0.map { try $0.decode(to: String.self) } + } + } + + Self.run(tests / "map", bson: bson, + to: ["a", "b", "c", "d"]) + { + try $0["four"].decode(to: [String].self) + } + + Self.run(tests / "map-invalid", bson: bson, + catching: BSON.DecodingError.init( + BSON.DecodingError.init( + BSON.TypecastError>>.init( + invalid: .int32), + in: 2), + in: "heterogenous")) + { + try $0["heterogenous"].decode(to: [String].self) + } + + Self.run(tests / "element", bson: bson, to: "c") + { + try $0["four"].decode + { + try $0.shape.expect { 2 < $0 } + return try $0[2].decode(to: String.self) + } + } + + Self.run(tests / "element-invalid", bson: bson, + catching: BSON.DecodingError.init( + BSON.DecodingError.init( + BSON.TypecastError>>.init( + invalid: .int32), + in: 2), + in: "heterogenous")) + { + try $0["heterogenous"].decode + { + try $0.shape.expect { 2 < $0 } + return try $0[2].decode(to: String.self) + } + } + } +} diff --git a/Sources/BSONDecodingTests/Main.DecodeNumeric.swift b/Sources/BSONDecodingTests/Main.DecodeNumeric.swift new file mode 100644 index 00000000..7285cd6c --- /dev/null +++ b/Sources/BSONDecodingTests/Main.DecodeNumeric.swift @@ -0,0 +1,53 @@ +import BSONDecoding +import Testing + +extension Main +{ + enum DecodeNumeric + { + } +} +extension Main.DecodeNumeric:TestBattery +{ + static + func run(tests:TestGroup) + { + let bson:BSON.DocumentView<[UInt8]> = + [ + "int32": .int32(0x7fff_ffff), + "int64": .int64(0x7fff_ffff_ffff_ffff), + "uint64": .uint64(0x7fff_ffff_ffff_ffff), + ] + + Self.run(tests / "int32-to-uint8", bson: bson, + catching: BSON.DecodingError.init( + BSON.IntegerOverflowError.int32(0x7fff_ffff), + in: "int32")) + { + try $0["int32"].decode(to: UInt8.self) + } + + Self.run(tests / "int32-to-int32", bson: bson, + to: 0x7fff_ffff) + { + try $0["int32"].decode(to: Int32.self) + } + + Self.run(tests / "int32-to-int", bson: bson, + to: 0x7fff_ffff) + { + try $0["int32"].decode(to: Int.self) + } + + Self.run(tests / "int64-to-int", bson: bson, + to: 0x7fff_ffff_ffff_ffff) + { + try $0["int64"].decode(to: Int.self) + } + Self.run(tests / "uint64-to-int", bson: bson, + to: 0x7fff_ffff_ffff_ffff) + { + try $0["uint64"].decode(to: Int.self) + } + } +} diff --git a/Sources/BSONDecodingTests/Main.DecodeString.swift b/Sources/BSONDecodingTests/Main.DecodeString.swift new file mode 100644 index 00000000..1f7bbe27 --- /dev/null +++ b/Sources/BSONDecodingTests/Main.DecodeString.swift @@ -0,0 +1,61 @@ +import BSONDecoding +import Testing + +extension Main +{ + enum DecodeString + { + } +} +extension Main.DecodeString:TestBattery +{ + static + func run(tests:TestGroup) + { + let bson:BSON.DocumentView<[UInt8]> = + [ + "string": "e\u{0301}e\u{0301}", + "character": "e\u{0301}", + "unicode-scalar": "e", + ] + + Self.run(tests / "unicode-scalar-from-string", bson: bson, + catching: BSON.DecodingError.init( + BSON.ValueError.init(invalid: "e\u{0301}e\u{0301}"), + in: "string")) + { + try $0["string"].decode(to: Unicode.Scalar.self) + } + Self.run(tests / "unicode-scalar-from-character", bson: bson, + catching: BSON.DecodingError.init( + BSON.ValueError.init(invalid: "e\u{0301}"), + in: "character")) + { + try $0["character"].decode(to: Unicode.Scalar.self) + } + Self.run(tests / "unicode-scalar", bson: bson, + to: "e") + { + try $0["unicode-scalar"].decode(to: Unicode.Scalar.self) + } + + Self.run(tests / "character-from-string", bson: bson, + catching: BSON.DecodingError.init( + BSON.ValueError.init(invalid: "e\u{0301}e\u{0301}"), + in: "string")) + { + try $0["string"].decode(to: Character.self) + } + Self.run(tests / "character", bson: bson, + to: "e\u{0301}") + { + try $0["character"].decode(to: Character.self) + } + + Self.run(tests / "string", bson: bson, + to: "e\u{0301}e\u{0301}") + { + try $0["string"].decode(to: String.self) + } + } +} diff --git a/Sources/BSONDecodingTests/Main.DecodeVoid.swift b/Sources/BSONDecodingTests/Main.DecodeVoid.swift new file mode 100644 index 00000000..92cb547d --- /dev/null +++ b/Sources/BSONDecodingTests/Main.DecodeVoid.swift @@ -0,0 +1,37 @@ +import BSONDecoding +import Testing + +extension Main +{ + enum DecodeVoid + { + } +} +extension Main.DecodeVoid:TestBattery +{ + static + func run(tests:TestGroup) + { + let bson:BSON.DocumentView<[UInt8]> = + [ + "null": .null, + "max": .max, + "min": .min, + ] + + (tests / "null")?.do + { + let bson:BSON.DocumentDecoder = try .init( + parsing: bson) + let _:Never? = try bson["null"].decode(to: Never?.self) + } + Self.run(tests / "max", bson: bson, to: .init()) + { + try $0["max"].decode(to: BSON.Max.self) + } + Self.run(tests / "min", bson: bson, to: .init()) + { + try $0["min"].decode(to: BSON.Min.self) + } + } +} diff --git a/Sources/BSONDecodingTests/Main.swift b/Sources/BSONDecodingTests/Main.swift index 8e02ec7c..3aa40f96 100644 --- a/Sources/BSONDecodingTests/Main.swift +++ b/Sources/BSONDecodingTests/Main.swift @@ -1,345 +1,16 @@ -import BSONDecoding import Testing @main -enum Main:SyncTests +enum Main:TestMain { static - func run(tests:Tests) - { - do - { - let bson:BSON.DocumentView<[UInt8]> = - [ - "null": .null, - "max": .max, - "min": .min, - ] - - (tests / "null")?.do - { - let bson:BSON.DocumentDecoder = try .init( - parsing: bson) - let _:Never? = try bson["null"].decode(to: Never?.self) - } - TestDecoding(tests / "max", bson: bson, to: .init()) - { - try $0["max"].decode(to: BSON.Max.self) - } - TestDecoding(tests / "min", bson: bson, to: .init()) - { - try $0["min"].decode(to: BSON.Min.self) - } - } - if let tests:TestGroup = tests / "numeric" - { - - let bson:BSON.DocumentView<[UInt8]> = - [ - "int32": .int32(0x7fff_ffff), - "int64": .int64(0x7fff_ffff_ffff_ffff), - "uint64": .uint64(0x7fff_ffff_ffff_ffff), - ] - - TestDecoding(tests / "int32-to-uint8", bson: bson, - catching: BSON.DecodingError.init( - BSON.IntegerOverflowError.int32(0x7fff_ffff), - in: "int32")) - { - try $0["int32"].decode(to: UInt8.self) - } - - TestDecoding(tests / "int32-to-int32", bson: bson, - to: 0x7fff_ffff) - { - try $0["int32"].decode(to: Int32.self) - } - - TestDecoding(tests / "int32-to-int", bson: bson, - to: 0x7fff_ffff) - { - try $0["int32"].decode(to: Int.self) - } - - TestDecoding(tests / "int64-to-int", bson: bson, - to: 0x7fff_ffff_ffff_ffff) - { - try $0["int64"].decode(to: Int.self) - } - TestDecoding(tests / "uint64-to-int", bson: bson, - to: 0x7fff_ffff_ffff_ffff) - { - try $0["uint64"].decode(to: Int.self) - } - } - - if let tests:TestGroup = tests / "tuple" - { - - let bson:BSON.DocumentView<[UInt8]> = - [ - "none": [], - "two": ["a", "b"], - "three": ["a", "b", "c"], - "four": ["a", "b", "c", "d"], - - "heterogenous": ["a", "b", 0, "d"], - ] - - TestDecoding(tests / "none-to-two", bson: bson, - catching: BSON.DecodingError.init( - BSON.ShapeError.init(invalid: 0, expected: .length(2)), - in: "none")) - { - try $0["none"].decode - { - try $0.shape.expect(length: 2) - } - } - - TestDecoding(tests / "two-to-two", bson: bson, - to: ["a", "b"]) - { - try $0["two"].decode - { - try $0.shape.expect(length: 2) - return try $0.map { try $0.decode(to: String.self) } - } - } - - TestDecoding(tests / "three-to-two", bson: bson, - catching: BSON.DecodingError.init( - BSON.ShapeError.init(invalid: 3, expected: .length(2)), - in: "three")) - { - try $0["three"].decode - { - try $0.shape.expect(length: 2) - } - } - - TestDecoding(tests / "three-by-two", bson: bson, - catching: BSON.DecodingError.init( - BSON.ShapeError.init(invalid: 3, expected: .multiple(of: 2)), - in: "three")) - { - try $0["three"].decode - { - try $0.shape.expect(multipleOf: 2) - } - } - - TestDecoding(tests / "four-by-two", bson: bson, - to: ["a", "b", "c", "d"]) - { - try $0["four"].decode - { - try $0.shape.expect(multipleOf: 2) - return try $0.map { try $0.decode(to: String.self) } - } - } - - TestDecoding(tests / "map", bson: bson, - to: ["a", "b", "c", "d"]) - { - try $0["four"].decode(to: [String].self) - } - - TestDecoding(tests / "map-invalid", bson: bson, - catching: BSON.DecodingError.init( - BSON.DecodingError.init( - BSON.TypecastError>>.init( - invalid: .int32), - in: 2), - in: "heterogenous")) - { - try $0["heterogenous"].decode(to: [String].self) - } - - TestDecoding(tests / "element", bson: bson, to: "c") - { - try $0["four"].decode - { - try $0.shape.expect { 2 < $0 } - return try $0[2].decode(to: String.self) - } - } - - TestDecoding(tests / "element-invalid", bson: bson, - catching: BSON.DecodingError.init( - BSON.DecodingError.init( - BSON.TypecastError>>.init( - invalid: .int32), - in: 2), - in: "heterogenous")) - { - try $0["heterogenous"].decode - { - try $0.shape.expect { 2 < $0 } - return try $0[2].decode(to: String.self) - } - } - } - - if let tests:TestGroup = tests / "document" - { - - let degenerate:BSON.DocumentView<[UInt8]> = - [ - "present": .null, - "present": true, - ] - let bson:BSON.DocumentView<[UInt8]> = - [ - "present": .null, - "inhabited": true, - ] - - TestDecoding(tests / "key-not-unique", bson: degenerate, - catching: BSON.DocumentKeyError.duplicate("present")) - { - try $0["not-present"].decode(to: Bool.self) - } - - TestDecoding(tests / "key-not-present", bson: bson, - catching: BSON.DocumentKeyError.undefined("not-present")) - { - try $0["not-present"].decode(to: Bool.self) - } - - TestDecoding(tests / "key-matching", bson: bson, - to: true) - { - try $0["inhabited"].decode(to: Bool.self) - } - - TestDecoding(tests / "key-not-matching", bson: bson, - catching: BSON.DecodingError.init( - BSON.TypecastError>>.init(invalid: .bool), - in: "inhabited")) - { - try $0["inhabited"].decode(to: String.self) - } - - TestDecoding(tests / "key-not-matching-inhabited", bson: bson, - catching: BSON.DecodingError.init( - BSON.TypecastError.init(invalid: .null), - in: "present")) - { - try $0["present"].decode(to: Bool.self) - } - - TestDecoding(tests / "key-inhabited", bson: bson, - to: .some(true)) - { - try $0["inhabited"].decode(to: Bool?.self) - } - - TestDecoding(tests / "key-null", bson: bson, - to: nil) - { - try $0["present"].decode(to: Bool?.self) - } - - TestDecoding(tests / "key-optional", bson: bson, - to: nil) - { - try $0["not-present"]?.decode(to: Bool.self) - } - - TestDecoding(tests / "key-optional-null", bson: bson, - to: .some(.none)) - { - try $0["present"]?.decode(to: Bool?.self) - } - - TestDecoding(tests / "key-optional-inhabited", bson: bson, - to: .some(.some(true))) - { - try $0["inhabited"]?.decode(to: Bool?.self) - } - - // should throw an error instead of returning [`nil`](). - TestDecoding(tests / "key-optional-not-inhabited", bson: bson, - catching: BSON.DecodingError.init( - BSON.TypecastError.init(invalid: .null), - in: "present")) - { - try $0["present"]?.decode(to: Bool.self) - } - } - - if let tests:TestGroup = tests / "binary" / "md5" - { - - let md5:BSON.BinaryView<[UInt8]> = .init(subtype: .md5, - slice: [0xff, 0xfe, 0xfd]) - let bson:BSON.DocumentView<[UInt8]> = - [ - "md5": .binary(md5), - ] - - tests.do - { - let bson:BSON.DocumentDecoder = try .init( - parsing: bson) - let decoded:BSON.BinaryView> = try bson["md5"].decode( - as: BSON.BinaryView>.self) - { - $0 - } - tests.expect(true: md5 == decoded) - } - } - - if let tests:TestGroup = tests / "losslessstringconvertible" - { - - let bson:BSON.DocumentView<[UInt8]> = - [ - "string": "e\u{0301}e\u{0301}", - "character": "e\u{0301}", - "unicode-scalar": "e", - ] - - TestDecoding(tests / "unicode-scalar-from-string", bson: bson, - catching: BSON.DecodingError.init( - BSON.ValueError.init(invalid: "e\u{0301}e\u{0301}"), - in: "string")) - { - try $0["string"].decode(to: Unicode.Scalar.self) - } - TestDecoding(tests / "unicode-scalar-from-character", bson: bson, - catching: BSON.DecodingError.init( - BSON.ValueError.init(invalid: "e\u{0301}"), - in: "character")) - { - try $0["character"].decode(to: Unicode.Scalar.self) - } - TestDecoding(tests / "unicode-scalar", bson: bson, - to: "e") - { - try $0["unicode-scalar"].decode(to: Unicode.Scalar.self) - } - - TestDecoding(tests / "character-from-string", bson: bson, - catching: BSON.DecodingError.init( - BSON.ValueError.init(invalid: "e\u{0301}e\u{0301}"), - in: "string")) - { - try $0["string"].decode(to: Character.self) - } - TestDecoding(tests / "character", bson: bson, - to: "e\u{0301}") - { - try $0["character"].decode(to: Character.self) - } - - TestDecoding(tests / "string", bson: bson, - to: "e\u{0301}e\u{0301}") - { - try $0["string"].decode(to: String.self) - } - } - } + let all:[any TestBattery.Type] = + [ + DecodeBinary.self, + DecodeDocument.self, + DecodeList.self, + DecodeNumeric.self, + DecodeString.self, + DecodeVoid.self, + ] } diff --git a/Sources/BSONDecodingTests/TestBattery (ext).swift b/Sources/BSONDecodingTests/TestBattery (ext).swift new file mode 100644 index 00000000..527529e9 --- /dev/null +++ b/Sources/BSONDecodingTests/TestBattery (ext).swift @@ -0,0 +1,32 @@ +import BSONDecoding +import Testing + +extension TestBattery +{ + static + func run(_ tests:TestGroup?, bson:BSON.DocumentView<[UInt8]>, + catching error:Failure, + with decode:(BSON.DocumentDecoder) throws -> Unexpected) + where Failure:Equatable & Error + { + tests?.do(catching: error) + { + _ = try decode(try .init(parsing: bson)) + } + } + static + func run(_ tests:TestGroup?, bson:BSON.DocumentView<[UInt8]>, + to expected:Decoded, + with decode:(BSON.DocumentDecoder) throws -> Decoded) + where Decoded:Equatable + { + if let tests:TestGroup + { + tests.do + { + let decoded:Decoded = try decode(try .init(parsing: bson)) + tests.expect(expected ==? decoded) + } + } + } +} diff --git a/Sources/BSONDecodingTests/TestDecoding.swift b/Sources/BSONDecodingTests/TestDecoding.swift deleted file mode 100644 index a0a9b153..00000000 --- a/Sources/BSONDecodingTests/TestDecoding.swift +++ /dev/null @@ -1,27 +0,0 @@ -import BSONDecoding -import Testing - -func TestDecoding(_ tests:TestGroup?, bson:BSON.DocumentView<[UInt8]>, - catching error:Failure, - with decode:(BSON.DocumentDecoder) throws -> Unexpected) - where Failure:Equatable & Error -{ - tests?.do(catching: error) - { - _ = try decode(try .init(parsing: bson)) - } -} -func TestDecoding(_ tests:TestGroup?, bson:BSON.DocumentView<[UInt8]>, - to expected:Decoded, - with decode:(BSON.DocumentDecoder) throws -> Decoded) - where Decoded:Equatable -{ - if let tests:TestGroup - { - tests.do - { - let decoded:Decoded = try decode(try .init(parsing: bson)) - tests.expect(expected ==? decoded) - } - } -} diff --git a/Sources/BSONEncoding/Dictionary (ext).swift b/Sources/BSONEncoding/Dictionary (ext).swift index d8b6957f..813e3933 100644 --- a/Sources/BSONEncoding/Dictionary (ext).swift +++ b/Sources/BSONEncoding/Dictionary (ext).swift @@ -4,7 +4,7 @@ extension [String: Never]:BSONEncodable { /// Encodes this dictionary as an empty document. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(document: .init(slice: [])) } diff --git a/Sources/BSONEncoding/Encodability/BSONDocumentEncodable.swift b/Sources/BSONEncoding/Encodability/BSONDocumentEncodable.swift index 01003b1a..c4375b0b 100644 --- a/Sources/BSONEncoding/Encodability/BSONDocumentEncodable.swift +++ b/Sources/BSONEncoding/Encodability/BSONDocumentEncodable.swift @@ -21,7 +21,7 @@ protocol BSONDocumentEncodable:BSONEncodable extension BSONDocumentEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.encode(to: &field[as: BSON.DocumentEncoder.self]) } diff --git a/Sources/BSONEncoding/Encodability/BSONEncodable.swift b/Sources/BSONEncoding/Encodability/BSONEncodable.swift index 871e0129..1532318f 100644 --- a/Sources/BSONEncoding/Encodability/BSONEncodable.swift +++ b/Sources/BSONEncoding/Encodability/BSONEncodable.swift @@ -1,22 +1,25 @@ +import BSONABI + /// A type that can be encoded to a BSON variant value. public protocol BSONEncodable { - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) } extension BSONEncodable where Self:BSONRepresentable, BSONRepresentation:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.bson.encode(to: &field) } } extension BSONEncodable where Self:RawRepresentable, RawValue:BSONEncodable { - /// Returns the ``encode(to:)`` witness of this type’s ``RawRepresentable.rawValue``. + /// Returns the ``encode(to:) [7NT06]`` witness of this type’s + /// ``RawRepresentable/rawValue``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.rawValue.encode(to: &field) } @@ -81,9 +84,9 @@ extension BSONEncodable where Self == BSON.List message: "UInt64 is not recommended for BSON that will be handled by MongoDB.") extension UInt64:BSONEncodable { - /// Encodes this integer as a value of type ``BSON.uint64``. + /// Encodes this integer as a value of type ``BSON.AnyType/uint64``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(uint64: self) } @@ -92,9 +95,9 @@ extension UInt64:BSONEncodable message: "UInt is not recommended for BSON that will be handled by MongoDB.") extension UInt:BSONEncodable { - /// Encodes this integer as a value of type ``BSON.uint64``. + /// Encodes this integer as a value of type ``BSON.AnyType/uint64``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(uint64: .init(self)) } diff --git a/Sources/BSONEncoding/Encodability/BSONListEncodable.swift b/Sources/BSONEncoding/Encodability/BSONListEncodable.swift index 085b26b0..4c0451a0 100644 --- a/Sources/BSONEncoding/Encodability/BSONListEncodable.swift +++ b/Sources/BSONEncoding/Encodability/BSONListEncodable.swift @@ -26,14 +26,14 @@ protocol BSONListEncodable:BSONEncodable extension BSONListEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.encode(to: &field[as: BSON.ListEncoder.self]) } } extension BSONListEncodable where Self:Sequence, Element:BSONEncodable { - /// Encodes this sequence as a value of type ``BSON.list``. + /// Encodes this sequence as a value of type ``BSON.AnyType/list``. @inlinable public func encode(to bson:inout BSON.ListEncoder) { diff --git a/Sources/BSONEncoding/Encodability/BSONStringEncodable.swift b/Sources/BSONEncoding/Encodability/BSONStringEncodable.swift index fe0d644a..60a060ea 100644 --- a/Sources/BSONEncoding/Encodability/BSONStringEncodable.swift +++ b/Sources/BSONEncoding/Encodability/BSONStringEncodable.swift @@ -16,7 +16,7 @@ protocol BSONStringEncodable:BSONEncodable { /// Converts an instance of this type to a string. This requirement /// restates its counterpart in ``LosslessStringConvertible`` if - /// [`Self`]() also conforms to it. + /// `Self` also conforms to it. var description:String { get } } extension BSONStringEncodable @@ -29,7 +29,7 @@ extension BSONStringEncodable /// who implement ``LosslessStringConvertible``, but expect to be /// encoded as something besides a UTF-8 string. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.description.encode(to: &field) } diff --git a/Sources/BSONEncoding/Encoding/BSON.Document (ext).swift b/Sources/BSONEncoding/Encoding/BSON.Document (ext).swift index 07ac596b..3ae7de55 100644 --- a/Sources/BSONEncoding/Encoding/BSON.Document (ext).swift +++ b/Sources/BSONEncoding/Encoding/BSON.Document (ext).swift @@ -1,12 +1,12 @@ extension BSON.Document:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(document: .init(self)) } } -extension BSON.Document:BSONBuilder +extension BSON.Document:_BSONBuilder { } extension BSON.Document diff --git a/Sources/BSONEncoding/Encoding/BSON.DocumentEncoder.swift b/Sources/BSONEncoding/Encoding/BSON.DocumentEncoder.swift index f99897e6..76dd4315 100644 --- a/Sources/BSONEncoding/Encoding/BSON.DocumentEncoder.swift +++ b/Sources/BSONEncoding/Encoding/BSON.DocumentEncoder.swift @@ -13,19 +13,34 @@ extension BSON } } } -extension BSON.DocumentEncoder:BSONEncoder +extension BSON.DocumentEncoder:BSON.Encoder { @inlinable public consuming func move() -> BSON.Output<[UInt8]> { self.output } @inlinable public static - var type:BSON { .document } + var type:BSON.AnyType { .document } } -extension BSON.DocumentEncoder:BSONBuilder +extension BSON.DocumentEncoder:_BSONBuilder { @inlinable public mutating - func append(_ key:CodingKey, with encode:(inout BSON.Field) -> ()) + func append(_ key:CodingKey, with encode:(inout BSON.FieldEncoder) -> ()) { encode(&self.output[with: .init(key)]) } } +extension BSON.DocumentEncoder +{ + @inlinable public + subscript(with key:some RawRepresentable) -> BSON.FieldEncoder + { + _read + { + yield self.output[with: .init(key)] + } + _modify + { + yield &self.output[with: .init(key)] + } + } +} diff --git a/Sources/BSONEncoding/Encoding/BSON.List (ext).swift b/Sources/BSONEncoding/Encoding/BSON.List (ext).swift index 943c444f..9377314e 100644 --- a/Sources/BSONEncoding/Encoding/BSON.List (ext).swift +++ b/Sources/BSONEncoding/Encoding/BSON.List (ext).swift @@ -1,7 +1,7 @@ extension BSON.List:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(list: .init(self)) } diff --git a/Sources/BSONEncoding/Encoding/BSON.ListEncoder.swift b/Sources/BSONEncoding/Encoding/BSON.ListEncoder.swift index aca8cc69..09a8889d 100644 --- a/Sources/BSONEncoding/Encoding/BSON.ListEncoder.swift +++ b/Sources/BSONEncoding/Encoding/BSON.ListEncoder.swift @@ -2,10 +2,9 @@ extension BSON { /// A type that can encode BSON list elements directly to an output buffer. /// - /// Unlike ``DocumentEncoder``, which works with ``BSONDocumentEncodable``, - /// this type currently doesn’t have a companion protocol. That’s because - /// we currently only use it to bootstrap faster ``BSONWeakEncodable`` - /// conformances for ``Sequence``s. + /// Like ``DocumentEncoder``, which works with ``BSONDocumentEncodable``, + /// this type has its own companion protocol ``BSONListEncodable``, which is used to + /// bootstrap faster ``BSONEncodable`` conformances for ``Sequence``s. @frozen public struct ListEncoder { @@ -22,18 +21,18 @@ extension BSON } } } -extension BSON.ListEncoder:BSONEncoder +extension BSON.ListEncoder:BSON.Encoder { @inlinable public consuming func move() -> BSON.Output<[UInt8]> { self.output } @inlinable public static - var type:BSON { .list } + var type:BSON.AnyType { .list } } extension BSON.ListEncoder { @inlinable public mutating - func append(with encode:(inout BSON.Field) -> ()) + func append(with encode:(inout BSON.FieldEncoder) -> ()) { encode(&self.output[with: .init(index: self.count)]) self.count += 1 diff --git a/Sources/BSONEncoding/Encoding/BSONBuilder.swift b/Sources/BSONEncoding/Encoding/_BSONBuilder.swift similarity index 84% rename from Sources/BSONEncoding/Encoding/BSONBuilder.swift rename to Sources/BSONEncoding/Encoding/_BSONBuilder.swift index 11ca3265..93e29aaa 100644 --- a/Sources/BSONEncoding/Encoding/BSONBuilder.swift +++ b/Sources/BSONEncoding/Encoding/_BSONBuilder.swift @@ -1,12 +1,12 @@ public -protocol BSONBuilder +protocol _BSONBuilder { associatedtype CodingKey mutating - func append(_ key:CodingKey, with encode:(inout BSON.Field) -> ()) + func append(_ key:CodingKey, with encode:(inout BSON.FieldEncoder) -> ()) } -extension BSONBuilder +extension _BSONBuilder { @inlinable public mutating func append(_ key:CodingKey, _ value:some BSONEncodable) @@ -28,7 +28,7 @@ extension BSONBuilder self.push(key, value as _?) } } -extension BSONBuilder +extension _BSONBuilder { @inlinable public subscript(key:CodingKey, with encode:(inout BSON.ListEncoder) -> ()) -> Void @@ -59,7 +59,7 @@ extension BSONBuilder } } -extension BSONBuilder +extension _BSONBuilder { /// Appends the given key-value pair to this document builder, encoding the /// value as the field value using its ``BSONEncodable`` implementation. @@ -67,10 +67,10 @@ extension BSONBuilder /// Type inference will always prefer one of the concretely-typed subscript /// overloads over this one. /// - /// The getter always returns [`nil`](). + /// The getter always returns nil. /// - /// Every non-[`nil`]() assignment to this subscript (including mutations - /// that leave the value in a non-[`nil`]() state after returning) will add + /// Every non-nil assignment to this subscript (including mutations + /// that leave the value in a non-nil state after returning) will add /// a new field to the document, even if the key is the same. @inlinable public subscript(key:CodingKey) -> Value? where Value:BSONEncodable diff --git a/Sources/BSONEncoding/Extensions/BSON (ext).swift b/Sources/BSONEncoding/Extensions/BSON (ext).swift deleted file mode 100644 index 285abcbe..00000000 --- a/Sources/BSONEncoding/Extensions/BSON (ext).swift +++ /dev/null @@ -1,9 +0,0 @@ -extension BSON:BSONEncodable -{ - /// Encodes this metatype as a value of type ``BSON.int32``. - @inlinable public - func encode(to field:inout BSON.Field) - { - field.encode(int32: .init(self.rawValue)) - } -} diff --git a/Sources/BSONEncoding/Extensions/BSON.AnyType (ext).swift b/Sources/BSONEncoding/Extensions/BSON.AnyType (ext).swift new file mode 100644 index 00000000..4749912b --- /dev/null +++ b/Sources/BSONEncoding/Extensions/BSON.AnyType (ext).swift @@ -0,0 +1,9 @@ +extension BSON.AnyType:BSONEncodable +{ + /// Encodes this metatype as a value of type ``BSON.AnyType/int32``. + @inlinable public + func encode(to field:inout BSON.FieldEncoder) + { + field.encode(int32: .init(self.rawValue)) + } +} diff --git a/Sources/BSONEncoding/Extensions/BSON.BinaryView (ext).swift b/Sources/BSONEncoding/Extensions/BSON.BinaryView (ext).swift index e46bb410..96e3d0c8 100644 --- a/Sources/BSONEncoding/Extensions/BSON.BinaryView (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.BinaryView (ext).swift @@ -1,7 +1,7 @@ extension BSON.BinaryView:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(binary: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.Decimal128 (ext).swift b/Sources/BSONEncoding/Extensions/BSON.Decimal128 (ext).swift index 3c167df4..10ba97a9 100644 --- a/Sources/BSONEncoding/Extensions/BSON.Decimal128 (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.Decimal128 (ext).swift @@ -1,7 +1,7 @@ extension BSON.Decimal128:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(decimal128: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.DocumentView (ext).swift b/Sources/BSONEncoding/Extensions/BSON.DocumentView (ext).swift index e54f7e6a..62d452dd 100644 --- a/Sources/BSONEncoding/Extensions/BSON.DocumentView (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.DocumentView (ext).swift @@ -1,7 +1,7 @@ extension BSON.DocumentView:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(document: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.Identifier (ext).swift b/Sources/BSONEncoding/Extensions/BSON.Identifier (ext).swift index 67a050ec..97bc6ad6 100644 --- a/Sources/BSONEncoding/Extensions/BSON.Identifier (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.Identifier (ext).swift @@ -1,7 +1,7 @@ extension BSON.Identifier:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(id: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.ListView (ext).swift b/Sources/BSONEncoding/Extensions/BSON.ListView (ext).swift index 95e578a4..1d60a7bc 100644 --- a/Sources/BSONEncoding/Extensions/BSON.ListView (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.ListView (ext).swift @@ -1,7 +1,7 @@ extension BSON.ListView:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(list: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.Max (ext).swift b/Sources/BSONEncoding/Extensions/BSON.Max (ext).swift index e3739d2a..a62c4195 100644 --- a/Sources/BSONEncoding/Extensions/BSON.Max (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.Max (ext).swift @@ -1,7 +1,7 @@ extension BSON.Max:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(max: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.Millisecond (ext).swift b/Sources/BSONEncoding/Extensions/BSON.Millisecond (ext).swift index 44a99278..ddbccb73 100644 --- a/Sources/BSONEncoding/Extensions/BSON.Millisecond (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.Millisecond (ext).swift @@ -1,7 +1,7 @@ extension BSON.Millisecond:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(millisecond: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.Min (ext).swift b/Sources/BSONEncoding/Extensions/BSON.Min (ext).swift index b76b2099..e851cff6 100644 --- a/Sources/BSONEncoding/Extensions/BSON.Min (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.Min (ext).swift @@ -1,7 +1,7 @@ extension BSON.Min:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(min: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.Regex (ext).swift b/Sources/BSONEncoding/Extensions/BSON.Regex (ext).swift index 600ded27..e7ee82e5 100644 --- a/Sources/BSONEncoding/Extensions/BSON.Regex (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.Regex (ext).swift @@ -1,8 +1,8 @@ extension BSON.Regex:BSONEncodable { - /// Encodes this regex as a value of type ``BSON.regex``. + /// Encodes this regex as a value of type ``BSON.AnyType/regex``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(regex: self) } diff --git a/Sources/BSONEncoding/Extensions/BSON.UTF8View (ext).swift b/Sources/BSONEncoding/Extensions/BSON.UTF8View (ext).swift index fe70c83d..704a0ca1 100644 --- a/Sources/BSONEncoding/Extensions/BSON.UTF8View (ext).swift +++ b/Sources/BSONEncoding/Extensions/BSON.UTF8View (ext).swift @@ -1,8 +1,8 @@ extension BSON.UTF8View:BSONEncodable { - /// Encodes this UTF-8 string as a ``BSON.string``. + /// Encodes this UTF-8 string as a ``BSON.AnyType/string``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(string: self) } diff --git a/Sources/BSONEncoding/Extensions/Bool (ext).swift b/Sources/BSONEncoding/Extensions/Bool (ext).swift index 71f9565c..84eb07a6 100644 --- a/Sources/BSONEncoding/Extensions/Bool (ext).swift +++ b/Sources/BSONEncoding/Extensions/Bool (ext).swift @@ -1,7 +1,7 @@ extension Bool:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(bool: self) } diff --git a/Sources/BSONEncoding/Extensions/Character (ext).swift b/Sources/BSONEncoding/Extensions/Character (ext).swift index ffdf49d1..8c9d358a 100644 --- a/Sources/BSONEncoding/Extensions/Character (ext).swift +++ b/Sources/BSONEncoding/Extensions/Character (ext).swift @@ -1,7 +1,7 @@ extension Character:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.description.encode(to: &field) } diff --git a/Sources/BSONEncoding/Extensions/Double (ext).swift b/Sources/BSONEncoding/Extensions/Double (ext).swift index 0e5c4535..6ee73af8 100644 --- a/Sources/BSONEncoding/Extensions/Double (ext).swift +++ b/Sources/BSONEncoding/Extensions/Double (ext).swift @@ -1,8 +1,8 @@ extension Double:BSONEncodable { - /// Encodes this metatype as a value of type ``BSON.double``. + /// Encodes this metatype as a value of type ``BSON.AnyType/double``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(double: .init(self)) } diff --git a/Sources/BSONEncoding/Extensions/Int (ext).swift b/Sources/BSONEncoding/Extensions/Int (ext).swift index 901bb7fd..8c4b2f2b 100644 --- a/Sources/BSONEncoding/Extensions/Int (ext).swift +++ b/Sources/BSONEncoding/Extensions/Int (ext).swift @@ -1,9 +1,9 @@ extension Int:BSONEncodable { - /// Encodes this integer as a value of type ``BSON.int32`` if it can be represented - /// exactly, or ``BSON.int64`` otherwise. + /// Encodes this integer as a value of type ``BSON.AnyType/int32`` if it can be represented + /// exactly, or ``BSON.AnyType/int64`` otherwise. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { if let int32:Int32 = .init(exactly: self) { diff --git a/Sources/BSONEncoding/Extensions/Int32 (ext).swift b/Sources/BSONEncoding/Extensions/Int32 (ext).swift index 83368d26..fda34b0c 100644 --- a/Sources/BSONEncoding/Extensions/Int32 (ext).swift +++ b/Sources/BSONEncoding/Extensions/Int32 (ext).swift @@ -1,8 +1,8 @@ extension Int32:BSONEncodable { - /// Encodes this integer as a value of type ``BSON.int32``. + /// Encodes this integer as a value of type ``BSON.AnyType/int32``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(int32: self) } diff --git a/Sources/BSONEncoding/Extensions/Int64 (ext).swift b/Sources/BSONEncoding/Extensions/Int64 (ext).swift index 7adcb807..83578959 100644 --- a/Sources/BSONEncoding/Extensions/Int64 (ext).swift +++ b/Sources/BSONEncoding/Extensions/Int64 (ext).swift @@ -1,10 +1,10 @@ extension Int64:BSONEncodable { - /// Encodes this integer as a value of type ``BSON.int64``. It will always use - /// the 64-bit representation, even if it would fit in a ``BSON.int32``. To use + /// Encodes this integer as a value of type ``BSON.AnyType/int64``. It will always use + /// the 64-bit representation, even if it would fit in a ``BSON.AnyType/int32``. To use /// a variable-length encoding, encode an ``Int`` instead. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(int64: self) } diff --git a/Sources/BSONEncoding/Extensions/Never (ext).swift b/Sources/BSONEncoding/Extensions/Never (ext).swift index fad013ba..9cd4aa5d 100644 --- a/Sources/BSONEncoding/Extensions/Never (ext).swift +++ b/Sources/BSONEncoding/Extensions/Never (ext).swift @@ -2,7 +2,7 @@ extension Never:BSONEncodable { /// ``Never`` encodes anything. @inlinable public - func encode(to _:inout BSON.Field) + func encode(to _:inout BSON.FieldEncoder) { } } diff --git a/Sources/BSONEncoding/Extensions/StaticString (ext).swift b/Sources/BSONEncoding/Extensions/StaticString (ext).swift index 84590fcd..665b0513 100644 --- a/Sources/BSONEncoding/Extensions/StaticString (ext).swift +++ b/Sources/BSONEncoding/Extensions/StaticString (ext).swift @@ -1,8 +1,8 @@ extension StaticString:BSONEncodable { - /// Encodes this string as a value of type ``BSON.string``. + /// Encodes this string as a value of type ``BSON.AnyType/string``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(string: .init(self)) } diff --git a/Sources/BSONEncoding/Extensions/String (ext).swift b/Sources/BSONEncoding/Extensions/String (ext).swift index ce46197f..88d77301 100644 --- a/Sources/BSONEncoding/Extensions/String (ext).swift +++ b/Sources/BSONEncoding/Extensions/String (ext).swift @@ -1,8 +1,8 @@ extension String:BSONEncodable { - /// Encodes this string as a value of type ``BSON.string``. + /// Encodes this string as a value of type ``BSON.AnyType/string``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(string: .init(self)) } diff --git a/Sources/BSONEncoding/Extensions/Substring (ext).swift b/Sources/BSONEncoding/Extensions/Substring (ext).swift index 457bd60c..ee9b9e81 100644 --- a/Sources/BSONEncoding/Extensions/Substring (ext).swift +++ b/Sources/BSONEncoding/Extensions/Substring (ext).swift @@ -1,8 +1,8 @@ extension Substring:BSONEncodable { - /// Encodes this substring as a value of type ``BSON.string``. + /// Encodes this substring as a value of type ``BSON.AnyType/string``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(string: .init(self)) } diff --git a/Sources/BSONEncoding/Extensions/Unicode.Scalar (ext).swift b/Sources/BSONEncoding/Extensions/Unicode.Scalar (ext).swift index c03ff36d..011fac44 100644 --- a/Sources/BSONEncoding/Extensions/Unicode.Scalar (ext).swift +++ b/Sources/BSONEncoding/Extensions/Unicode.Scalar (ext).swift @@ -1,7 +1,7 @@ extension Unicode.Scalar:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.description.encode(to: &field) } diff --git a/Sources/BSONEncoding/Optional (ext).swift b/Sources/BSONEncoding/Optional (ext).swift index cd5727e7..6c9b197b 100644 --- a/Sources/BSONEncoding/Optional (ext).swift +++ b/Sources/BSONEncoding/Optional (ext).swift @@ -1,8 +1,8 @@ extension Optional:BSONEncodable where Wrapped:BSONEncodable { - /// Encodes this optional as an explicit ``BSON.null``, if nil. + /// Encodes this optional as an explicit ``BSON.AnyType/null``, if nil. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { if let self:Wrapped { diff --git a/Sources/BSONEncoding/README.md b/Sources/BSONEncoding/README.md new file mode 100644 index 00000000..d099ed1f --- /dev/null +++ b/Sources/BSONEncoding/README.md @@ -0,0 +1 @@ +# ``/BSONEncoding`` diff --git a/Sources/BSONEncoding/exports.swift b/Sources/BSONEncoding/exports.swift index 16493bb2..39f8f8b5 100644 --- a/Sources/BSONEncoding/exports.swift +++ b/Sources/BSONEncoding/exports.swift @@ -1 +1 @@ -@_exported import BSON +@_exported import enum BSONABI.BSON diff --git a/Sources/BSONEncodingTests/Fields/Main.FieldDuplication.swift b/Sources/BSONEncodingTests/Fields/Main.FieldDuplication.swift new file mode 100644 index 00000000..0dcd7ffe --- /dev/null +++ b/Sources/BSONEncodingTests/Fields/Main.FieldDuplication.swift @@ -0,0 +1,29 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum FieldDuplication + { + } +} +extension Main.FieldDuplication:TestBattery +{ + static + func run(tests:TestGroup) + { + Self.run(tests / "integer", + encoded: .init + { + $0["inhabited"] = 5 + $0["uninhabited"] = nil as Never?? + $0["inhabited"] = 7 + $0["uninhabited"] = nil as Never?? + }, + literal: + [ + "inhabited": 5, + "inhabited": 7, + ]) + } +} diff --git a/Sources/BSONEncodingTests/Fields/Main.FieldElision.swift b/Sources/BSONEncodingTests/Fields/Main.FieldElision.swift new file mode 100644 index 00000000..f8eaf4d0 --- /dev/null +++ b/Sources/BSONEncodingTests/Fields/Main.FieldElision.swift @@ -0,0 +1,52 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum FieldElision + { + } +} +extension Main.FieldElision:TestBattery +{ + static + func run(tests:TestGroup) + { + let _:BSON.Document = [:] + + Self.run(tests / "null", + encoded: .init + { + $0["elided"] = nil as Never?? + $0["inhabited"] = (nil as Never?) as Never?? + }, + literal: + [ + "inhabited": .null, + ]) + + Self.run(tests / "integer", + encoded: .init + { + $0["elided"] = nil as Int? + $0["inhabited"] = 5 + }, + literal: + [ + "inhabited": 5, + ]) + + Self.run(tests / "optional", + encoded: .init + { + $0["elided"] = nil as Int?? + $0["inhabited"] = (5 as Int?) as Int?? + $0["uninhabited"] = (nil as Int?) as Int?? + }, + literal: + [ + "inhabited": 5, + "uninhabited": .null, + ]) + } +} diff --git a/Sources/BSONEncodingTests/Inference/Main.LiteralInference.swift b/Sources/BSONEncodingTests/Inference/Main.LiteralInference.swift new file mode 100644 index 00000000..034b46f5 --- /dev/null +++ b/Sources/BSONEncodingTests/Inference/Main.LiteralInference.swift @@ -0,0 +1,121 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum LiteralInference + { + } +} +extension Main.LiteralInference:TestBattery +{ + static + func run(tests:TestGroup) + { + Self.run(tests / "integer", + encoded: .init + { + $0["default"] = 1 + $0["default-long"] = 0x7fff_ffff_ffff_ffff + $0["int32"] = 1 as Int32 + $0["int64"] = 1 as Int64 + $0["int"] = 1 as Int + $0["int-long"] = 0x7fff_ffff_ffff_ffff as Int + }, + literal: + [ + "default": 1, + "default-long": 0x7fff_ffff_ffff_ffff, + "int32": .int32(1), + "int64": .int64(1), + "int": .int32(1), + "int-long": .int64(0x7fff_ffff_ffff_ffff), + ]) + + Self.run(tests / "floating-point", + encoded: .init + { + $0["default"] = 1.0 + $0["a"] = 1.0 as Double + }, + literal: + [ + "default": 1.0, + "a": .double(1.0), + ]) + + Self.run(tests / "string", + encoded: .init + { + $0["a"] = "string" + $0["b"] = "string" + $0["c"] = "string" as BSON.UTF8View + $0["d"] = "string" as BSON.UTF8View + }, + literal: + [ + "a": "string", + "b": "string", + "c": .string(.init(from: "string")), + "d": .string(.init(from: "string")), + ]) + + Self.run(tests / "optionals", + encoded: .init + { + $0["a"] = [1, nil, 3] + $0["b"] = [1, nil, 3] + $0["c"] = [1, .null, 3] as BSON.ListView<[UInt8]> + $0["d"] = [1, .null, 3] as BSON.ListView<[UInt8]> + }, + literal: + [ + "a": [1, .null, 3], + "b": .list([1, .null, 3]), + "c": [1, .null, 3], + "d": .list([1, .null, 3]), + ]) + + Self.run(tests / "list", + encoded: .init + { + $0["a"] = [1, 2, 3] + $0["b"] = [1, 2, 3] + $0["c"] = [1, 2, 3] as BSON.ListView<[UInt8]> + $0["d"] = [1, 2, 3] as BSON.ListView<[UInt8]> + }, + literal: + [ + "a": [1, 2, 3], + "b": .list([1, 2, 3]), + "c": [1, 2, 3], + "d": .list([1, 2, 3]), + ]) + + Self.run(tests / "document", + encoded: .init + { + $0["a"] + { + $0["a"] = 1 + $0["b"] = 2 + $0["c"] = 3 + } + $0["b"] + { + $0["a"] = 1 + $0["b"] = 2 + $0["c"] = 3 + } + $0["c"] = ["a": 1, "b": 2, "c": 3] as BSON.DocumentView<[UInt8]> + $0["d"] = ["a": 1, "b": 2, "c": 3] as BSON.DocumentView<[UInt8]> + }, + literal: + [ + "a": ["a": 1, "b": 2, "c": 3], + "b": .document(["a": 1, "b": 2, "c": 3]), + "c": ["a": 1, "b": 2, "c": 3], + "d": .document(["a": 1, "b": 2, "c": 3]), + ]) + } +} diff --git a/Sources/BSONEncodingTests/Inference/Main.TypeInference.swift b/Sources/BSONEncodingTests/Inference/Main.TypeInference.swift new file mode 100644 index 00000000..4ce2bcda --- /dev/null +++ b/Sources/BSONEncodingTests/Inference/Main.TypeInference.swift @@ -0,0 +1,57 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum TypeInference + { + } +} +extension Main.TypeInference:TestBattery +{ + static + func run(tests:TestGroup) + { + Self.run(tests / "binary", + encoded: .init + { + $0["a"] = BSON.BinaryView<[UInt8]>.init(subtype: .generic, + slice: [0xff, 0xff, 0xff]) + }, + literal: + [ + "a": .binary(.init(subtype: .generic, + slice: [0xff, 0xff, 0xff])), + ]) + + Self.run(tests / "max", + encoded: .init + { + $0["max"] = BSON.Max.init() + }, + literal: + [ + "max": .max, + ]) + + Self.run(tests / "min", + encoded: .init + { + $0["min"] = BSON.Min.init() + }, + literal: + [ + "min": .min, + ]) + + Self.run(tests / "null", + encoded: .init + { + $0["null"] = (nil as Never?) as Never?? + }, + literal: + [ + "null": .null, + ]) + } +} diff --git a/Sources/BSONEncodingTests/Main.EncodeDocument.swift b/Sources/BSONEncodingTests/Main.EncodeDocument.swift new file mode 100644 index 00000000..7133f060 --- /dev/null +++ b/Sources/BSONEncodingTests/Main.EncodeDocument.swift @@ -0,0 +1,36 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum EncodeDocument + { + } +} +extension Main.EncodeDocument:TestBattery +{ + static + func run(tests:TestGroup) + { + Self.run(tests, + encoded: .init + { + $0["a"] = [:] + $0["b"] + { + $0["x"] = 1 + } + $0["c"] + { + $0["x"] = 1 + $0["y"] = 2 + } + }, + literal: + [ + "a": [:], + "b": ["x": 1], + "c": ["x": 1, "y": 2], + ]) + } +} diff --git a/Sources/BSONEncodingTests/Main.EncodeList.swift b/Sources/BSONEncodingTests/Main.EncodeList.swift new file mode 100644 index 00000000..35a45f47 --- /dev/null +++ b/Sources/BSONEncodingTests/Main.EncodeList.swift @@ -0,0 +1,34 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum EncodeList + { + } +} +extension Main.EncodeList:TestBattery +{ + static + func run(tests:TestGroup) + { + Self.run(tests, + encoded: .init + { + $0["a"] = [] as [Never] + $0["b"] = [1] + $0["c"] + { + $0.append(1) + $0.append("x") + $0.append(5.5) + } + }, + literal: + [ + "a": [], + "b": [1], + "c": [1, "x", 5.5], + ]) + } +} diff --git a/Sources/BSONEncodingTests/Main.EncodeString.swift b/Sources/BSONEncodingTests/Main.EncodeString.swift new file mode 100644 index 00000000..c467b3e5 --- /dev/null +++ b/Sources/BSONEncodingTests/Main.EncodeString.swift @@ -0,0 +1,29 @@ +import BSONEncoding +import Testing + +extension Main +{ + enum EncodeString + { + } +} +extension Main.EncodeString:TestBattery +{ + static + func run(tests:TestGroup) + { + Self.run(tests, + encoded: .init + { + $0["a"] = "" + $0["b"] = "foo" + $0["c"] = "foo\u{0}" + }, + literal: + [ + "a": "", + "b": "foo", + "c": "foo\u{0}", + ]) + } +} diff --git a/Sources/BSONEncodingTests/Main.swift b/Sources/BSONEncodingTests/Main.swift index 8e46acdb..301cb0e4 100644 --- a/Sources/BSONEncodingTests/Main.swift +++ b/Sources/BSONEncodingTests/Main.swift @@ -1,280 +1,19 @@ -import BSONEncoding import Testing @main -enum Main:SyncTests +enum Main:TestMain { static - func run(tests:Tests) - { - if let tests:TestGroup = tests / "literal-inference" - { - TestEncoding(tests / "integer", - encoded: .init - { - $0["default"] = 1 - $0["default-long"] = 0x7fff_ffff_ffff_ffff - $0["int32"] = 1 as Int32 - $0["int64"] = 1 as Int64 - $0["int"] = 1 as Int - $0["int-long"] = 0x7fff_ffff_ffff_ffff as Int - }, - literal: - [ - "default": 1, - "default-long": 0x7fff_ffff_ffff_ffff, - "int32": .int32(1), - "int64": .int64(1), - "int": .int32(1), - "int-long": .int64(0x7fff_ffff_ffff_ffff), - ]) - - TestEncoding(tests / "floating-point", - encoded: .init - { - $0["default"] = 1.0 - $0["a"] = 1.0 as Double - }, - literal: - [ - "default": 1.0, - "a": .double(1.0), - ]) - - TestEncoding(tests / "string", - encoded: .init - { - $0["a"] = "string" - $0["b"] = "string" - $0["c"] = "string" as BSON.UTF8View - $0["d"] = "string" as BSON.UTF8View - }, - literal: - [ - "a": "string", - "b": "string", - "c": .string(.init(from: "string")), - "d": .string(.init(from: "string")), - ]) - - TestEncoding(tests / "optionals", - encoded: .init - { - $0["a"] = [1, nil, 3] - $0["b"] = [1, nil, 3] - $0["c"] = [1, .null, 3] as BSON.ListView<[UInt8]> - $0["d"] = [1, .null, 3] as BSON.ListView<[UInt8]> - }, - literal: - [ - "a": [1, .null, 3], - "b": .list([1, .null, 3]), - "c": [1, .null, 3], - "d": .list([1, .null, 3]), - ]) - - TestEncoding(tests / "list", - encoded: .init - { - $0["a"] = [1, 2, 3] - $0["b"] = [1, 2, 3] - $0["c"] = [1, 2, 3] as BSON.ListView<[UInt8]> - $0["d"] = [1, 2, 3] as BSON.ListView<[UInt8]> - }, - literal: - [ - "a": [1, 2, 3], - "b": .list([1, 2, 3]), - "c": [1, 2, 3], - "d": .list([1, 2, 3]), - ]) - - TestEncoding(tests / "document", - encoded: .init - { - $0["a"] - { - $0["a"] = 1 - $0["b"] = 2 - $0["c"] = 3 - } - $0["b"] - { - $0["a"] = 1 - $0["b"] = 2 - $0["c"] = 3 - } - $0["c"] = ["a": 1, "b": 2, "c": 3] as BSON.DocumentView<[UInt8]> - $0["d"] = ["a": 1, "b": 2, "c": 3] as BSON.DocumentView<[UInt8]> - }, - literal: - [ - "a": ["a": 1, "b": 2, "c": 3], - "b": .document(["a": 1, "b": 2, "c": 3]), - "c": ["a": 1, "b": 2, "c": 3], - "d": .document(["a": 1, "b": 2, "c": 3]), - ]) - } - if let tests:TestGroup = tests / "type-inference" - { - - TestEncoding(tests / "binary", - encoded: .init - { - $0["a"] = BSON.BinaryView<[UInt8]>.init(subtype: .generic, - slice: [0xff, 0xff, 0xff]) - }, - literal: - [ - "a": .binary(.init(subtype: .generic, - slice: [0xff, 0xff, 0xff])), - ]) - - TestEncoding(tests / "max", - encoded: .init - { - $0["max"] = BSON.Max.init() - }, - literal: - [ - "max": .max, - ]) - - TestEncoding(tests / "min", - encoded: .init - { - $0["min"] = BSON.Min.init() - }, - literal: - [ - "min": .min, - ]) - - TestEncoding(tests / "null", - encoded: .init - { - $0["null"] = (nil as Never?) as Never?? - }, - literal: - [ - "null": .null, - ]) - } - if let tests:TestGroup = tests / "string" - { - TestEncoding(tests, - encoded: .init - { - $0["a"] = "" - $0["b"] = "foo" - $0["c"] = "foo\u{0}" - }, - literal: - [ - "a": "", - "b": "foo", - "c": "foo\u{0}", - ]) - } - if let tests:TestGroup = tests / "array" - { - TestEncoding(tests, - encoded: .init - { - $0["a"] = [] as [Never] - $0["b"] = [1] - $0["c"] - { - $0.append(1) - $0.append("x") - $0.append(5.5) - } - }, - literal: - [ - "a": [], - "b": [1], - "c": [1, "x", 5.5], - ]) - } - if let tests:TestGroup = tests / "document" - { - TestEncoding(tests, - encoded: .init - { - $0["a"] = [:] - $0["b"] - { - $0["x"] = 1 - } - $0["c"] - { - $0["x"] = 1 - $0["y"] = 2 - } - }, - literal: - [ - "a": [:], - "b": ["x": 1], - "c": ["x": 1, "y": 2], - ]) - } - if let tests:TestGroup = tests / "elided-fields" - { - let _:BSON.Document = [:] - - TestEncoding(tests / "null", - encoded: .init - { - $0["elided"] = nil as Never?? - $0["inhabited"] = (nil as Never?) as Never?? - }, - literal: - [ - "inhabited": .null, - ]) - - TestEncoding(tests / "integer", - encoded: .init - { - $0["elided"] = nil as Int? - $0["inhabited"] = 5 - }, - literal: - [ - "inhabited": 5, - ]) - - TestEncoding(tests / "optional", - encoded: .init - { - $0["elided"] = nil as Int?? - $0["inhabited"] = (5 as Int?) as Int?? - $0["uninhabited"] = (nil as Int?) as Int?? - }, - literal: - [ - "inhabited": 5, - "uninhabited": .null, - ]) - } - if let tests:TestGroup = tests / "duplicate-fields" - { - - TestEncoding(tests / "integer", - encoded: .init - { - $0["inhabited"] = 5 - $0["uninhabited"] = nil as Never?? - $0["inhabited"] = 7 - $0["uninhabited"] = nil as Never?? - }, - literal: - [ - "inhabited": 5, - "inhabited": 7, - ]) - } - } + let all:[any TestBattery.Type] = + [ + LiteralInference.self, + TypeInference.self, + + EncodeDocument.self, + EncodeList.self, + EncodeString.self, + + FieldDuplication.self, + FieldElision.self, + ] } diff --git a/Sources/BSONEncodingTests/TestBattery (ext).swift b/Sources/BSONEncodingTests/TestBattery (ext).swift new file mode 100644 index 00000000..64fd7ee2 --- /dev/null +++ b/Sources/BSONEncodingTests/TestBattery (ext).swift @@ -0,0 +1,34 @@ +import BSONEncoding +import Testing + +extension TestBattery +{ + static + func run(_ tests:TestGroup?, + encoded:BSON.Document, + literal:BSON.DocumentView<[UInt8]>) + { + guard + let tests:TestGroup + else + { + return + } + + let encoded:BSON.DocumentView<[UInt8]> = .init(encoded) + + tests.expect(encoded ==? literal) + + guard let encoded:[(key:BSON.Key, value:BSON.AnyValue>)] = + tests.do({ try encoded.parse { ($0, $1) } }), + let literal:[(key:BSON.Key, value:BSON.AnyValue>)] = + tests.do({ try literal.parse { ($0, $1) } }) + else + { + return + } + + tests.expect(encoded.map(\.key) ..? literal.map(\.key)) + tests.expect(encoded.map(\.value) ..? literal.map(\.value)) + } +} diff --git a/Sources/BSONEncodingTests/TestEncoding.swift b/Sources/BSONEncodingTests/TestEncoding.swift deleted file mode 100644 index 236a83c2..00000000 --- a/Sources/BSONEncodingTests/TestEncoding.swift +++ /dev/null @@ -1,29 +0,0 @@ -import BSONEncoding -import Testing - -func TestEncoding(_ tests:TestGroup?, - encoded:BSON.Document, - literal:BSON.DocumentView<[UInt8]>) -{ - guard let tests:TestGroup - else - { - return - } - - let encoded:BSON.DocumentView<[UInt8]> = .init(encoded) - - tests.expect(encoded ==? literal) - - guard let encoded:[(key:BSON.Key, value:BSON.AnyValue>)] = - tests.do({ try encoded.parse { ($0, $1) } }), - let literal:[(key:BSON.Key, value:BSON.AnyValue>)] = - tests.do({ try literal.parse { ($0, $1) } }) - else - { - return - } - - tests.expect(encoded.map(\.key) ..? literal.map(\.key)) - tests.expect(encoded.map(\.value) ..? literal.map(\.value)) -} diff --git a/Sources/BSONIntegrationTests/Main.EnumeratedCodingKeys.swift b/Sources/BSONIntegrationTests/Main.EnumeratedCodingKeys.swift new file mode 100644 index 00000000..3aaecbdd --- /dev/null +++ b/Sources/BSONIntegrationTests/Main.EnumeratedCodingKeys.swift @@ -0,0 +1,81 @@ +import BSON +import BSONReflection +import Testing + +extension Main +{ + enum EnumeratedCodingKeys + { + } +} +extension Main.EnumeratedCodingKeys:TestBattery +{ + static + func run(tests:TestGroup) + { + struct Codable:BSONDocumentDecodable, BSONDocumentEncodable, Equatable + { + enum CodingKey:String, Sendable + { + case a + case b + case c + } + + let a:Int + let b:[Int] + let c:[[Int]] + + init(a:Int, b:[Int], c:[[Int]]) + { + self.a = a + self.b = b + self.c = c + } + + init(bson:BSON.DocumentDecoder>) + throws + { + self.a = try bson[.a].decode() + self.b = try bson[.b].decode() + self.c = try bson[.c].decode() + } + + func encode(to bson:inout BSON.DocumentEncoder) + { + bson[.a] = self.a + bson[.b] = self.b + bson[.c] = self.c + } + } + + let expected:Codable = .init(a: 5, b: [5, 6], c: [[5, 6, 7], [8]]) + let bson:BSON.Document = .init + { + $0["a"] = 5 + $0["b"] = [5, 6] + $0["c"] = [[5, 6, 7], [8]] + $0["d"] = [[[5, 6, 7, 8], [9, 10]], [[11]]] + } + + tests.do + { + let original:BSON.DocumentView = .init(bson) + let decoded:Codable = try .init(bson: original) + + tests.expect(decoded ==? expected) + + let encoded:BSON.Document = .init(with: decoded.encode(to:)) + + tests.expect(true: encoded.bytes.count < original.slice.count) + + let redecoded:Codable = try .init(bson: .init(encoded)) + + tests.expect(redecoded ==? expected) + + let reencoded:BSON.Document = .init(with: redecoded.encode(to:)) + + tests.expect(reencoded.bytes ..? encoded.bytes) + } + } +} diff --git a/Sources/BSONIntegrationTests/Main.swift b/Sources/BSONIntegrationTests/Main.swift index de6b8d04..8b88e3f6 100644 --- a/Sources/BSONIntegrationTests/Main.swift +++ b/Sources/BSONIntegrationTests/Main.swift @@ -1,81 +1,8 @@ import Testing -import BSONReflection -import BSONDecoding -import BSONEncoding @main -enum Main:SyncTests +enum Main:TestMain { static - func run(tests:Tests) - { - if let tests:TestGroup = tests / "EnumeratedCodingKeys" - { - struct Codable:BSONDocumentDecodable, BSONDocumentEncodable, Equatable - { - enum CodingKey:String, Sendable - { - case a - case b - case c - } - - let a:Int - let b:[Int] - let c:[[Int]] - - init(a:Int, b:[Int], c:[[Int]]) - { - self.a = a - self.b = b - self.c = c - } - - init(bson:BSON.DocumentDecoder>) - throws - { - self.a = try bson[.a].decode() - self.b = try bson[.b].decode() - self.c = try bson[.c].decode() - } - - func encode(to bson:inout BSON.DocumentEncoder) - { - bson[.a] = self.a - bson[.b] = self.b - bson[.c] = self.c - } - } - - let expected:Codable = .init(a: 5, b: [5, 6], c: [[5, 6, 7], [8]]) - let bson:BSON.Document = .init - { - $0["a"] = 5 - $0["b"] = [5, 6] - $0["c"] = [[5, 6, 7], [8]] - $0["d"] = [[[5, 6, 7, 8], [9, 10]], [[11]]] - } - - tests.do - { - let original:BSON.DocumentView = .init(bson) - let decoded:Codable = try .init(bson: original) - - tests.expect(decoded ==? expected) - - let encoded:BSON.Document = .init(with: decoded.encode(to:)) - - tests.expect(true: encoded.bytes.count < original.slice.count) - - let redecoded:Codable = try .init(bson: .init(encoded)) - - tests.expect(redecoded ==? expected) - - let reencoded:BSON.Document = .init(with: redecoded.encode(to:)) - - tests.expect(reencoded.bytes ..? encoded.bytes) - } - - } - } + let all:[any TestBattery.Type] = [EnumeratedCodingKeys.self] } diff --git a/Sources/BSONReflection/BSON.AnyValue (ext).swift b/Sources/BSONReflection/BSON.AnyValue (ext).swift index 057d6449..265cfdf2 100644 --- a/Sources/BSONReflection/BSON.AnyValue (ext).swift +++ b/Sources/BSONReflection/BSON.AnyValue (ext).swift @@ -7,43 +7,43 @@ extension BSON.AnyValue switch self { case .document(let document): - return document.description(indent: indent) + document.description(indent: indent) case .list(let list): - return list.description(indent: indent) + list.description(indent: indent) case .binary(let binary): - return "{ binary data, type \(binary.subtype.rawValue) }" + "{ binary data, type \(binary.subtype.rawValue) }" case .bool(let bool): - return "\(bool)" + "\(bool)" case .decimal128(let decimal128): - return "\(decimal128) as BSON.Decimal128" + "\(decimal128) as BSON.Decimal128" case .double(let double): - return "\(double)" + "\(double)" case .id(let id): - return "\(id)" + "\(id)" case .int32(let int32): - return "\(int32)" + "\(int32)" case .int64(let int64): - return "\(int64) as Int64" + "\(int64) as Int64" case .javascript(let javascript): - return "'\(javascript)'" + "'\(javascript)'" case .javascriptScope(_, _): - return "{ javascript with scope }" + "{ javascript with scope }" case .max: - return "max" + "max" case .millisecond(let millisecond): - return "\(millisecond.value) as BSON.Millisecond" + "\(millisecond.value) as BSON.Millisecond" case .min: - return "min" + "min" case .null: - return "null" + "null" case .pointer(let database, let id): - return "\(database) + \(id)" + "\(database) + \(id)" case .regex(let regex): - return "\(regex)" + "\(regex)" case .string(let utf8): - return "\"\(utf8)\"" + "\"\(utf8)\"" case .uint64(let uint64): - return "\(uint64) as UInt64" + "\(uint64) as UInt64" } } } @@ -82,46 +82,46 @@ extension BSON.AnyValue switch (lhs, rhs) { case (.document (let lhs), .document (let rhs)): - return lhs ~~ rhs + lhs ~~ rhs case (.list (let lhs), .list (let rhs)): - return lhs ~~ rhs + lhs ~~ rhs case (.binary (let lhs), .binary (let rhs)): - return lhs == rhs + lhs == rhs case (.bool (let lhs), .bool (let rhs)): - return lhs == rhs + lhs == rhs case (.decimal128 (let lhs), .decimal128 (let rhs)): - return lhs == rhs + lhs == rhs case (.double (let lhs), .double (let rhs)): - return lhs == rhs + lhs == rhs case (.id (let lhs), .id (let rhs)): - return lhs == rhs + lhs == rhs case (.int32 (let lhs), .int32 (let rhs)): - return lhs == rhs + lhs == rhs case (.int64 (let lhs), .int64 (let rhs)): - return lhs == rhs + lhs == rhs case (.javascript (let lhs), .javascript (let rhs)): - return lhs == rhs + lhs == rhs case (.javascriptScope(let lhs, let lhsCode), .javascriptScope(let rhs, let rhsCode)): - return lhsCode == rhsCode && lhs ~~ rhs + lhsCode == rhsCode && lhs ~~ rhs case (.max, .max): - return true + true case (.millisecond (let lhs), .millisecond (let rhs)): - return lhs.value == rhs.value + lhs.value == rhs.value case (.min, .min): - return true + true case (.null, .null): - return true + true case (.pointer(let lhs, let lhsID), .pointer(let rhs, let rhsID)): - return lhsID == rhsID && lhs == rhs + lhsID == rhsID && lhs == rhs case (.regex (let lhs), .regex (let rhs)): - return lhs == rhs + lhs == rhs case (.string (let lhs), .string (let rhs)): - return lhs == rhs + lhs == rhs case (.uint64 (let lhs), .uint64 (let rhs)): - return lhs == rhs + lhs == rhs default: - return false + false } } } @@ -138,9 +138,9 @@ extension BSON.AnyValue switch self { case .document(let document): - return .document(try document.canonicalized()) + .document(try document.canonicalized()) case .list(let list): - return .list(try list.canonicalized()) + .list(try list.canonicalized()) case .binary, .bool, .decimal128, @@ -149,9 +149,9 @@ extension BSON.AnyValue .int32, .int64, .javascript: - return self + self case .javascriptScope(let scope, let utf8): - return .javascriptScope(try scope.canonicalized(), utf8) + .javascriptScope(try scope.canonicalized(), utf8) case .max, .millisecond, .min, @@ -160,7 +160,7 @@ extension BSON.AnyValue .regex, .string, .uint64: - return self + self } } } diff --git a/Sources/BSONReflection/BSON.DocumentView (ext).swift b/Sources/BSONReflection/BSON.DocumentView (ext).swift index 85672869..11644c71 100644 --- a/Sources/BSONReflection/BSON.DocumentView (ext).swift +++ b/Sources/BSONReflection/BSON.DocumentView (ext).swift @@ -37,7 +37,7 @@ extension BSON.DocumentView /// Some documents that do not compare equal under byte-wise /// `==` comparison may compare equal under this operator, due to normalization /// of deprecated BSON variants. For example, a value of the deprecated `symbol` type - /// will compare equal to a ``BSON Value.string(_:)`` value with the same contents. + /// will compare equal to a ``BSON.AnyValue/string(_:)`` value with the same contents. @inlinable public static func ~~ (lhs:Self, rhs:BSON.DocumentView) -> Bool { diff --git a/Sources/BSONReflection/BSON.ListView (ext).swift b/Sources/BSONReflection/BSON.ListView (ext).swift index 85c62b03..838cf969 100644 --- a/Sources/BSONReflection/BSON.ListView (ext).swift +++ b/Sources/BSONReflection/BSON.ListView (ext).swift @@ -15,7 +15,7 @@ extension BSON.ListView:CustomStringConvertible extension BSON.ListView { /// Performs a type-aware equivalence comparison by parsing each operand and recursively - /// comparing the elements, ignoring list key names. Returns [`false`]() if either + /// comparing the elements, ignoring list key names. Returns `false` if either /// operand fails to parse. /// /// Some embedded documents that do not compare equal under byte-wise diff --git a/Sources/BSONReflectionTests/Main.Documents.swift b/Sources/BSONReflectionTests/Main.Documents.swift new file mode 100644 index 00000000..fe39fbe5 --- /dev/null +++ b/Sources/BSONReflectionTests/Main.Documents.swift @@ -0,0 +1,256 @@ +import BSON +import BSONReflection +import Testing + +extension Main +{ + enum Documents + { + } +} +extension Main.Documents:TestBattery +{ + static + func run(tests:TestGroup) + { + let document:BSON.Document = .init + { + $0["_id"] = 0x1111_2222_3333_4444_5555_6666 as BSON.Identifier + $0["facility"] = "Recreation and Activities Center" + + $0["logo"] = BSON.BinaryView<[UInt8]>.init( + subtype: .generic, + slice: [1, 2, 3, 4, 5]) + + $0["incidents"] = 145 + $0["averageRating"] = 2.76 + $0["supervisors"] = ["Barbie", "Midge", "Raquelle"] + $0["notes"] = [] as [Never] + $0["campaigns"] = [:] + $0["complaints"] + { + $0.append + { + $0["_id"] = 0x4455_6677_8899_AABB + $0["type"] = "property damage" + $0["supervisor"] = "Raquelle" + $0["status"] = "open" + $0["date"] + { + $0["Y"] = 2022 + $0["M"] = 12 + $0["D"] = 31 + } + } + $0.append + { + $0["_id"] = 0x4455_6677_8899_AABC + $0["type"] = "sexual assault" + $0["supervisor"] = "Midge" + $0["status"] = "open" + $0["rpi"] = true + $0["date"] + { + $0["Y"] = 2023 + $0["M"] = 1 + $0["D"] = 1 + } + } + $0.append + { + $0["_id"] = 0x4455_6677_8899_AABD + $0["type"] = "property theft" + $0["supervisor"] = "Barbie" + $0["status"] = "closed" + $0["date"] + { + $0["Y"] = 2023 + $0["M"] = 1 + $0["D"] = 4 + } + } + $0.append + { + $0["_id"] = 0x4455_6677_8899_AABE + $0["type"] = "property damage" + $0["supervisor"] = "Midge" + $0["status"] = "open" + $0["date"] + { + $0["Y"] = 2023 + $0["M"] = 1 + $0["D"] = 16 + } + } + $0.append + { + $0["_id"] = 0x4455_6677_8899_AABF + $0["type"] = "assault" + $0["supervisor"] = "Raquelle" + $0["status"] = "closed" + $0["rpi"] = false + $0["date"] + { + $0["Y"] = 2023 + $0["M"] = 1 + $0["D"] = 22 + } + } + $0.append + { + $0["_id"] = 0x4455_6677_8899_AAC0 + $0["type"] = "guest expulsion" + $0["supervisor"] = "Barbie" + $0["status"] = "closed" + $0["rpi"] = true + $0["date"] + { + $0["Y"] = 2023 + $0["M"] = 2 + $0["D"] = 14 + } + } + $0.append + { + $0["_id"] = 0x4455_6677_8899_AAC1 + $0["type"] = "sexual assault" + $0["supervisor"] = "Barbie" + $0["status"] = "open" + $0["rpi"] = false + $0["date"] + { + $0["Y"] = 2023 + $0["M"] = 2 + $0["D"] = 14 + } + } + } + } + let value:BSON.AnyValue<[UInt8]> = .document(.init(document)) + let expected:String = + """ + { + $0[_id] = 0x11112222_33334444_55556666 + $0[facility] = "Recreation and Activities Center" + $0[logo] = { binary data, type 0 } + $0[incidents] = 145 + $0[averageRating] = 2.76 + $0[supervisors] = + { + $0[0] = "Barbie" + $0[1] = "Midge" + $0[2] = "Raquelle" + } + $0[notes] = [] + $0[campaigns] = [:] + $0[complaints] = + { + $0[0] = + { + $0[_id] = 4923954431178418875 as Int64 + $0[type] = "property damage" + $0[supervisor] = "Raquelle" + $0[status] = "open" + $0[date] = + { + $0[Y] = 2022 + $0[M] = 12 + $0[D] = 31 + } + } + $0[1] = + { + $0[_id] = 4923954431178418876 as Int64 + $0[type] = "sexual assault" + $0[supervisor] = "Midge" + $0[status] = "open" + $0[rpi] = true + $0[date] = + { + $0[Y] = 2023 + $0[M] = 1 + $0[D] = 1 + } + } + $0[2] = + { + $0[_id] = 4923954431178418877 as Int64 + $0[type] = "property theft" + $0[supervisor] = "Barbie" + $0[status] = "closed" + $0[date] = + { + $0[Y] = 2023 + $0[M] = 1 + $0[D] = 4 + } + } + $0[3] = + { + $0[_id] = 4923954431178418878 as Int64 + $0[type] = "property damage" + $0[supervisor] = "Midge" + $0[status] = "open" + $0[date] = + { + $0[Y] = 2023 + $0[M] = 1 + $0[D] = 16 + } + } + $0[4] = + { + $0[_id] = 4923954431178418879 as Int64 + $0[type] = "assault" + $0[supervisor] = "Raquelle" + $0[status] = "closed" + $0[rpi] = false + $0[date] = + { + $0[Y] = 2023 + $0[M] = 1 + $0[D] = 22 + } + } + $0[5] = + { + $0[_id] = 4923954431178418880 as Int64 + $0[type] = "guest expulsion" + $0[supervisor] = "Barbie" + $0[status] = "closed" + $0[rpi] = true + $0[date] = + { + $0[Y] = 2023 + $0[M] = 2 + $0[D] = 14 + } + } + $0[6] = + { + $0[_id] = 4923954431178418881 as Int64 + $0[type] = "sexual assault" + $0[supervisor] = "Barbie" + $0[status] = "open" + $0[rpi] = false + $0[date] = + { + $0[Y] = 2023 + $0[M] = 2 + $0[D] = 14 + } + } + } + } + """ + + var l:Int = 0 + for (line, expected):(Substring, Substring) in zip( + "\(value)".split(whereSeparator: \.isNewline), + expected.split(whereSeparator: \.isNewline)) + { + l += 1 + (tests / "Line\(l)")?.expect(line ==? expected) + } + } +} diff --git a/Sources/BSONReflectionTests/Main.swift b/Sources/BSONReflectionTests/Main.swift index 503e629d..3a646af7 100644 --- a/Sources/BSONReflectionTests/Main.swift +++ b/Sources/BSONReflectionTests/Main.swift @@ -1,254 +1,8 @@ -import BSONEncoding -import BSONReflection import Testing @main -enum Main:SyncTests +enum Main:TestMain { static - func run(tests:Tests) - { - if let tests:TestGroup = tests / "Documents" - { - let document:BSON.Document = .init - { - $0["_id"] = 0x1111_2222_3333_4444_5555_6666 as BSON.Identifier - $0["facility"] = "Recreation and Activities Center" - - $0["logo"] = BSON.BinaryView<[UInt8]>.init( - subtype: .generic, - slice: [1, 2, 3, 4, 5]) - - $0["incidents"] = 145 - $0["averageRating"] = 2.76 - $0["supervisors"] = ["Barbie", "Midge", "Raquelle"] - $0["notes"] = [] as [Never] - $0["campaigns"] = [:] - $0["complaints"] - { - $0.append - { - $0["_id"] = 0x4455_6677_8899_AABB - $0["type"] = "property damage" - $0["supervisor"] = "Raquelle" - $0["status"] = "open" - $0["date"] - { - $0["Y"] = 2022 - $0["M"] = 12 - $0["D"] = 31 - } - } - $0.append - { - $0["_id"] = 0x4455_6677_8899_AABC - $0["type"] = "sexual assault" - $0["supervisor"] = "Midge" - $0["status"] = "open" - $0["rpi"] = true - $0["date"] - { - $0["Y"] = 2023 - $0["M"] = 1 - $0["D"] = 1 - } - } - $0.append - { - $0["_id"] = 0x4455_6677_8899_AABD - $0["type"] = "property theft" - $0["supervisor"] = "Barbie" - $0["status"] = "closed" - $0["date"] - { - $0["Y"] = 2023 - $0["M"] = 1 - $0["D"] = 4 - } - } - $0.append - { - $0["_id"] = 0x4455_6677_8899_AABE - $0["type"] = "property damage" - $0["supervisor"] = "Midge" - $0["status"] = "open" - $0["date"] - { - $0["Y"] = 2023 - $0["M"] = 1 - $0["D"] = 16 - } - } - $0.append - { - $0["_id"] = 0x4455_6677_8899_AABF - $0["type"] = "assault" - $0["supervisor"] = "Raquelle" - $0["status"] = "closed" - $0["rpi"] = false - $0["date"] - { - $0["Y"] = 2023 - $0["M"] = 1 - $0["D"] = 22 - } - } - $0.append - { - $0["_id"] = 0x4455_6677_8899_AAC0 - $0["type"] = "guest expulsion" - $0["supervisor"] = "Barbie" - $0["status"] = "closed" - $0["rpi"] = true - $0["date"] - { - $0["Y"] = 2023 - $0["M"] = 2 - $0["D"] = 14 - } - } - $0.append - { - $0["_id"] = 0x4455_6677_8899_AAC1 - $0["type"] = "sexual assault" - $0["supervisor"] = "Barbie" - $0["status"] = "open" - $0["rpi"] = false - $0["date"] - { - $0["Y"] = 2023 - $0["M"] = 2 - $0["D"] = 14 - } - } - } - } - let value:BSON.AnyValue<[UInt8]> = .document(.init(document)) - let expected:String = - """ - { - $0[_id] = 0x11112222_33334444_55556666 - $0[facility] = "Recreation and Activities Center" - $0[logo] = { binary data, type 0 } - $0[incidents] = 145 - $0[averageRating] = 2.76 - $0[supervisors] = - { - $0[0] = "Barbie" - $0[1] = "Midge" - $0[2] = "Raquelle" - } - $0[notes] = [] - $0[campaigns] = [:] - $0[complaints] = - { - $0[0] = - { - $0[_id] = 4923954431178418875 as Int64 - $0[type] = "property damage" - $0[supervisor] = "Raquelle" - $0[status] = "open" - $0[date] = - { - $0[Y] = 2022 - $0[M] = 12 - $0[D] = 31 - } - } - $0[1] = - { - $0[_id] = 4923954431178418876 as Int64 - $0[type] = "sexual assault" - $0[supervisor] = "Midge" - $0[status] = "open" - $0[rpi] = true - $0[date] = - { - $0[Y] = 2023 - $0[M] = 1 - $0[D] = 1 - } - } - $0[2] = - { - $0[_id] = 4923954431178418877 as Int64 - $0[type] = "property theft" - $0[supervisor] = "Barbie" - $0[status] = "closed" - $0[date] = - { - $0[Y] = 2023 - $0[M] = 1 - $0[D] = 4 - } - } - $0[3] = - { - $0[_id] = 4923954431178418878 as Int64 - $0[type] = "property damage" - $0[supervisor] = "Midge" - $0[status] = "open" - $0[date] = - { - $0[Y] = 2023 - $0[M] = 1 - $0[D] = 16 - } - } - $0[4] = - { - $0[_id] = 4923954431178418879 as Int64 - $0[type] = "assault" - $0[supervisor] = "Raquelle" - $0[status] = "closed" - $0[rpi] = false - $0[date] = - { - $0[Y] = 2023 - $0[M] = 1 - $0[D] = 22 - } - } - $0[5] = - { - $0[_id] = 4923954431178418880 as Int64 - $0[type] = "guest expulsion" - $0[supervisor] = "Barbie" - $0[status] = "closed" - $0[rpi] = true - $0[date] = - { - $0[Y] = 2023 - $0[M] = 2 - $0[D] = 14 - } - } - $0[6] = - { - $0[_id] = 4923954431178418881 as Int64 - $0[type] = "sexual assault" - $0[supervisor] = "Barbie" - $0[status] = "open" - $0[rpi] = false - $0[date] = - { - $0[Y] = 2023 - $0[M] = 2 - $0[D] = 14 - } - } - } - } - """ - - var l:Int = 0 - for (line, expected):(Substring, Substring) in zip( - "\(value)".split(whereSeparator: \.isNewline), - expected.split(whereSeparator: \.isNewline)) - { - l += 1 - (tests / "Line\(l)")?.expect(line ==? expected) - } - } - } + let all:[any TestBattery.Type] = [Documents.self] } diff --git a/Sources/BSONStreaming/BSON.AnyValue (ext).swift b/Sources/BSONStreaming/BSON.AnyValue (ext).swift deleted file mode 100644 index 4cf55a2f..00000000 --- a/Sources/BSONStreaming/BSON.AnyValue (ext).swift +++ /dev/null @@ -1,66 +0,0 @@ -import BSONTypes - -extension BSON.AnyValue:ExpressibleByStringLiteral, - ExpressibleByArrayLiteral, - ExpressibleByExtendedGraphemeClusterLiteral, - ExpressibleByUnicodeScalarLiteral, - ExpressibleByDictionaryLiteral - where Bytes:RangeReplaceableCollection, - Bytes:RandomAccessCollection, - Bytes.Index == Int -{ - @inlinable public - init(stringLiteral:String) - { - self = .string(.init(from: stringLiteral)) - } - @inlinable public - init(arrayLiteral:Self...) - { - self = .list(.init(elements: arrayLiteral)) - } - @inlinable public - init(dictionaryLiteral:(BSON.Key, Self)...) - { - self = .document(.init(fields: dictionaryLiteral)) - } -} - -extension BSON.AnyValue:ExpressibleByFloatLiteral -{ - @inlinable public - init(floatLiteral:Double) - { - self = .double(floatLiteral) - } -} -extension BSON.AnyValue:ExpressibleByIntegerLiteral -{ - /// Creates an instance initialized to the specified integer value. - /// It will be an ``int32(_:)`` value if it fits, otherwise it will - /// be an ``int64(_:)``. - /// - /// Although MongoDB uses ``Int32`` as its default integer type, - /// this library infers integer literals to be of type ``Int`` for - /// consistency with the rest of the Swift language. - @inlinable public - init(integerLiteral:Int) - { - if let int32:Int32 = .init(exactly: integerLiteral) - { - self = .int32(int32) - } - else - { - self = .int64(Int64.init(integerLiteral)) - } - } -} -extension BSON.AnyValue:ExpressibleByBooleanLiteral -{ - @inlinable public - init(booleanLiteral:Bool) - { - self = .bool(booleanLiteral) - } -} diff --git a/Sources/BSONStreaming/BSONDecoder.swift b/Sources/BSONStreaming/BSONDecoder.swift deleted file mode 100644 index 489d3b8a..00000000 --- a/Sources/BSONStreaming/BSONDecoder.swift +++ /dev/null @@ -1,17 +0,0 @@ -import BSONTypes - -public -protocol BSONDecoder -{ - associatedtype Storage:RandomAccessCollection - - init(parsing bson:__shared BSON.AnyValue) throws -} -extension BSONDecoder -{ - /// Ddecoder elements are indices over fragments of BSON - /// parsed from a larger allocation, like ``Substring``s from a - /// larger parent ``String``. - public - typealias Bytes = Storage.SubSequence -} diff --git a/Sources/BSONStreaming/BSONEncoder.swift b/Sources/BSONStreaming/BSONEncoder.swift deleted file mode 100644 index 104a1417..00000000 --- a/Sources/BSONStreaming/BSONEncoder.swift +++ /dev/null @@ -1,13 +0,0 @@ -import BSONTypes - -public -protocol BSONEncoder -{ - init(_:consuming BSON.Output<[UInt8]>) - - consuming - func move() -> BSON.Output<[UInt8]> - - static - var type:BSON { get } -} diff --git a/Sources/BSONTesting/TestGroup (ext).swift b/Sources/BSONTesting/TestGroup (ext).swift index d0c187fc..f5a13802 100644 --- a/Sources/BSONTesting/TestGroup (ext).swift +++ b/Sources/BSONTesting/TestGroup (ext).swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import Testing extension TestGroup diff --git a/Sources/BSONTests/Main.InvalidBSON.swift b/Sources/BSONTests/Main.InvalidBSON.swift new file mode 100644 index 00000000..6603d2c7 --- /dev/null +++ b/Sources/BSONTests/Main.InvalidBSON.swift @@ -0,0 +1,329 @@ +import Base16 +import BSON +import BSONReflection +import Testing + +extension Main +{ + enum InvalidBSON + { + } +} +extension Main.InvalidBSON:TestBattery +{ + static + func run(tests:TestGroup) + { + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/boolean.json + if let tests:TestGroup = tests / "bool" + { + Self.run(tests / "invalid-subtype", + invalid: "090000000862000200", + catching: BSON.BooleanSubtypeError.init(invalid: 2)) + + Self.run(tests / "invalid-subtype-negative", + invalid: "09000000086200FF00", + catching: BSON.BooleanSubtypeError.init(invalid: 255)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/int32.json + if let tests:TestGroup = tests / "int32" + { + Self.run(tests / "truncated", + invalid: "09000000_10_6100_05_00", + catching: BSON.InputError.init(expected: .bytes(4), encountered: 1)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/int32.json + if let tests:TestGroup = tests / "int64" + { + Self.run(tests / "truncated", + invalid: "0C000000_12_6100_12345678_00", + catching: BSON.InputError.init(expected: .bytes(8), encountered: 4)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/timestamp.json + if let tests:TestGroup = tests / "uint64" + { + Self.run(tests / "truncated", + invalid: "0F000000_11_6100_2A00000015CD5B_00", + catching: BSON.InputError.init(expected: .bytes(8), encountered: 7)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/top.json + if let tests:TestGroup = tests / "top" + { + Self.run(tests / "zeroes", + invalid: "00000000_000000000000", + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: 0)) + + Self.run(tests / "invalid-length-over", + invalid: "12000000_02_666F6F00_04000000_626172", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0x12 - 4), encountered: 12)) + + Self.run(tests / "invalid-length-under", + invalid: "12000000_02_666F6F00_04000000_62617200_00_DEADBEEF", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .end, encountered: 4)) + + Self.run(tests / "invalid-type-0x00", + invalid: "07000000_00_0000", + // ^~ + catching: BSON.TypeError.init(invalid: 0x00)) + + Self.run(tests / "invalid-type-0x80", + invalid: "07000000_80_0000", + // ^~ + catching: BSON.TypeError.init(invalid: 0x80)) + + Self.run(tests / "truncated", + invalid: "12000000_02_666F", + catching: BSON.InputError.init(expected: .bytes(0x12 - 4), encountered: 3)) + + Self.run(tests / "invalid-key", + invalid: "0D000000_10_7800_00_0100000000", + // ^~ + catching: BSON.TypeError.init(invalid: 0x00)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/datetime.json + if let tests:TestGroup = tests / "millisecond" + { + Self.run(tests / "truncated", + invalid: "0C000000_0961001234567800", + catching: BSON.InputError.init(expected: .bytes(8), encountered: 4)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/double.json + if let tests:TestGroup = tests / "double" + { + // note: frameshift + Self.run(tests / "truncated", + invalid: "0B000000_0164000000F03F00", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .end, encountered: 1)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/oid.json + if let tests:TestGroup = tests / "id" + { + Self.run(tests / "truncated", + invalid: "12000000_07_6100_56E1FC72E0C917E9C471", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0x12 - 4), encountered: 13)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/dbpointer.json + if let tests:TestGroup = tests / "pointer" + { + Self.run(tests / "invalid-length-negative", + invalid: "1A000000_0C_6100_FFFFFFFF_620056E1FC72E0C917E9C471416100", + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: -1)) + + Self.run(tests / "invalid-length-zero", + invalid: "1A000000_0C_6100_00000000_620056E1FC72E0C917E9C471416100", + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: 0)) + + Self.run(tests / "truncated", + invalid: "16000000_0C_6100_03000000_616200_56E1FC72E0C91700", + catching: BSON.InputError.init(expected: .bytes(12), encountered: 7)) + + Self.run(tests / "truncated-identifier", + invalid: "1A000000_0C_6100_03000000_616200_56E1FC72E0C917E9C4716100", + catching: BSON.InputError.init(expected: .bytes(12), encountered: 11)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/binary.json + if let tests:TestGroup = tests / "binary" + { + Self.run(tests / "invalid-length-over", + invalid: "1D000000_05_7800_FF000000_05_73FFD26444B34C6990E8E7D1DFC035D400", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0xFF + 1), encountered: 17)) + + Self.run(tests / "invalid-length-negative", + invalid: "0D000000057800FFFFFFFF0000", + catching: BSON.BinaryViewError.init(expected: .subtype)) + // TODO: tests for legacy binary subtype 0x02 + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/document.json + if let tests:TestGroup = tests / "document" + { + Self.run(tests / "invalid-length-over", + invalid: "18000000_03_666F6F00_0F000000_10_62617200_FFFFFF7F_0000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0x0F - 4), encountered: 10)) + + Self.run(tests / "invalid-length-under", + invalid: "15000000_03_666F6F00_0A000000_08_62617200_01_00_00", + // ^~ + catching: BSON.InputError.init(expected: .bytes(1))) + + Self.run(tests / "invalid-value", + invalid: "1C000000_03_666F6F00_12000000_02_62617200_05000000_62617A000000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) + + Self.run(tests / "invalid-key", + invalid: "15000000_03_7800_0D000000_10_6100_00010000_00_0000", + // ^~ + catching: BSON.TypeError.init(invalid: 0)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/array.json + if let tests:TestGroup = tests / "tuple" + { + Self.run(tests / "invalid-length-over", + invalid: "14000000_04_6100_0D000000_10_30000A00_00_000000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0x0D - 4), encountered: 8)) + + Self.run(tests / "invalid-length-under", + invalid: "14000000_04_6100_0B000000_10_30000A00_00_000000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(4), encountered: 3)) + + Self.run(tests / "invalid-element", + invalid: "1A000000_04_666F6F00_100000000230000500000062617A000000", + // + catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/regex.json + if let tests:TestGroup = tests / "regex" + { + // note: frameshift + Self.run(tests / "invalid-pattern", + invalid: "0F0000000B610061006300696D0000", + catching: BSON.Regex.OptionError.init(invalid: "c")) + // note: frameshift + Self.run(tests / "invalid-options", + invalid: "100000000B61006162630069006D0000", + catching: BSON.TypeError.init(invalid: 109)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/string.json + if let tests:TestGroup = tests / "string" + { + Self.run(tests / "missing-trailing-null-byte", + invalid: "0C000000_02_6100_00000000_00", + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: 0)) + + Self.run(tests / "invalid-length-negative", + invalid: "0C000000_02_6100_FFFFFFFF_00", + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: -1)) + + Self.run(tests / "invalid-length-over", + invalid: "10000000_02_6100_05000000_62006200_00", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) + + Self.run(tests / "invalid-length-over-document", + invalid: "12000000_02_00_FFFFFF00_666F6F6261720000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0xffffff), encountered: 7)) + + Self.run(tests / "invalid-length-under", + invalid: "0E000000_02_6100_01000000_00_00_00", + // ^~ + catching: BSON.TypeError.init(invalid: 0x00)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/code.json + if let tests:TestGroup = tests / "javascript" + { + Self.run(tests / "missing-trailing-null-byte", + invalid: "0C000000_0D_6100_00000000_00", + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: 0)) + + Self.run(tests / "invalid-length-negative", + invalid: "0C0000000D6100FFFFFFFF00", + catching: BSON.HeaderError.init(length: -1)) + + Self.run(tests / "invalid-length-over", + invalid: "10000000_0D_6100_05000000_6200620000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) + + Self.run(tests / "invalid-length-over-document", + invalid: "12000000_0D_00_FFFFFF00_666F6F6261720000", + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0xffffff), encountered: 7)) + + Self.run(tests / "invalid-length-under", + invalid: "0E000000_0D_6100_01000000_00_00_00", + // ^~ + catching: BSON.TypeError.init(invalid: 0x00)) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/code_w_scope.json + if let tests:TestGroup = tests / "javascript-scope" + { + // note: we do not validate the redundant field length, + // so those tests are not included + + // note: the length is actually too short, but because we use the component-wise + // length headers instead of the field length, this manifests itself as a + // frameshift error. + Self.run(tests / "invalid-length-frameshift-clips-scope", + invalid: """ + 28000000_0F_6100_20000000_04000000_61626364_00130000_0010780001000000107900010000000000 + """, + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0x00_00_13_00 - 4), encountered: 16)) + + Self.run(tests / "invalid-length-over", + invalid: """ + 28000000_0F_6100_20000000_06000000_616263640013_00000010_780001000000107900010000000000 + """, + // ^~~~~~~~ + catching: BSON.InputError.init(expected: .bytes(0x10_00_00_00 - 4), encountered: 14)) + // note: frameshift + Self.run(tests / "invalid-length-frameshift", + invalid: """ + 28000000_0F_6100_20000000_FF000000_61626364001300000010780001000000107900010000000000 + """, + catching: BSON.InputError.init(expected: .bytes(255), encountered: 24)) + + Self.run(tests / "invalid-scope", + invalid: """ + 1C000000_0F_00_15000000_01000000_00_0C000000_02_00000000_00000000 + """, + // ^~~~~~~~ + catching: BSON.HeaderError.init(length: 0)) + } + } +} +extension Main.InvalidBSON +{ + private static + func run(_ tests:TestGroup?, invalid:String, catching error:some Error & Equatable) + { + guard + let tests:TestGroup + else + { + return + } + + let invalid:[UInt8] = Base16.decode(invalid.utf8) + + var input:BSON.Input<[UInt8]> = .init(invalid) + + tests.do(catching: error) + { + let document:BSON.DocumentView> = try input.parse( + as: BSON.DocumentView>.self) + try input.finish() + _ = try document.canonicalized() + } + } +} diff --git a/Sources/BSONTests/Main.ValidBSON.swift b/Sources/BSONTests/Main.ValidBSON.swift new file mode 100644 index 00000000..6d69265c --- /dev/null +++ b/Sources/BSONTests/Main.ValidBSON.swift @@ -0,0 +1,609 @@ +import Base16 +import BSON +import BSONReflection +import Testing + +extension Main +{ + enum ValidBSON + { + } +} +extension Main.ValidBSON:TestBattery +{ + static + func run(tests:TestGroup) + { + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/multi-type.json + // cannot use this test, because it encodes a deprecated binary subtype, which is + // (intentionally) impossible to construct with swift-bson. + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/maxkey.json + do + { + Self.run(tests / "max", + canonical: "080000007F610000", + expected: ["a": .max]) + } + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/minkey.json + do + { + Self.run(tests / "min", + canonical: "08000000FF610000", + expected: ["a": .min]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/null.json + do + { + Self.run(tests / "null", + canonical: "080000000A610000", + expected: ["a": .null]) + } + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/undefined.json + do + { + Self.run(tests / "undefined", + degenerate: "0800000006610000", + canonical: "080000000A610000", + expected: ["a": .null]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/boolean.json + if let tests:TestGroup = tests / "bool" + { + Self.run(tests / "true", + canonical: "090000000862000100", + expected: ["b": true]) + Self.run(tests / "false", + canonical: "090000000862000000", + expected: ["b": false]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/int32.json + if let tests:TestGroup = tests / "int32" + { + Self.run(tests / "min", + canonical: "0C0000001069000000008000", + expected: ["i": .int32(-2147483648)]) + + Self.run(tests / "max", + canonical: "0C000000106900FFFFFF7F00", + expected: ["i": .int32(2147483647)]) + + Self.run(tests / "-1", + canonical: "0C000000106900FFFFFFFF00", + expected: ["i": .int32(-1)]) + + Self.run(tests / "0", + canonical: "0C0000001069000000000000", + expected: ["i": .int32(0)]) + + Self.run(tests / "+1", + canonical: "0C0000001069000100000000", + expected: ["i": .int32(1)]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/int32.json + if let tests:TestGroup = tests / "int64" + { + Self.run(tests / "min", + canonical: "10000000126100000000000000008000", + expected: ["a": .int64(-9223372036854775808)]) + + Self.run(tests / "max", + canonical: "10000000126100FFFFFFFFFFFFFF7F00", + expected: ["a": .int64(9223372036854775807)]) + + Self.run(tests / "-1", + canonical: "10000000126100FFFFFFFFFFFFFFFF00", + expected: ["a": .int64(-1)]) + + Self.run(tests / "0", + canonical: "10000000126100000000000000000000", + expected: ["a": .int64(0)]) + + Self.run(tests / "+1", + canonical: "10000000126100010000000000000000", + expected: ["a": .int64(1)]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/timestamp.json + if let tests:TestGroup = tests / "uint64" + { + Self.run(tests / "(123456789, 42)", + canonical: "100000001161002A00000015CD5B0700", + expected: ["a": .uint64(123456789 << 32 | 42)]) + + Self.run(tests / "ones", + canonical: "10000000116100FFFFFFFFFFFFFFFF00", + expected: ["a": .uint64(.max)]) + + Self.run(tests / "(4000000000, 4000000000)", + canonical: "1000000011610000286BEE00286BEE00", + expected: ["a": .uint64(4000000000 << 32 | 4000000000)]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/top.json + if let tests:TestGroup = tests / "top" + { + Self.run(tests / "dollar-prefixed-key", + canonical: "0F00000010246B6579002A00000000", + expected: ["$key": .int32(42)]) + + Self.run(tests / "dollar-key", + canonical: "0E00000002240002000000610000", + expected: ["$": "a"]) + + Self.run(tests / "dotted-key", + canonical: "1000000002612E620002000000630000", + expected: ["a.b": "c"]) + + Self.run(tests / "dot-key", + canonical: "0E000000022E0002000000610000", + expected: [".": "a"]) + + Self.run(tests / "empty-truncated-header", + degenerate: "0100000000", + canonical: "0500000000", + expected: [:]) + + Self.run(tests / "empty", + canonical: "0500000000", + expected: [:]) + + Self.run(tests / "invalid-end-of-object-0x01", + degenerate: "05000000_01", + canonical: "05000000_00", + expected: [:]) + + Self.run(tests / "invalid-end-of-object-0xff", + degenerate: "05000000_FF", + canonical: "05000000_00", + expected: [:]) + + Self.run(tests / "invalid-end-of-object-0x70", + degenerate: "05000000_70", + canonical: "05000000_00", + expected: [:]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/decimal128-1.json + if let tests:TestGroup = tests / "decimal128" + { + Self.run(tests / "positive-quiet-nan", + canonical: "180000001364000000000000000000000000000000007C00", + expected: ["d": .decimal128(.init( + high: 0x7C00_0000_0000_0000, + low: 0x0000_0000_0000_0000))]) + + Self.run(tests / "negative-quiet-nan", + canonical: "18000000136400000000000000000000000000000000FC00", + expected: ["d": .decimal128(.init( + high: 0xFC00_0000_0000_0000, + low: 0x0000_0000_0000_0000))]) + + Self.run(tests / "positive-signaling-nan", + canonical: "180000001364000000000000000000000000000000007E00", + expected: ["d": .decimal128(.init( + high: 0x7E00_0000_0000_0000, + low: 0x0000_0000_0000_0000))]) + + Self.run(tests / "negative-signaling-nan", + canonical: "18000000136400000000000000000000000000000000FE00", + expected: ["d": .decimal128(.init( + high: 0xFE00_0000_0000_0000, + low: 0x0000_0000_0000_0000))]) + + // this only serves to verify we are handling byte-order correctly; + // there is very little point in elaborating decimal128 tests further + Self.run(tests / "largest", + canonical: "18000000136400F2AF967ED05C82DE3297FF6FDE3C403000", + expected: ["d": .decimal128(.init( + high: 0x3040_3CDE_6FFF_9732, + low: 0xDE82_5CD0_7E96_AFF2))]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/datetime.json + if let tests:TestGroup = tests / "millisecond" + { + Self.run(tests / "epoch", + canonical: "10000000096100000000000000000000", + expected: ["a": .millisecond(0)]) + + Self.run(tests / "positive", + canonical: "10000000096100C5D8D6CC3B01000000", + expected: ["a": .millisecond(1356351330501)]) + + Self.run(tests / "negative", + canonical: "10000000096100C33CE7B9BDFFFFFF00", + expected: ["a": .millisecond(-284643869501)]) + + Self.run(tests / "positive-2", + canonical: "1000000009610000DC1FD277E6000000", + expected: ["a": .millisecond(253402300800000)]) + + Self.run(tests / "positive-3", + canonical: "10000000096100D1D6D6CC3B01000000", + expected: ["a": .millisecond(1356351330001)]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/double.json + if let tests:TestGroup = tests / "double" + { + Self.run(tests / "+1.0", + canonical: "10000000016400000000000000F03F00", + expected: ["d": .double(1.0)]) + + Self.run(tests / "-1.0", + canonical: "10000000016400000000000000F0BF00", + expected: ["d": .double(-1.0)]) + + Self.run(tests / "+1.0001220703125", + canonical: "10000000016400000000008000F03F00", + expected: ["d": .double(1.0001220703125)]) + + Self.run(tests / "-1.0001220703125", + canonical: "10000000016400000000008000F0BF00", + expected: ["d": .double(-1.0001220703125)]) + + Self.run(tests / "1.2345678921232E+18", + canonical: "100000000164002a1bf5f41022b14300", + expected: ["d": .double(1.2345678921232e18)]) + + Self.run(tests / "-1.2345678921232E+18", + canonical: "100000000164002a1bf5f41022b1c300", + expected: ["d": .double(-1.2345678921232e18)]) + + // remaining corpus test cases are pointless because swift cannot distinguish + // between -0.0 and +0.0 + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/oid.json + if let tests:TestGroup = tests / "id" + { + let id:BSON.Identifier = 0x0123_4567_89AB_CDEF_4567_3210 + + tests.expect(id.timestamp ==? 0x0123_4567) + tests.expect(true: id.seed == (0x89, 0xAB, 0xCD, 0xEF, 0x45)) + tests.expect(true: id.ordinal == (0x67, 0x32, 0x10)) + + tests.expect(id ==? .init(timestamp: id.timestamp, seed: id.seed, ordinal: id.ordinal)) + + Self.run(tests / "zeroes", + canonical: "1400000007610000000000000000000000000000", + expected: ["a": .id(0x00000000_00000000_00_000000)]) + + Self.run(tests / "ones", + canonical: "14000000076100FFFFFFFFFFFFFFFFFFFFFFFF00", + expected: ["a": .id(0xffffffff_ffffffff_ff_ffffff)]) + + Self.run(tests / "random", + canonical: "1400000007610056E1FC72E0C917E9C471416100", + expected: ["a": .id(0x56e1fc72_e0c917e9_c4_714161)]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/dbpointer.json + if let tests:TestGroup = tests / "pointer" + { + + Self.run(tests / "ascii", + canonical: "1A0000000C610002000000620056E1FC72E0C917E9C471416100", + expected: ["a": .pointer(.init(from: "b"), .init( + 0x56e1fc72, 0xe0c917e9, 0xc4_714161))]) + + Self.run(tests / "unicode", + canonical: "1B0000000C610003000000C3A90056E1FC72E0C917E9C471416100", + expected: ["a": .pointer(.init(from: "é"), .init( + 0x56e1fc72, 0xe0c917e9, 0xc4_714161))]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/binary.json + if let tests:TestGroup = tests / "binary" + { + Self.run(tests / "generic-empty", + canonical: "0D000000057800000000000000", + expected: ["x": .binary(.init(subtype: .generic, slice: []))]) + + Self.run(tests / "generic", + canonical: "0F0000000578000200000000FFFF00", + expected: ["x": .binary(.init(subtype: .generic, + slice: Base16.decode("ffff")))]) + + Self.run(tests / "function", + canonical: "0F0000000578000200000001FFFF00", + expected: ["x": .binary(.init(subtype: .function, + slice: Base16.decode("ffff")))]) + + Self.run(tests / "uuid", + canonical: "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400", + expected: ["x": .binary(.init(subtype: .uuid, + slice: Base16.decode("73ffd26444b34c6990e8e7d1dfc035d4")))]) + + Self.run(tests / "md5", + canonical: "1D000000057800100000000573FFD26444B34C6990E8E7D1DFC035D400", + expected: ["x": .binary(.init(subtype: .md5, + slice: Base16.decode("73ffd26444b34c6990e8e7d1dfc035d4")))]) + + Self.run(tests / "compressed", + canonical: "1D000000057800100000000773FFD26444B34C6990E8E7D1DFC035D400", + expected: ["x": .binary(.init(subtype: .compressed, + slice: Base16.decode("73ffd26444b34c6990e8e7d1dfc035d4")))]) + + Self.run(tests / "custom", + canonical: "0F0000000578000200000080FFFF00", + expected: ["x": .binary(.init(subtype: .custom(code: 0x80), + slice: Base16.decode("ffff")))]) + // TODO: tests for legacy binary subtype 0x02 + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/document.json + if let tests:TestGroup = tests / "document" + { + Self.run(tests / "empty", + canonical: "0D000000037800050000000000", + expected: ["x": [:]]) + + Self.run(tests / "empty-key", + canonical: "150000000378000D00000002000200000062000000", + expected: ["x": ["": "b"]]) + + Self.run(tests / "single-character-key", + canonical: "160000000378000E0000000261000200000062000000", + expected: ["x": ["a": "b"]]) + + Self.run(tests / "dollar-prefixed-key", + canonical: "170000000378000F000000022461000200000062000000", + expected: ["x": ["$a": "b"]]) + + Self.run(tests / "dollar-key", + canonical: "160000000378000E0000000224000200000061000000", + expected: ["x": ["$": "a"]]) + + Self.run(tests / "dotted-key", + canonical: "180000000378001000000002612E62000200000063000000", + expected: ["x": ["a.b": "c"]]) + + Self.run(tests / "dot-key", + canonical: "160000000378000E000000022E000200000061000000", + expected: ["x": [".": "a"]]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/array.json + if let tests:TestGroup = tests / "tuple" + { + Self.run(tests / "empty", + canonical: "0D000000046100050000000000", + expected: ["a": []]) + Self.run(tests / "single-element", + canonical: "140000000461000C0000001030000A0000000000", + expected: ["a": [.int32(10)]]) + + Self.run(tests / "single-element-empty-key", + degenerate: "130000000461000B00000010000A0000000000", + canonical: "140000000461000C0000001030000A0000000000", + expected: ["a": [.int32(10)]]) + + Self.run(tests / "single-element-invalid-key", + degenerate: "150000000461000D000000106162000A0000000000", + canonical: "140000000461000C0000001030000A0000000000", + expected: ["a": [.int32(10)]]) + + Self.run(tests / "multiple-element-duplicate-keys", + degenerate: "1b000000046100130000001030000a000000103000140000000000", + canonical: "1b000000046100130000001030000a000000103100140000000000", + expected: ["a": [.int32(10), .int32(20)]]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/regex.json + if let tests:TestGroup = tests / "regex" + { + Self.run(tests / "empty", + canonical: "0A0000000B6100000000", + expected: ["a": .regex(.init(pattern: "", options: []))]) + + Self.run(tests / "empty-options", + canonical: "0D0000000B6100616263000000", + expected: ["a": .regex(.init(pattern: "abc", options: []))]) + + Self.run(tests / "I-HAVE-OPTIONS", + canonical: "0F0000000B610061626300696D0000", + expected: ["a": .regex(.init(pattern: "abc", options: [.i, .m]))]) + + Self.run(tests / "slash", + canonical: "110000000B610061622F636400696D0000", + expected: ["a": .regex(.init(pattern: "ab/cd", options: [.i, .m]))]) + + Self.run(tests / "non-alphabetized", + degenerate: "100000000B6100616263006D69780000", + canonical: "100000000B610061626300696D780000", + expected: ["a": .regex(.init(pattern: "abc", options: [.i, .m, .x]))]) + + Self.run(tests / "escaped", + canonical: "100000000B610061625C226162000000", + expected: ["a": .regex(.init(pattern: #"ab\"ab"#, options: []))]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/string.json + if let tests:TestGroup = tests / "string" + { + Self.run(tests / "empty", + canonical: "0D000000026100010000000000", + expected: ["a": ""]) + + Self.run(tests / "single-character", + canonical: "0E00000002610002000000620000", + expected: ["a": "b"]) + + Self.run(tests / "multiple-character", + canonical: "190000000261000D0000006162616261626162616261620000", + expected: ["a": "abababababab"]) + + Self.run(tests / "utf-8-double-code-unit", + canonical: "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", + expected: ["a": "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"]) + + Self.run(tests / "utf-8-triple-code-unit", + canonical: "190000000261000D000000E29886E29886E29886E298860000", + expected: ["a": "\u{2606}\u{2606}\u{2606}\u{2606}"]) + + Self.run(tests / "utf-8-null-bytes", + canonical: "190000000261000D0000006162006261620062616261620000", + expected: ["a": "ab\u{00}bab\u{00}babab"]) + + Self.run(tests / "escaped", + canonical: + """ + 3200000002610026000000\ + 61625C220102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F6162\ + 0000 + """, + expected: + [ + "a": + """ + ab\\\"\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\ + \t\n\u{0b}\u{0c}\r\u{0e}\u{0f}\u{10}\ + \u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\ + \u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}ab + """ + ]) + + Self.run(tests / "invalid-utf-8", + canonical: "0E00000002610002000000E90000", + expected: ["a": .string(.init(slice: [0xe9]))]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/symbol.json + if let tests:TestGroup = tests / "symbol" + { + Self.run(tests / "empty", + degenerate: "0D0000000E6100010000000000", + canonical: "0D000000026100010000000000", + expected: ["a": ""]) + + Self.run(tests / "single-character", + degenerate: "0E0000000E610002000000620000", + canonical: "0E00000002610002000000620000", + expected: ["a": "b"]) + + Self.run(tests / "multiple-character", + degenerate: "190000000E61000D0000006162616261626162616261620000", + canonical: "190000000261000D0000006162616261626162616261620000", + expected: ["a": "abababababab"]) + + Self.run(tests / "utf-8-double-code-unit", + degenerate: "190000000E61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", + canonical: "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", + expected: ["a": "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"]) + + Self.run(tests / "utf-8-triple-code-unit", + degenerate: "190000000E61000D000000E29886E29886E29886E298860000", + canonical: "190000000261000D000000E29886E29886E29886E298860000", + expected: ["a": "\u{2606}\u{2606}\u{2606}\u{2606}"]) + + Self.run(tests / "utf-8-null-bytes", + degenerate: "190000000E61000D0000006162006261620062616261620000", + canonical: "190000000261000D0000006162006261620062616261620000", + expected: ["a": "ab\u{00}bab\u{00}babab"]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/code.json + if let tests:TestGroup = tests / "javascript" + { + Self.run(tests / "empty", + canonical: "0D0000000D6100010000000000", + expected: ["a": .javascript(.init(from: ""))]) + + Self.run(tests / "single-character", + canonical: "0E0000000D610002000000620000", + expected: ["a": .javascript(.init(from: "b"))]) + + Self.run(tests / "multiple-character", + canonical: "190000000D61000D0000006162616261626162616261620000", + expected: ["a": .javascript(.init(from: "abababababab"))]) + + Self.run(tests / "utf-8-double-code-unit", + canonical: "190000000D61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", + expected: ["a": .javascript(.init(from: "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"))]) + + Self.run(tests / "utf-8-triple-code-unit", + canonical: "190000000D61000D000000E29886E29886E29886E298860000", + expected: ["a": .javascript(.init(from: "\u{2606}\u{2606}\u{2606}\u{2606}"))]) + + Self.run(tests / "utf-8-null-bytes", + canonical: "190000000D61000D0000006162006261620062616261620000", + expected: ["a": .javascript(.init(from: "ab\u{00}bab\u{00}babab"))]) + } + + // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/code_w_scope.json + if let tests:TestGroup = tests / "javascript-scope" + { + Self.run(tests / "empty", + canonical: "160000000F61000E0000000100000000050000000000", + expected: ["a": .javascriptScope([:], .init(from: ""))]) + + Self.run(tests / "empty-scope", + canonical: "1A0000000F610012000000050000006162636400050000000000", + expected: ["a": .javascriptScope([:], .init(from: "abcd"))]) + + Self.run(tests / "empty-code", + canonical: "1D0000000F61001500000001000000000C000000107800010000000000", + expected: ["a": .javascriptScope(["x": .int32(1)], .init(from: ""))]) + + Self.run(tests / "non-empty", + canonical: "210000000F6100190000000500000061626364000C000000107800010000000000", + expected: ["a": .javascriptScope(["x": .int32(1)], .init(from: "abcd"))]) + + Self.run(tests / "unicode", + canonical: "1A0000000F61001200000005000000C3A9006400050000000000", + expected: ["a": .javascriptScope([:], .init(from: "\u{e9}\u{00}d"))]) + } + } +} +extension Main.ValidBSON +{ + private static + func run(_ tests:TestGroup?, + degenerate:String? = nil, + canonical:String, + expected:BSON.DocumentView<[UInt8]>) + { + guard let tests:TestGroup + else + { + return + } + + let canonical:[UInt8] = Base16.decode(canonical.utf8) + let size:Int32 = canonical.prefix(4).withUnsafeBytes + { + .init(littleEndian: $0.load(as: Int32.self)) + } + + let document:BSON.DocumentView> = .init( + slicing: canonical.dropFirst(4).dropLast()) + + tests.expect(canonical.count ==? .init(size)) + tests.expect(document.header ==? size) + + tests.expect(true: expected ~~ document) + tests.expect(true: expected == document) + + if let degenerate:String + { + let degenerate:[UInt8] = Base16.decode(degenerate.utf8) + let document:BSON.DocumentView> = .init( + slicing: degenerate.dropFirst(4).dropLast()) + + (tests / "canonicalization")?.do + { + let canonicalized:BSON.DocumentView> = + try document.canonicalized() + + tests.expect(true: expected ~~ document) + tests.expect(true: expected == canonicalized) + } + } + } +} diff --git a/Sources/BSONTests/Main.swift b/Sources/BSONTests/Main.swift index f7c08a45..3b8d7cfb 100644 --- a/Sources/BSONTests/Main.swift +++ b/Sources/BSONTests/Main.swift @@ -1,789 +1,8 @@ -import Base16 -import BSON -import BSONReflection import Testing @main -enum Main:SyncTests +enum Main:TestMain { static - func run(tests:Tests) - { - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/multi-type.json - // cannot use this test, because it encodes a deprecated binary subtype, which is - // (intentionally) impossible to construct with swift-bson. - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/maxkey.json - do - { - TestValidBSON(tests / "max", - canonical: "080000007F610000", - expected: ["a": .max]) - } - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/minkey.json - do - { - TestValidBSON(tests / "min", - canonical: "08000000FF610000", - expected: ["a": .min]) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/null.json - do - { - TestValidBSON(tests / "null", - canonical: "080000000A610000", - expected: ["a": .null]) - } - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/undefined.json - do - { - TestValidBSON(tests / "undefined", - degenerate: "0800000006610000", - canonical: "080000000A610000", - expected: ["a": .null]) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/boolean.json - if let tests:TestGroup = tests / "bool" - { - - TestValidBSON(tests / "true", - canonical: "090000000862000100", - expected: ["b": true]) - TestValidBSON(tests / "false", - canonical: "090000000862000000", - expected: ["b": false]) - - TestInvalidBSON(tests / "invalid-subtype", - invalid: "090000000862000200", - catching: BSON.BooleanSubtypeError.init(invalid: 2)) - - TestInvalidBSON(tests / "invalid-subtype-negative", - invalid: "09000000086200FF00", - catching: BSON.BooleanSubtypeError.init(invalid: 255)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/int32.json - if let tests:TestGroup = tests / "int32" - { - - TestValidBSON(tests / "min", - canonical: "0C0000001069000000008000", - expected: ["i": .int32(-2147483648)]) - - TestValidBSON(tests / "max", - canonical: "0C000000106900FFFFFF7F00", - expected: ["i": .int32(2147483647)]) - - TestValidBSON(tests / "-1", - canonical: "0C000000106900FFFFFFFF00", - expected: ["i": .int32(-1)]) - - TestValidBSON(tests / "0", - canonical: "0C0000001069000000000000", - expected: ["i": .int32(0)]) - - TestValidBSON(tests / "+1", - canonical: "0C0000001069000100000000", - expected: ["i": .int32(1)]) - - TestInvalidBSON(tests / "truncated", - invalid: "09000000_10_6100_05_00", - catching: BSON.InputError.init(expected: .bytes(4), encountered: 1)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/int32.json - if let tests:TestGroup = tests / "int64" - { - - TestValidBSON(tests / "min", - canonical: "10000000126100000000000000008000", - expected: ["a": .int64(-9223372036854775808)]) - - TestValidBSON(tests / "max", - canonical: "10000000126100FFFFFFFFFFFFFF7F00", - expected: ["a": .int64(9223372036854775807)]) - - TestValidBSON(tests / "-1", - canonical: "10000000126100FFFFFFFFFFFFFFFF00", - expected: ["a": .int64(-1)]) - - TestValidBSON(tests / "0", - canonical: "10000000126100000000000000000000", - expected: ["a": .int64(0)]) - - TestValidBSON(tests / "+1", - canonical: "10000000126100010000000000000000", - expected: ["a": .int64(1)]) - - TestInvalidBSON(tests / "truncated", - invalid: "0C000000_12_6100_12345678_00", - catching: BSON.InputError.init(expected: .bytes(8), encountered: 4)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/timestamp.json - if let tests:TestGroup = tests / "uint64" - { - - TestValidBSON(tests / "(123456789, 42)", - canonical: "100000001161002A00000015CD5B0700", - expected: ["a": .uint64(123456789 << 32 | 42)]) - - TestValidBSON(tests / "ones", - canonical: "10000000116100FFFFFFFFFFFFFFFF00", - expected: ["a": .uint64(.max)]) - - TestValidBSON(tests / "(4000000000, 4000000000)", - canonical: "1000000011610000286BEE00286BEE00", - expected: ["a": .uint64(4000000000 << 32 | 4000000000)]) - - TestInvalidBSON(tests / "truncated", - invalid: "0F000000_11_6100_2A00000015CD5B_00", - catching: BSON.InputError.init(expected: .bytes(8), encountered: 7)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/top.json - if let tests:TestGroup = tests / "top" - { - - TestValidBSON(tests / "dollar-prefixed-key", - canonical: "0F00000010246B6579002A00000000", - expected: ["$key": .int32(42)]) - - TestValidBSON(tests / "dollar-key", - canonical: "0E00000002240002000000610000", - expected: ["$": "a"]) - - TestValidBSON(tests / "dotted-key", - canonical: "1000000002612E620002000000630000", - expected: ["a.b": "c"]) - - TestValidBSON(tests / "dot-key", - canonical: "0E000000022E0002000000610000", - expected: [".": "a"]) - - TestValidBSON(tests / "empty-truncated-header", - degenerate: "0100000000", - canonical: "0500000000", - expected: [:]) - - TestValidBSON(tests / "empty", - canonical: "0500000000", - expected: [:]) - - TestValidBSON(tests / "invalid-end-of-object-0x01", - degenerate: "05000000_01", - canonical: "05000000_00", - expected: [:]) - - TestValidBSON(tests / "invalid-end-of-object-0xff", - degenerate: "05000000_FF", - canonical: "05000000_00", - expected: [:]) - - TestValidBSON(tests / "invalid-end-of-object-0x70", - degenerate: "05000000_70", - canonical: "05000000_00", - expected: [:]) - - TestInvalidBSON(tests / "zeroes", - invalid: "00000000_000000000000", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: 0)) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "12000000_02_666F6F00_04000000_626172", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0x12 - 4), encountered: 12)) - - TestInvalidBSON(tests / "invalid-length-under", - invalid: "12000000_02_666F6F00_04000000_62617200_00_DEADBEEF", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .end, encountered: 4)) - - TestInvalidBSON(tests / "invalid-type-0x00", - invalid: "07000000_00_0000", - // ^~ - catching: BSON.TypeError.init(invalid: 0x00)) - - TestInvalidBSON(tests / "invalid-type-0x80", - invalid: "07000000_80_0000", - // ^~ - catching: BSON.TypeError.init(invalid: 0x80)) - - TestInvalidBSON(tests / "truncated", - invalid: "12000000_02_666F", - catching: BSON.InputError.init(expected: .bytes(0x12 - 4), encountered: 3)) - - TestInvalidBSON(tests / "invalid-key", - invalid: "0D000000_10_7800_00_0100000000", - // ^~ - catching: BSON.TypeError.init(invalid: 0x00)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/decimal128-1.json - if let tests:TestGroup = tests / "decimal128" - { - - TestValidBSON(tests / "positive-quiet-nan", - canonical: "180000001364000000000000000000000000000000007C00", - expected: ["d": .decimal128(.init( - high: 0x7C00_0000_0000_0000, - low: 0x0000_0000_0000_0000))]) - - TestValidBSON(tests / "negative-quiet-nan", - canonical: "18000000136400000000000000000000000000000000FC00", - expected: ["d": .decimal128(.init( - high: 0xFC00_0000_0000_0000, - low: 0x0000_0000_0000_0000))]) - - TestValidBSON(tests / "positive-signaling-nan", - canonical: "180000001364000000000000000000000000000000007E00", - expected: ["d": .decimal128(.init( - high: 0x7E00_0000_0000_0000, - low: 0x0000_0000_0000_0000))]) - - TestValidBSON(tests / "negative-signaling-nan", - canonical: "18000000136400000000000000000000000000000000FE00", - expected: ["d": .decimal128(.init( - high: 0xFE00_0000_0000_0000, - low: 0x0000_0000_0000_0000))]) - - // this only serves to verify we are handling byte-order correctly; - // there is very little point in elaborating decimal128 tests further - TestValidBSON(tests / "largest", - canonical: "18000000136400F2AF967ED05C82DE3297FF6FDE3C403000", - expected: ["d": .decimal128(.init( - high: 0x3040_3CDE_6FFF_9732, - low: 0xDE82_5CD0_7E96_AFF2))]) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/datetime.json - if let tests:TestGroup = tests / "millisecond" - { - - TestValidBSON(tests / "epoch", - canonical: "10000000096100000000000000000000", - expected: ["a": .millisecond(0)]) - - TestValidBSON(tests / "positive", - canonical: "10000000096100C5D8D6CC3B01000000", - expected: ["a": .millisecond(1356351330501)]) - - TestValidBSON(tests / "negative", - canonical: "10000000096100C33CE7B9BDFFFFFF00", - expected: ["a": .millisecond(-284643869501)]) - - TestValidBSON(tests / "positive-2", - canonical: "1000000009610000DC1FD277E6000000", - expected: ["a": .millisecond(253402300800000)]) - - TestValidBSON(tests / "positive-3", - canonical: "10000000096100D1D6D6CC3B01000000", - expected: ["a": .millisecond(1356351330001)]) - - TestInvalidBSON(tests / "truncated", - invalid: "0C000000_0961001234567800", - catching: BSON.InputError.init(expected: .bytes(8), encountered: 4)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/double.json - if let tests:TestGroup = tests / "double" - { - - TestValidBSON(tests / "+1.0", - canonical: "10000000016400000000000000F03F00", - expected: ["d": .double(1.0)]) - - TestValidBSON(tests / "-1.0", - canonical: "10000000016400000000000000F0BF00", - expected: ["d": .double(-1.0)]) - - TestValidBSON(tests / "+1.0001220703125", - canonical: "10000000016400000000008000F03F00", - expected: ["d": .double(1.0001220703125)]) - - TestValidBSON(tests / "-1.0001220703125", - canonical: "10000000016400000000008000F0BF00", - expected: ["d": .double(-1.0001220703125)]) - - TestValidBSON(tests / "1.2345678921232E+18", - canonical: "100000000164002a1bf5f41022b14300", - expected: ["d": .double(1.2345678921232e18)]) - - TestValidBSON(tests / "-1.2345678921232E+18", - canonical: "100000000164002a1bf5f41022b1c300", - expected: ["d": .double(-1.2345678921232e18)]) - - // remaining corpus test cases are pointless because swift cannot distinguish - // between -0.0 and +0.0 - - // note: frameshift - TestInvalidBSON(tests / "truncated", - invalid: "0B000000_0164000000F03F00", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .end, encountered: 1)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/oid.json - if let tests:TestGroup = tests / "id" - { - - let id:BSON.Identifier = 0x0123_4567_89AB_CDEF_4567_3210 - - tests.expect(id.timestamp ==? 0x0123_4567) - tests.expect(true: id.seed == (0x89, 0xAB, 0xCD, 0xEF, 0x45)) - tests.expect(true: id.ordinal == (0x67, 0x32, 0x10)) - - tests.expect(id ==? .init(timestamp: id.timestamp, seed: id.seed, ordinal: id.ordinal)) - - TestValidBSON(tests / "zeroes", - canonical: "1400000007610000000000000000000000000000", - expected: ["a": .id(0x00000000_00000000_00_000000)]) - - TestValidBSON(tests / "ones", - canonical: "14000000076100FFFFFFFFFFFFFFFFFFFFFFFF00", - expected: ["a": .id(0xffffffff_ffffffff_ff_ffffff)]) - - TestValidBSON(tests / "random", - canonical: "1400000007610056E1FC72E0C917E9C471416100", - expected: ["a": .id(0x56e1fc72_e0c917e9_c4_714161)]) - - TestInvalidBSON(tests / "truncated", - invalid: "12000000_07_6100_56E1FC72E0C917E9C471", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0x12 - 4), encountered: 13)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/dbpointer.json - if let tests:TestGroup = tests / "pointer" - { - - TestValidBSON(tests / "ascii", - canonical: "1A0000000C610002000000620056E1FC72E0C917E9C471416100", - expected: ["a": .pointer(.init(from: "b"), .init( - 0x56e1fc72, 0xe0c917e9, 0xc4_714161))]) - - TestValidBSON(tests / "unicode", - canonical: "1B0000000C610003000000C3A90056E1FC72E0C917E9C471416100", - expected: ["a": .pointer(.init(from: "é"), .init( - 0x56e1fc72, 0xe0c917e9, 0xc4_714161))]) - - TestInvalidBSON(tests / "invalid-length-negative", - invalid: "1A000000_0C_6100_FFFFFFFF_620056E1FC72E0C917E9C471416100", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: -1)) - - TestInvalidBSON(tests / "invalid-length-zero", - invalid: "1A000000_0C_6100_00000000_620056E1FC72E0C917E9C471416100", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: 0)) - - TestInvalidBSON(tests / "truncated", - invalid: "16000000_0C_6100_03000000_616200_56E1FC72E0C91700", - catching: BSON.InputError.init(expected: .bytes(12), encountered: 7)) - - TestInvalidBSON(tests / "truncated-identifier", - invalid: "1A000000_0C_6100_03000000_616200_56E1FC72E0C917E9C4716100", - catching: BSON.InputError.init(expected: .bytes(12), encountered: 11)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/binary.json - if let tests:TestGroup = tests / "binary" - { - - TestValidBSON(tests / "generic-empty", - canonical: "0D000000057800000000000000", - expected: ["x": .binary(.init(subtype: .generic, slice: []))]) - - TestValidBSON(tests / "generic", - canonical: "0F0000000578000200000000FFFF00", - expected: ["x": .binary(.init(subtype: .generic, - slice: Base16.decode("ffff")))]) - - TestValidBSON(tests / "function", - canonical: "0F0000000578000200000001FFFF00", - expected: ["x": .binary(.init(subtype: .function, - slice: Base16.decode("ffff")))]) - - TestValidBSON(tests / "uuid", - canonical: "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400", - expected: ["x": .binary(.init(subtype: .uuid, - slice: Base16.decode("73ffd26444b34c6990e8e7d1dfc035d4")))]) - - TestValidBSON(tests / "md5", - canonical: "1D000000057800100000000573FFD26444B34C6990E8E7D1DFC035D400", - expected: ["x": .binary(.init(subtype: .md5, - slice: Base16.decode("73ffd26444b34c6990e8e7d1dfc035d4")))]) - - TestValidBSON(tests / "compressed", - canonical: "1D000000057800100000000773FFD26444B34C6990E8E7D1DFC035D400", - expected: ["x": .binary(.init(subtype: .compressed, - slice: Base16.decode("73ffd26444b34c6990e8e7d1dfc035d4")))]) - - TestValidBSON(tests / "custom", - canonical: "0F0000000578000200000080FFFF00", - expected: ["x": .binary(.init(subtype: .custom(code: 0x80), - slice: Base16.decode("ffff")))]) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "1D000000_05_7800_FF000000_05_73FFD26444B34C6990E8E7D1DFC035D400", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0xFF + 1), encountered: 17)) - - TestInvalidBSON(tests / "invalid-length-negative", - invalid: "0D000000057800FFFFFFFF0000", - catching: BSON.BinaryViewError.init(expected: .subtype)) - // TODO: tests for legacy binary subtype 0x02 - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/document.json - if let tests:TestGroup = tests / "document" - { - - TestValidBSON(tests / "empty", - canonical: "0D000000037800050000000000", - expected: ["x": [:]]) - - TestValidBSON(tests / "empty-key", - canonical: "150000000378000D00000002000200000062000000", - expected: ["x": ["": "b"]]) - - TestValidBSON(tests / "single-character-key", - canonical: "160000000378000E0000000261000200000062000000", - expected: ["x": ["a": "b"]]) - - TestValidBSON(tests / "dollar-prefixed-key", - canonical: "170000000378000F000000022461000200000062000000", - expected: ["x": ["$a": "b"]]) - - TestValidBSON(tests / "dollar-key", - canonical: "160000000378000E0000000224000200000061000000", - expected: ["x": ["$": "a"]]) - - TestValidBSON(tests / "dotted-key", - canonical: "180000000378001000000002612E62000200000063000000", - expected: ["x": ["a.b": "c"]]) - - TestValidBSON(tests / "dot-key", - canonical: "160000000378000E000000022E000200000061000000", - expected: ["x": [".": "a"]]) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "18000000_03_666F6F00_0F000000_10_62617200_FFFFFF7F_0000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0x0F - 4), encountered: 10)) - - TestInvalidBSON(tests / "invalid-length-under", - invalid: "15000000_03_666F6F00_0A000000_08_62617200_01_00_00", - // ^~ - catching: BSON.InputError.init(expected: .bytes(1))) - - TestInvalidBSON(tests / "invalid-value", - invalid: "1C000000_03_666F6F00_12000000_02_62617200_05000000_62617A000000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) - - TestInvalidBSON(tests / "invalid-key", - invalid: "15000000_03_7800_0D000000_10_6100_00010000_00_0000", - // ^~ - catching: BSON.TypeError.init(invalid: 0)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/array.json - if let tests:TestGroup = tests / "tuple" - { - - TestValidBSON(tests / "empty", - canonical: "0D000000046100050000000000", - expected: ["a": []]) - TestValidBSON(tests / "single-element", - canonical: "140000000461000C0000001030000A0000000000", - expected: ["a": [.int32(10)]]) - - TestValidBSON(tests / "single-element-empty-key", - degenerate: "130000000461000B00000010000A0000000000", - canonical: "140000000461000C0000001030000A0000000000", - expected: ["a": [.int32(10)]]) - - TestValidBSON(tests / "single-element-invalid-key", - degenerate: "150000000461000D000000106162000A0000000000", - canonical: "140000000461000C0000001030000A0000000000", - expected: ["a": [.int32(10)]]) - - TestValidBSON(tests / "multiple-element-duplicate-keys", - degenerate: "1b000000046100130000001030000a000000103000140000000000", - canonical: "1b000000046100130000001030000a000000103100140000000000", - expected: ["a": [.int32(10), .int32(20)]]) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "14000000_04_6100_0D000000_10_30000A00_00_000000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0x0D - 4), encountered: 8)) - - TestInvalidBSON(tests / "invalid-length-under", - invalid: "14000000_04_6100_0B000000_10_30000A00_00_000000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(4), encountered: 3)) - - TestInvalidBSON(tests / "invalid-element", - invalid: "1A000000_04_666F6F00_100000000230000500000062617A000000", - // - catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/regex.json - if let tests:TestGroup = tests / "regex" - { - - TestValidBSON(tests / "empty", - canonical: "0A0000000B6100000000", - expected: ["a": .regex(.init(pattern: "", options: []))]) - - TestValidBSON(tests / "empty-options", - canonical: "0D0000000B6100616263000000", - expected: ["a": .regex(.init(pattern: "abc", options: []))]) - - TestValidBSON(tests / "I-HAVE-OPTIONS", - canonical: "0F0000000B610061626300696D0000", - expected: ["a": .regex(.init(pattern: "abc", options: [.i, .m]))]) - - TestValidBSON(tests / "slash", - canonical: "110000000B610061622F636400696D0000", - expected: ["a": .regex(.init(pattern: "ab/cd", options: [.i, .m]))]) - - TestValidBSON(tests / "non-alphabetized", - degenerate: "100000000B6100616263006D69780000", - canonical: "100000000B610061626300696D780000", - expected: ["a": .regex(.init(pattern: "abc", options: [.i, .m, .x]))]) - - TestValidBSON(tests / "escaped", - canonical: "100000000B610061625C226162000000", - expected: ["a": .regex(.init(pattern: #"ab\"ab"#, options: []))]) - - // note: frameshift - TestInvalidBSON(tests / "invalid-pattern", - invalid: "0F0000000B610061006300696D0000", - catching: BSON.Regex.OptionError.init(invalid: "c")) - // note: frameshift - TestInvalidBSON(tests / "invalid-options", - invalid: "100000000B61006162630069006D0000", - catching: BSON.TypeError.init(invalid: 109)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/string.json - if let tests:TestGroup = tests / "string" - { - - TestValidBSON(tests / "empty", - canonical: "0D000000026100010000000000", - expected: ["a": ""]) - - TestValidBSON(tests / "single-character", - canonical: "0E00000002610002000000620000", - expected: ["a": "b"]) - - TestValidBSON(tests / "multiple-character", - canonical: "190000000261000D0000006162616261626162616261620000", - expected: ["a": "abababababab"]) - - TestValidBSON(tests / "utf-8-double-code-unit", - canonical: "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", - expected: ["a": "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"]) - - TestValidBSON(tests / "utf-8-triple-code-unit", - canonical: "190000000261000D000000E29886E29886E29886E298860000", - expected: ["a": "\u{2606}\u{2606}\u{2606}\u{2606}"]) - - TestValidBSON(tests / "utf-8-null-bytes", - canonical: "190000000261000D0000006162006261620062616261620000", - expected: ["a": "ab\u{00}bab\u{00}babab"]) - - TestValidBSON(tests / "escaped", - canonical: - """ - 3200000002610026000000\ - 61625C220102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F6162\ - 0000 - """, - expected: - [ - "a": - """ - ab\\\"\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\ - \t\n\u{0b}\u{0c}\r\u{0e}\u{0f}\u{10}\ - \u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\ - \u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}ab - """ - ]) - - TestInvalidBSON(tests / "missing-trailing-null-byte", - invalid: "0C000000_02_6100_00000000_00", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: 0)) - - TestInvalidBSON(tests / "invalid-length-negative", - invalid: "0C000000_02_6100_FFFFFFFF_00", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: -1)) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "10000000_02_6100_05000000_62006200_00", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) - - TestInvalidBSON(tests / "invalid-length-over-document", - invalid: "12000000_02_00_FFFFFF00_666F6F6261720000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0xffffff), encountered: 7)) - - TestInvalidBSON(tests / "invalid-length-under", - invalid: "0E000000_02_6100_01000000_00_00_00", - // ^~ - catching: BSON.TypeError.init(invalid: 0x00)) - - TestValidBSON(tests / "invalid-utf-8", - canonical: "0E00000002610002000000E90000", - expected: ["a": .string(.init(slice: [0xe9]))]) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/symbol.json - if let tests:TestGroup = tests / "symbol" - { - - TestValidBSON(tests / "empty", - degenerate: "0D0000000E6100010000000000", - canonical: "0D000000026100010000000000", - expected: ["a": ""]) - - TestValidBSON(tests / "single-character", - degenerate: "0E0000000E610002000000620000", - canonical: "0E00000002610002000000620000", - expected: ["a": "b"]) - - TestValidBSON(tests / "multiple-character", - degenerate: "190000000E61000D0000006162616261626162616261620000", - canonical: "190000000261000D0000006162616261626162616261620000", - expected: ["a": "abababababab"]) - - TestValidBSON(tests / "utf-8-double-code-unit", - degenerate: "190000000E61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", - canonical: "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", - expected: ["a": "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"]) - - TestValidBSON(tests / "utf-8-triple-code-unit", - degenerate: "190000000E61000D000000E29886E29886E29886E298860000", - canonical: "190000000261000D000000E29886E29886E29886E298860000", - expected: ["a": "\u{2606}\u{2606}\u{2606}\u{2606}"]) - - TestValidBSON(tests / "utf-8-null-bytes", - degenerate: "190000000E61000D0000006162006261620062616261620000", - canonical: "190000000261000D0000006162006261620062616261620000", - expected: ["a": "ab\u{00}bab\u{00}babab"]) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/code.json - if let tests:TestGroup = tests / "javascript" - { - - TestValidBSON(tests / "empty", - canonical: "0D0000000D6100010000000000", - expected: ["a": .javascript(.init(from: ""))]) - - TestValidBSON(tests / "single-character", - canonical: "0E0000000D610002000000620000", - expected: ["a": .javascript(.init(from: "b"))]) - - TestValidBSON(tests / "multiple-character", - canonical: "190000000D61000D0000006162616261626162616261620000", - expected: ["a": .javascript(.init(from: "abababababab"))]) - - TestValidBSON(tests / "utf-8-double-code-unit", - canonical: "190000000D61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", - expected: ["a": .javascript(.init(from: "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"))]) - - TestValidBSON(tests / "utf-8-triple-code-unit", - canonical: "190000000D61000D000000E29886E29886E29886E298860000", - expected: ["a": .javascript(.init(from: "\u{2606}\u{2606}\u{2606}\u{2606}"))]) - - TestValidBSON(tests / "utf-8-null-bytes", - canonical: "190000000D61000D0000006162006261620062616261620000", - expected: ["a": .javascript(.init(from: "ab\u{00}bab\u{00}babab"))]) - - TestInvalidBSON(tests / "missing-trailing-null-byte", - invalid: "0C000000_0D_6100_00000000_00", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: 0)) - - TestInvalidBSON(tests / "invalid-length-negative", - invalid: "0C0000000D6100FFFFFFFF00", - catching: BSON.HeaderError.init(length: -1)) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "10000000_0D_6100_05000000_6200620000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(5), encountered: 4)) - - TestInvalidBSON(tests / "invalid-length-over-document", - invalid: "12000000_0D_00_FFFFFF00_666F6F6261720000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0xffffff), encountered: 7)) - - TestInvalidBSON(tests / "invalid-length-under", - invalid: "0E000000_0D_6100_01000000_00_00_00", - // ^~ - catching: BSON.TypeError.init(invalid: 0x00)) - } - - // https://github.com/mongodb/specifications/blob/master/source/bson-corpus/tests/code_w_scope.json - if let tests:TestGroup = tests / "javascript-scope" - { - - TestValidBSON(tests / "empty", - canonical: "160000000F61000E0000000100000000050000000000", - expected: ["a": .javascriptScope([:], .init(from: ""))]) - - TestValidBSON(tests / "empty-scope", - canonical: "1A0000000F610012000000050000006162636400050000000000", - expected: ["a": .javascriptScope([:], .init(from: "abcd"))]) - - TestValidBSON(tests / "empty-code", - canonical: "1D0000000F61001500000001000000000C000000107800010000000000", - expected: ["a": .javascriptScope(["x": .int32(1)], .init(from: ""))]) - - TestValidBSON(tests / "non-empty", - canonical: "210000000F6100190000000500000061626364000C000000107800010000000000", - expected: ["a": .javascriptScope(["x": .int32(1)], .init(from: "abcd"))]) - - TestValidBSON(tests / "unicode", - canonical: "1A0000000F61001200000005000000C3A9006400050000000000", - expected: ["a": .javascriptScope([:], .init(from: "\u{e9}\u{00}d"))]) - - // note: we do not validate the redundant field length, - // so those tests are not included - - // note: the length is actually too short, but because we use the component-wise - // length headers instead of the field length, this manifests itself as a - // frameshift error. - TestInvalidBSON(tests / "invalid-length-frameshift-clips-scope", - invalid: "28000000_0F_6100_20000000_04000000_61626364_00130000_0010780001000000107900010000000000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0x00_00_13_00 - 4), encountered: 16)) - - TestInvalidBSON(tests / "invalid-length-over", - invalid: "28000000_0F_6100_20000000_06000000_616263640013_00000010_780001000000107900010000000000", - // ^~~~~~~~ - catching: BSON.InputError.init(expected: .bytes(0x10_00_00_00 - 4), encountered: 14)) - // note: frameshift - TestInvalidBSON(tests / "invalid-length-frameshift", - invalid: "28000000_0F_6100_20000000_FF000000_61626364001300000010780001000000107900010000000000", - catching: BSON.InputError.init(expected: .bytes(255), encountered: 24)) - - TestInvalidBSON(tests / "invalid-scope", - invalid: "1C000000_0F_00_15000000_01000000_00_0C000000_02_00000000_00000000", - // ^~~~~~~~ - catching: BSON.HeaderError.init(length: 0)) - } - } + let all:[any TestBattery.Type] = [ValidBSON.self, InvalidBSON.self] } diff --git a/Sources/BSONTests/TestInvalidBSON.swift b/Sources/BSONTests/TestInvalidBSON.swift deleted file mode 100644 index 04a0b746..00000000 --- a/Sources/BSONTests/TestInvalidBSON.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Base16 -import BSON -import BSONReflection -import Testing - -func TestInvalidBSON(_ tests:TestGroup?, invalid:String, catching error:some Error & Equatable) -{ - guard let tests:TestGroup - else - { - return - } - - let invalid:[UInt8] = Base16.decode(invalid.utf8) - - var input:BSON.Input<[UInt8]> = .init(invalid) - - tests.do(catching: error) - { - let document:BSON.DocumentView> = try input.parse( - as: BSON.DocumentView>.self) - try input.finish() - _ = try document.canonicalized() - } -} diff --git a/Sources/BSONTests/TestValidBSON.swift b/Sources/BSONTests/TestValidBSON.swift deleted file mode 100644 index e3499226..00000000 --- a/Sources/BSONTests/TestValidBSON.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Base16 -import BSON -import BSONReflection -import Testing - -func TestValidBSON(_ tests:TestGroup?, - degenerate:String? = nil, - canonical:String, - expected:BSON.DocumentView<[UInt8]>) -{ - guard let tests:TestGroup - else - { - return - } - - let canonical:[UInt8] = Base16.decode(canonical.utf8) - let size:Int32 = canonical.prefix(4).withUnsafeBytes - { - .init(littleEndian: $0.load(as: Int32.self)) - } - - let document:BSON.DocumentView> = .init( - slicing: canonical.dropFirst(4).dropLast()) - - tests.expect(canonical.count ==? .init(size)) - tests.expect(document.header ==? size) - - tests.expect(true: expected ~~ document) - tests.expect(true: expected == document) - - if let degenerate:String - { - let degenerate:[UInt8] = Base16.decode(degenerate.utf8) - let document:BSON.DocumentView> = .init( - slicing: degenerate.dropFirst(4).dropLast()) - - (tests / "canonicalization")?.do - { - let canonicalized:BSON.DocumentView> = - try document.canonicalized() - - tests.expect(true: expected ~~ document) - tests.expect(true: expected == canonicalized) - } - } -} diff --git a/Sources/BSONTypes/BSON.DocumentView.swift b/Sources/BSONTypes/BSON.DocumentView.swift deleted file mode 100644 index af59f772..00000000 --- a/Sources/BSONTypes/BSON.DocumentView.swift +++ /dev/null @@ -1,76 +0,0 @@ -import BSONTraversal - -extension BSON -{ - /// A BSON document. The backing storage of this type is opaque, - /// permitting lazy parsing of its inline content. - @frozen public - struct DocumentView where Bytes:RandomAccessCollection - { - /// The raw data backing this document. This collection *does not* - /// include the trailing null byte that typically appears after its - /// inline field list. - public - let slice:Bytes - - /// Stores the argument in ``slice`` unchanged. - /// - /// > Complexity: O(1) - @inlinable public - init(slice:Bytes) - { - self.slice = slice - } - } -} -extension BSON.DocumentView:Equatable -{ - /// Performs an exact byte-wise comparison on two lists. - /// Does not parse or validate the operands. - @inlinable public static - func == (lhs:Self, rhs:BSON.DocumentView>) -> Bool - { - lhs.slice.elementsEqual(rhs.slice) - } -} -extension BSON.DocumentView:Sendable where Bytes:Sendable -{ -} -extension BSON.DocumentView:VariableLengthBSON -{ - public - typealias Frame = BSON.DocumentFrame - - /// Stores the argument in ``slice`` unchanged. Equivalent to ``init(slice:)``. - /// - /// > Complexity: O(1) - @inlinable public - init(slicing bytes:Bytes) - { - self.init(slice: bytes) - } -} -extension BSON.DocumentView:BSONView -{ - @inlinable public - init(_ value:BSON.AnyValue) throws - { - self = try value.cast(with: \.document) - } -} -extension BSON.DocumentView -{ - /// Indicates if this document contains no fields. - @inlinable public - var isEmpty:Bool { self.slice.isEmpty } - - /// The length that would be encoded in this document’s prefixed header. - /// Equal to ``size``. - @inlinable public - var header:Int32 { .init(self.size) } - - /// The size of this document when encoded with its header and trailing - /// null byte. This *is* the same as the length encoded in the header itself. - @inlinable public - var size:Int { 5 + self.slice.count } -} diff --git a/Sources/BSONTypes/BSON.ListView.swift b/Sources/BSONTypes/BSON.ListView.swift deleted file mode 100644 index ae8c877c..00000000 --- a/Sources/BSONTypes/BSON.ListView.swift +++ /dev/null @@ -1,77 +0,0 @@ -import BSONTraversal - -extension BSON -{ - /// A BSON list. The backing storage of this type is opaque, - /// permitting lazy parsing of its inline content. - @frozen public - struct ListView where Bytes:RandomAccessCollection - { - public - let document:BSON.DocumentView - - @inlinable public - init(slice:Bytes) - { - self.document = .init(slice: slice) - } - } -} -extension BSON.ListView:Equatable -{ - /// Performs an exact byte-wise comparison on two lists. - /// Does not parse or validate the operands. - @inlinable public static - func == (lhs:Self, rhs:BSON.ListView>) -> Bool - { - lhs.document == rhs.document - } -} -extension BSON.ListView:Sendable where Bytes:Sendable -{ -} -extension BSON.ListView:VariableLengthBSON -{ - public - typealias Frame = BSON.DocumentFrame - - /// Stores the argument in ``slice`` unchanged. Equivalent to ``init(slice:)``. - /// - /// > Complexity: O(1) - @inlinable public - init(slicing bytes:Bytes) - { - self.init(slice: bytes) - } -} -extension BSON.ListView:BSONView -{ - @inlinable public - init(_ value:BSON.AnyValue) throws - { - self = try value.cast(with: \.list) - } -} -extension BSON.ListView -{ - /// The raw data backing this list. This collection *does not* - /// include the trailing null byte that appears after its inline - /// elements list. - @inlinable public - var slice:Bytes { self.document.slice } - - /// Indicates if this list contains no elements. - @inlinable public - var isEmpty:Bool { self.slice.isEmpty } - - /// The length that would be encoded in this list’s prefixed header. - /// Equal to ``size``. - @inlinable public - var header:Int32 { .init(self.size) } - - /// The size of this list when encoded with its header and trailing - /// null byte. This *is* the same as the length encoded in the header - /// itself. - @inlinable public - var size:Int { 5 + self.slice.count } -} diff --git a/Sources/BSONTypes/BSONView.swift b/Sources/BSONTypes/BSONView.swift deleted file mode 100644 index 01a269af..00000000 --- a/Sources/BSONTypes/BSONView.swift +++ /dev/null @@ -1,17 +0,0 @@ -/// A type that can be (efficiently) decoded from a BSON variant value -/// backed by a preferred type of storage particular to the decoded type. -/// -/// This protocol is parallel and unrelated to ``BSONDecodable`` to -/// emphasize the performance characteristics of types that conform to -/// this protocol and not ``BSONDecodable``. -public -protocol BSONView:Equatable -{ - /// The backing storage used by this type. It is recommended that - /// implementations satisfy this with generics. - associatedtype Bytes:RandomAccessCollection - - /// Attempts to cast a BSON variant backed by ``Bytes`` to an instance - /// of this view type without copying the contents of the backing storage. - init(_:BSON.AnyValue) throws -} diff --git a/Sources/BSON_Durations/Milliseconds (ext).swift b/Sources/BSON_Durations/Milliseconds (ext).swift index 5877bd0f..d95501b4 100644 --- a/Sources/BSON_Durations/Milliseconds (ext).swift +++ b/Sources/BSON_Durations/Milliseconds (ext).swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import Durations extension Milliseconds:BSONDecodable, BSONEncodable diff --git a/Sources/BSON_Durations/Minutes (ext).swift b/Sources/BSON_Durations/Minutes (ext).swift index 0f3f2425..94786302 100644 --- a/Sources/BSON_Durations/Minutes (ext).swift +++ b/Sources/BSON_Durations/Minutes (ext).swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import Durations extension Minutes:BSONDecodable, BSONEncodable diff --git a/Sources/BSON_Durations/Seconds (ext).swift b/Sources/BSON_Durations/Seconds (ext).swift index 75a19d50..0125adf6 100644 --- a/Sources/BSON_Durations/Seconds (ext).swift +++ b/Sources/BSON_Durations/Seconds (ext).swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import Durations extension Seconds:BSONDecodable, BSONEncodable diff --git a/Sources/BSON_OrderedCollections/OrderedDictionary (ext).swift b/Sources/BSON_OrderedCollections/OrderedDictionary (ext).swift index 9d538c05..e73424ce 100644 --- a/Sources/BSON_OrderedCollections/OrderedDictionary (ext).swift +++ b/Sources/BSON_OrderedCollections/OrderedDictionary (ext).swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import OrderedCollections extension OrderedDictionary:BSONDocumentViewDecodable, BSONDecodable @@ -11,7 +10,7 @@ extension OrderedDictionary:BSONDocumentViewDecodable, BSONDecodable self.init() try bson.parse { - (field:BSON.ExplicitField) in + (field:BSON.FieldDecoder) in if case _? = self.updateValue(try field.decode(to: Value.self), forKey: field.key.rawValue) diff --git a/Sources/BSON_UUID/UUID (ext).swift b/Sources/BSON_UUID/UUID (ext).swift index fa84f444..b5946787 100644 --- a/Sources/BSON_UUID/UUID (ext).swift +++ b/Sources/BSON_UUID/UUID (ext).swift @@ -1,6 +1,5 @@ +import BSON import UUID -import BSONDecoding -import BSONEncoding extension UUID:BSONDecodable, BSONBinaryViewDecodable { @@ -16,7 +15,7 @@ extension UUID:BSONDecodable, BSONBinaryViewDecodable extension UUID:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(binary: .init(subtype: .uuid, slice: self)) } diff --git a/Sources/Mongo/README.md b/Sources/Mongo/README.md new file mode 100644 index 00000000..b0f7f587 --- /dev/null +++ b/Sources/Mongo/README.md @@ -0,0 +1,3 @@ +# ``Mongo`` + +A single-type module that declares the ``Mongo`` namespace. diff --git a/Sources/MongoSchema/Mongo.Collection.swift b/Sources/MongoABI/Modeling/Mongo.Collection.swift similarity index 94% rename from Sources/MongoSchema/Mongo.Collection.swift rename to Sources/MongoABI/Modeling/Mongo.Collection.swift index 4c7ae9e1..4d1958e5 100644 --- a/Sources/MongoSchema/Mongo.Collection.swift +++ b/Sources/MongoABI/Modeling/Mongo.Collection.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoSchema/Mongo.Database.swift b/Sources/MongoABI/Modeling/Mongo.Database.swift similarity index 95% rename from Sources/MongoSchema/Mongo.Database.swift rename to Sources/MongoABI/Modeling/Mongo.Database.swift index 450ac127..34cb16bb 100644 --- a/Sources/MongoSchema/Mongo.Database.swift +++ b/Sources/MongoABI/Modeling/Mongo.Database.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoSchema/Mongo.Namespaced.swift b/Sources/MongoABI/Modeling/Mongo.Namespaced.swift similarity index 97% rename from Sources/MongoSchema/Mongo.Namespaced.swift rename to Sources/MongoABI/Modeling/Mongo.Namespaced.swift index ec7b6071..50a87b68 100644 --- a/Sources/MongoSchema/Mongo.Namespaced.swift +++ b/Sources/MongoABI/Modeling/Mongo.Namespaced.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoSchema/Mongo.KeyPath.swift b/Sources/MongoABI/Mongo.KeyPath.swift similarity index 97% rename from Sources/MongoSchema/Mongo.KeyPath.swift rename to Sources/MongoABI/Mongo.KeyPath.swift index cca78ef0..2eb931fe 100644 --- a/Sources/MongoSchema/Mongo.KeyPath.swift +++ b/Sources/MongoABI/Mongo.KeyPath.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoSchema/Mongo.Variable.swift b/Sources/MongoABI/Mongo.Variable.swift similarity index 97% rename from Sources/MongoSchema/Mongo.Variable.swift rename to Sources/MongoABI/Mongo.Variable.swift index fc13064f..6d3fe880 100644 --- a/Sources/MongoSchema/Mongo.Variable.swift +++ b/Sources/MongoABI/Mongo.Variable.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoSchema/MongoMasterCodingModel.swift b/Sources/MongoABI/MongoMasterCodingModel.swift similarity index 92% rename from Sources/MongoSchema/MongoMasterCodingModel.swift rename to Sources/MongoABI/MongoMasterCodingModel.swift index 022b37fc..a7c749a2 100644 --- a/Sources/MongoSchema/MongoMasterCodingModel.swift +++ b/Sources/MongoABI/MongoMasterCodingModel.swift @@ -5,7 +5,7 @@ /// representations. A master coding model is a type that defines the full set of /// coding keys used to encode the documents. public -protocol MongoMasterCodingModel +protocol MongoMasterCodingModel { associatedtype CodingKey:RawRepresentable } diff --git a/Sources/MongoDriver/Outlining/Mongo.OutlineType.swift b/Sources/MongoABI/Outlining/Mongo.OutlineType.swift similarity index 100% rename from Sources/MongoDriver/Outlining/Mongo.OutlineType.swift rename to Sources/MongoABI/Outlining/Mongo.OutlineType.swift diff --git a/Sources/MongoDriver/Outlining/Mongo.OutlineVector.swift b/Sources/MongoABI/Outlining/Mongo.OutlineVector.swift similarity index 58% rename from Sources/MongoDriver/Outlining/Mongo.OutlineVector.swift rename to Sources/MongoABI/Outlining/Mongo.OutlineVector.swift index 585b3760..165e044f 100644 --- a/Sources/MongoDriver/Outlining/Mongo.OutlineVector.swift +++ b/Sources/MongoABI/Outlining/Mongo.OutlineVector.swift @@ -1,4 +1,4 @@ -import MongoWire +import BSON extension Mongo { @@ -6,14 +6,14 @@ extension Mongo struct OutlineVector:Sendable { public - let documents:OutlineDocuments + let bson:BSON.Output<[UInt8]> public let type:OutlineType @inlinable public - init(_ documents:OutlineDocuments, type:OutlineType) + init(bson:BSON.Output<[UInt8]>, type:OutlineType) { - self.documents = documents + self.bson = bson self.type = type } } diff --git a/Sources/MongoSchema/exports.swift b/Sources/MongoABI/exports.swift similarity index 100% rename from Sources/MongoSchema/exports.swift rename to Sources/MongoABI/exports.swift diff --git a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.Superlative.swift b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.Superlative.swift index d3538fd3..c51a2346 100644 --- a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.Superlative.swift +++ b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.Superlative.swift @@ -16,10 +16,10 @@ extension Mongo.Accumulator.Superlative { switch self { - case .first: return "$firstN" - case .last: return "$lastN" - case .max: return "$maxN" - case .min: return "$minN" + case .first: "$firstN" + case .last: "$lastN" + case .max: "$maxN" + case .min: "$minN" } } } diff --git a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeDocument.swift b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeDocument.swift index 5bd9b423..d1dd1725 100644 --- a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeDocument.swift +++ b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.Accumulator { diff --git a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSort.swift b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSort.swift index d85e2ed2..0e3270f4 100644 --- a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSort.swift +++ b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSort.swift @@ -14,8 +14,8 @@ extension Mongo.Accumulator.SuperlativeSort { switch self { - case .bottom: return "$bottomN" - case .top: return "$topN" + case .bottom: "$bottomN" + case .top: "$topN" } } } diff --git a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSortDocument.swift b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSortDocument.swift index 8d0610bc..493a596d 100644 --- a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSortDocument.swift +++ b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.SuperlativeSortDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.Accumulator { diff --git a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.swift b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.swift index 7b4ae9ba..7f5e0245 100644 --- a/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.swift +++ b/Sources/MongoBuiltins/Accumulators/Mongo.Accumulator.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Expressions/BSONEncodable (ext).swift b/Sources/MongoBuiltins/BSONEncodable (ext).swift similarity index 95% rename from Sources/MongoBuiltins/Expressions/BSONEncodable (ext).swift rename to Sources/MongoBuiltins/BSONEncodable (ext).swift index 4dde63af..41ea372b 100644 --- a/Sources/MongoBuiltins/Expressions/BSONEncodable (ext).swift +++ b/Sources/MongoBuiltins/BSONEncodable (ext).swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON // This must be an extension on ``Optional`` and not ``BSONEncodable`` // because SE-299 does not support protocol extension member lookup with diff --git a/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketAutoDocument.swift b/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketAutoDocument.swift index f605913f..df21dc7d 100644 --- a/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketAutoDocument.swift +++ b/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketAutoDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketDocument.swift b/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketDocument.swift index 6b016896..9f3c280f 100644 --- a/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketDocument.swift +++ b/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketOutputDocument.swift b/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketOutputDocument.swift index bcba0380..a747a243 100644 --- a/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketOutputDocument.swift +++ b/Sources/MongoBuiltins/Documents/Bucket/Mongo.BucketOutputDocument.swift @@ -1,6 +1,5 @@ -import BSONDecoding -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Bucket/Mongo.PreferredNumbers.swift b/Sources/MongoBuiltins/Documents/Bucket/Mongo.PreferredNumbers.swift index e041be60..85fddf94 100644 --- a/Sources/MongoBuiltins/Documents/Bucket/Mongo.PreferredNumbers.swift +++ b/Sources/MongoBuiltins/Documents/Bucket/Mongo.PreferredNumbers.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.CollectionStatsDocument.swift b/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.CollectionStatsDocument.swift index 1a03b4eb..45a055f4 100644 --- a/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.CollectionStatsDocument.swift +++ b/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.CollectionStatsDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.LatencyStatsDocument.swift b/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.LatencyStatsDocument.swift index 4b05187e..1003fc66 100644 --- a/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.LatencyStatsDocument.swift +++ b/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.LatencyStatsDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.StorageStatsDocument.swift b/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.StorageStatsDocument.swift index 02d19a8f..b0931cc1 100644 --- a/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.StorageStatsDocument.swift +++ b/Sources/MongoBuiltins/Documents/CollectionStats/Mongo.StorageStatsDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/CurrentOperation/Mongo.CurrentOperationDocument.swift b/Sources/MongoBuiltins/Documents/CurrentOperation/Mongo.CurrentOperationDocument.swift index 23dbacda..c9d00a89 100644 --- a/Sources/MongoBuiltins/Documents/CurrentOperation/Mongo.CurrentOperationDocument.swift +++ b/Sources/MongoBuiltins/Documents/CurrentOperation/Mongo.CurrentOperationDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Facet/Mongo.FacetDocument.swift b/Sources/MongoBuiltins/Documents/Facet/Mongo.FacetDocument.swift index 4eeba6fb..577e491a 100644 --- a/Sources/MongoBuiltins/Documents/Facet/Mongo.FacetDocument.swift +++ b/Sources/MongoBuiltins/Documents/Facet/Mongo.FacetDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Filter/Mongo.FilterDocument.swift b/Sources/MongoBuiltins/Documents/Filter/Mongo.FilterDocument.swift index 9cb9491c..3747af05 100644 --- a/Sources/MongoBuiltins/Documents/Filter/Mongo.FilterDocument.swift +++ b/Sources/MongoBuiltins/Documents/Filter/Mongo.FilterDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Group/Mongo.GroupDocument.swift b/Sources/MongoBuiltins/Documents/Group/Mongo.GroupDocument.swift index 7828ad3a..6b604a50 100644 --- a/Sources/MongoBuiltins/Documents/Group/Mongo.GroupDocument.swift +++ b/Sources/MongoBuiltins/Documents/Group/Mongo.GroupDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Let/Mongo.LetDocument.swift b/Sources/MongoBuiltins/Documents/Let/Mongo.LetDocument.swift index 8a289d14..b3e09e6a 100644 --- a/Sources/MongoBuiltins/Documents/Let/Mongo.LetDocument.swift +++ b/Sources/MongoBuiltins/Documents/Let/Mongo.LetDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Lookup/Mongo.LookupDocument.swift b/Sources/MongoBuiltins/Documents/Lookup/Mongo.LookupDocument.swift index 76244abf..d5988324 100644 --- a/Sources/MongoBuiltins/Documents/Lookup/Mongo.LookupDocument.swift +++ b/Sources/MongoBuiltins/Documents/Lookup/Mongo.LookupDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Map/Mongo.MapDocument.swift b/Sources/MongoBuiltins/Documents/Map/Mongo.MapDocument.swift index be855233..b604e84f 100644 --- a/Sources/MongoBuiltins/Documents/Map/Mongo.MapDocument.swift +++ b/Sources/MongoBuiltins/Documents/Map/Mongo.MapDocument.swift @@ -1,6 +1,5 @@ -import BSONDecoding -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeDocument.swift b/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeDocument.swift index ff6c8ab5..63430390 100644 --- a/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeDocument.swift +++ b/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeInsertMode.swift b/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeInsertMode.swift index 70683eb7..57d03194 100644 --- a/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeInsertMode.swift +++ b/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeInsertMode.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeUpdateMode.swift b/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeUpdateMode.swift index 1462264a..102a782b 100644 --- a/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeUpdateMode.swift +++ b/Sources/MongoBuiltins/Documents/Merge/Mongo.MergeUpdateMode.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Mongo.KeyPath (ext).swift b/Sources/MongoBuiltins/Documents/Mongo.KeyPath (ext).swift index db4c5b16..ba1fe3a3 100644 --- a/Sources/MongoBuiltins/Documents/Mongo.KeyPath (ext).swift +++ b/Sources/MongoBuiltins/Documents/Mongo.KeyPath (ext).swift @@ -1,4 +1,4 @@ -import MongoSchema +import MongoABI extension Mongo.KeyPath:_MongoExpressionRestrictedEncodable { diff --git a/Sources/MongoBuiltins/Documents/Mongo.Variable (ext).swift b/Sources/MongoBuiltins/Documents/Mongo.Variable (ext).swift index 626716ae..9a248708 100644 --- a/Sources/MongoBuiltins/Documents/Mongo.Variable (ext).swift +++ b/Sources/MongoBuiltins/Documents/Mongo.Variable (ext).swift @@ -1,4 +1,4 @@ -import MongoSchema +import MongoABI extension Mongo.Variable:_MongoExpressionRestrictedEncodable { diff --git a/Sources/MongoBuiltins/Documents/MongoDocumentDSL.swift b/Sources/MongoBuiltins/Documents/MongoDocumentDSL.swift index 53dcd178..983af228 100644 --- a/Sources/MongoBuiltins/Documents/MongoDocumentDSL.swift +++ b/Sources/MongoBuiltins/Documents/MongoDocumentDSL.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON /// A `MongoDocumentDSL` is nothing more than a type that supports an /// ``init(with:)`` builder API. diff --git a/Sources/MongoBuiltins/Documents/MongoListDSL.swift b/Sources/MongoBuiltins/Documents/MongoListDSL.swift index 023e6fd1..0b1221a1 100644 --- a/Sources/MongoBuiltins/Documents/MongoListDSL.swift +++ b/Sources/MongoBuiltins/Documents/MongoListDSL.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON /// A `MongoListDSL` is nothing more than a type that supports an /// ``init(with:)`` builder API. @@ -9,7 +8,7 @@ import BSONEncoding public protocol MongoListDSL:BSONRepresentable, BSONDecodable, BSONEncodable { - associatedtype Encoder:BSONEncoder + associatedtype Encoder:BSON.Encoder } extension MongoListDSL { diff --git a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateDocument.swift b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateDocument.swift index a1438c5f..116a1062 100644 --- a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateDocument.swift +++ b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateList.swift b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateList.swift index 272d08ee..13b1ca42 100644 --- a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateList.swift +++ b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateList.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateListEncoder.swift b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateListEncoder.swift index 2fbb7130..9c07beb2 100644 --- a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateListEncoder.swift +++ b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateListEncoder.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { @@ -15,7 +15,7 @@ extension Mongo } } } -extension Mongo.PredicateListEncoder:BSONEncoder +extension Mongo.PredicateListEncoder:BSON.Encoder { @inlinable public init(_ output:consuming BSON.Output<[UInt8]>) @@ -27,7 +27,7 @@ extension Mongo.PredicateListEncoder:BSONEncoder func move() -> BSON.Output<[UInt8]> { self.list.move() } @inlinable public static - var type:BSON { .list } + var type:BSON.AnyType { .list } } extension Mongo.PredicateListEncoder { diff --git a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateOperator.swift b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateOperator.swift index 701d72a3..5125b622 100644 --- a/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateOperator.swift +++ b/Sources/MongoBuiltins/Documents/Predicate/Mongo.PredicateOperator.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { @@ -30,7 +30,7 @@ extension Mongo.PredicateOperator } } @inlinable public - subscript(key:Metatype) -> BSON? + subscript(key:Metatype) -> BSON.AnyType? { get { @@ -42,7 +42,7 @@ extension Mongo.PredicateOperator } } @inlinable public - subscript(key:Metatype) -> [BSON]? + subscript(key:Metatype) -> [BSON.AnyType]? { get { diff --git a/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionDocument.swift b/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionDocument.swift index f7d73a3e..3276f4b5 100644 --- a/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionDocument.swift +++ b/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.Metadata.swift b/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.Metadata.swift index 42dc58df..551a14e9 100644 --- a/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.Metadata.swift +++ b/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.Metadata.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.ProjectionOperator { diff --git a/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.swift b/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.swift index f80fcee6..a8b64c3f 100644 --- a/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.swift +++ b/Sources/MongoBuiltins/Documents/Projection/Mongo.ProjectionOperator.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Reduce/Mongo.ReduceDocument.swift b/Sources/MongoBuiltins/Documents/Reduce/Mongo.ReduceDocument.swift index 99eb3a09..e414bc34 100644 --- a/Sources/MongoBuiltins/Documents/Reduce/Mongo.ReduceDocument.swift +++ b/Sources/MongoBuiltins/Documents/Reduce/Mongo.ReduceDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Sample/Mongo.SampleDocument.swift b/Sources/MongoBuiltins/Documents/Sample/Mongo.SampleDocument.swift index fadbb0a6..1f5c8110 100644 --- a/Sources/MongoBuiltins/Documents/Sample/Mongo.SampleDocument.swift +++ b/Sources/MongoBuiltins/Documents/Sample/Mongo.SampleDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Set/Mongo.SetDocument.swift b/Sources/MongoBuiltins/Documents/Set/Mongo.SetDocument.swift index 0119ac1f..f66b2510 100644 --- a/Sources/MongoBuiltins/Documents/Set/Mongo.SetDocument.swift +++ b/Sources/MongoBuiltins/Documents/Set/Mongo.SetDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Sort/Mongo.SortDocument.swift b/Sources/MongoBuiltins/Documents/Sort/Mongo.SortDocument.swift index 67c456fa..02966c83 100644 --- a/Sources/MongoBuiltins/Documents/Sort/Mongo.SortDocument.swift +++ b/Sources/MongoBuiltins/Documents/Sort/Mongo.SortDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.Metadata.swift b/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.Metadata.swift index 85fa93c4..d61f0dc2 100644 --- a/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.Metadata.swift +++ b/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.Metadata.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.SortOperator { diff --git a/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.swift b/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.swift index 28576d82..16051b08 100644 --- a/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.swift +++ b/Sources/MongoBuiltins/Documents/Sort/Mongo.SortOperator.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/SortArray/Mongo.SortArrayDocument.swift b/Sources/MongoBuiltins/Documents/SortArray/Mongo.SortArrayDocument.swift index 094cc045..345d62d9 100644 --- a/Sources/MongoBuiltins/Documents/SortArray/Mongo.SortArrayDocument.swift +++ b/Sources/MongoBuiltins/Documents/SortArray/Mongo.SortArrayDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranch.swift b/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranch.swift index a54ae279..a10a4a2f 100644 --- a/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranch.swift +++ b/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranch.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranches.Encoder.swift b/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranches.Encoder.swift index e61565e7..70e8509f 100644 --- a/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranches.Encoder.swift +++ b/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchBranches.Encoder.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.SwitchBranches { @@ -15,7 +15,7 @@ extension Mongo.SwitchBranches } } } -extension Mongo.SwitchBranches.Encoder:BSONEncoder +extension Mongo.SwitchBranches.Encoder:BSON.Encoder { @inlinable public init(_ output:consuming BSON.Output<[UInt8]>) @@ -27,7 +27,7 @@ extension Mongo.SwitchBranches.Encoder:BSONEncoder func move() -> BSON.Output<[UInt8]> { self.list.move() } @inlinable public static - var type:BSON { .list } + var type:BSON.AnyType { .list } } extension Mongo.SwitchBranches.Encoder { diff --git a/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchDocument.swift b/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchDocument.swift index beadd7db..4faa12ac 100644 --- a/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchDocument.swift +++ b/Sources/MongoBuiltins/Documents/Switch/Mongo.SwitchDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/UnionWith/Mongo.UnionWithDocument.swift b/Sources/MongoBuiltins/Documents/UnionWith/Mongo.UnionWithDocument.swift index 66b8e775..5359e7f8 100644 --- a/Sources/MongoBuiltins/Documents/UnionWith/Mongo.UnionWithDocument.swift +++ b/Sources/MongoBuiltins/Documents/UnionWith/Mongo.UnionWithDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Unwind/Mongo.UnwindDocument.swift b/Sources/MongoBuiltins/Documents/Unwind/Mongo.UnwindDocument.swift index 5d9eafc8..8dd03489 100644 --- a/Sources/MongoBuiltins/Documents/Unwind/Mongo.UnwindDocument.swift +++ b/Sources/MongoBuiltins/Documents/Unwind/Mongo.UnwindDocument.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateBitwiseOperator.swift b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateBitwiseOperator.swift index d0ab6c04..1927fd46 100644 --- a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateBitwiseOperator.swift +++ b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateBitwiseOperator.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.Unset.swift b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.Unset.swift index 140a5d89..053c204c 100644 --- a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.Unset.swift +++ b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.Unset.swift @@ -1,7 +1,7 @@ extension Mongo.UpdateDocument { /// Takes a document and removes the specified fields. - /// Not to be confused with the ``Mongo.PipelineState.Unset unset`` + /// Not to be confused with the ``Mongo.PipelineState.Unset/unset`` /// aggregation pipeline stage, which can take a field path directly. @frozen public enum Unset:String, Hashable, Sendable diff --git a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.swift b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.swift index f1f0e36b..ca7d1ce6 100644 --- a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.swift +++ b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateFields.swift b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateFields.swift index 09626da1..ad7c1c7d 100644 --- a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateFields.swift +++ b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdateFields.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdatePosition.swift b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdatePosition.swift index 43a55f03..e2910a7e 100644 --- a/Sources/MongoBuiltins/Documents/Update/Mongo.UpdatePosition.swift +++ b/Sources/MongoBuiltins/Documents/Update/Mongo.UpdatePosition.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoBuiltins/Documents/Zip/Mongo.ZipDocument.swift b/Sources/MongoBuiltins/Documents/Zip/Mongo.ZipDocument.swift index 709d7b82..a780f3f6 100644 --- a/Sources/MongoBuiltins/Documents/Zip/Mongo.ZipDocument.swift +++ b/Sources/MongoBuiltins/Documents/Zip/Mongo.ZipDocument.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { @@ -92,7 +92,7 @@ extension Mongo.ZipDocument } /// This subscript automatically sets `useLongestLength` if set to a - /// non-[`nil`]() value. + /// non-nil value. @inlinable public subscript(key:Defaults) -> Encodable? where Encodable:BSONEncodable diff --git a/Sources/MongoBuiltins/Documents/_MongoExpressionRestrictedEncodable.swift b/Sources/MongoBuiltins/Documents/_MongoExpressionRestrictedEncodable.swift index 66f7689f..a16010bf 100644 --- a/Sources/MongoBuiltins/Documents/_MongoExpressionRestrictedEncodable.swift +++ b/Sources/MongoBuiltins/Documents/_MongoExpressionRestrictedEncodable.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON /// This protocol provides diagnostics to help you avoid common mistakes, such as /// using keypaths or expression variables at the top-level of a ``Mongo.PredicateDocument``. diff --git a/Sources/MongoBuiltins/Expressions/BSON.ListEncoder (ext).swift b/Sources/MongoBuiltins/Expressions/BSON.ListEncoder (ext).swift index 9950d85f..c408634c 100644 --- a/Sources/MongoBuiltins/Expressions/BSON.ListEncoder (ext).swift +++ b/Sources/MongoBuiltins/Expressions/BSON.ListEncoder (ext).swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension BSON.ListEncoder { diff --git a/Sources/MongoBuiltins/Expressions/Mongo.Expression.swift b/Sources/MongoBuiltins/Expressions/Mongo.Expression.swift index f03de21d..a99ade85 100644 --- a/Sources/MongoBuiltins/Expressions/Mongo.Expression.swift +++ b/Sources/MongoBuiltins/Expressions/Mongo.Expression.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON @available(*, deprecated, renamed: "Mongo.Expression") public diff --git a/Sources/MongoBuiltins/Expressions/Optional (ext).swift b/Sources/MongoBuiltins/Expressions/Optional (ext).swift index f4f7e3d8..3e4c2ec0 100644 --- a/Sources/MongoBuiltins/Expressions/Optional (ext).swift +++ b/Sources/MongoBuiltins/Expressions/Optional (ext).swift @@ -5,6 +5,6 @@ extension Mongo.Expression? @inlinable public static func expr(with populate:(inout Mongo.Expression) throws -> ()) rethrows -> Self { - return .some(try .expr(with: populate)) + .some(try .expr(with: populate)) } } diff --git a/Sources/MongoBuiltins/Mongo.List.swift b/Sources/MongoBuiltins/Mongo.List.swift index 6dc84c10..c4adab2a 100644 --- a/Sources/MongoBuiltins/Mongo.List.swift +++ b/Sources/MongoBuiltins/Mongo.List.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoBuiltins/Mongo.Namespaced (ext).swift b/Sources/MongoBuiltins/Mongo.Namespaced (ext).swift index e3c39037..c9f533a9 100644 --- a/Sources/MongoBuiltins/Mongo.Namespaced (ext).swift +++ b/Sources/MongoBuiltins/Mongo.Namespaced (ext).swift @@ -1,5 +1,5 @@ import BSON -import MongoSchema +import MongoABI extension Mongo.Namespaced { diff --git a/Sources/MongoBuiltins/Pipelines/Mongo.Pipeline.swift b/Sources/MongoBuiltins/Pipelines/Mongo.Pipeline.swift index cccead13..979331a4 100644 --- a/Sources/MongoBuiltins/Pipelines/Mongo.Pipeline.swift +++ b/Sources/MongoBuiltins/Pipelines/Mongo.Pipeline.swift @@ -1,7 +1,5 @@ -import BSONDecoding -import BSONEncoding import BSON -import MongoSchema +import MongoABI extension Mongo { @@ -38,7 +36,7 @@ extension Mongo.Pipeline:BSONDecodable extension Mongo.Pipeline:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.bson.encode(to: &field) } diff --git a/Sources/MongoBuiltins/Pipelines/Mongo.PipelineEncoder.swift b/Sources/MongoBuiltins/Pipelines/Mongo.PipelineEncoder.swift index 0257abb9..391e592c 100644 --- a/Sources/MongoBuiltins/Pipelines/Mongo.PipelineEncoder.swift +++ b/Sources/MongoBuiltins/Pipelines/Mongo.PipelineEncoder.swift @@ -1,7 +1,5 @@ -import BSONDecoding -import BSONEncoding import BSON -import MongoSchema +import MongoABI extension Mongo { @@ -18,7 +16,7 @@ extension Mongo } } } -extension Mongo.PipelineEncoder:BSONEncoder +extension Mongo.PipelineEncoder:BSON.Encoder { @inlinable public init(_ output:consuming BSON.Output<[UInt8]>) @@ -30,7 +28,7 @@ extension Mongo.PipelineEncoder:BSONEncoder func move() -> BSON.Output<[UInt8]> { self.list.move() } @inlinable public static - var type:BSON { .list } + var type:BSON.AnyType { .list } } extension Mongo.PipelineEncoder { diff --git a/Sources/MongoClusters/ServerMetadata/Mongo.Replica.SecondaryBaseline.swift b/Sources/MongoClusters/ServerMetadata/Mongo.Replica.SecondaryBaseline.swift index 61a8a46c..7d8702ac 100644 --- a/Sources/MongoClusters/ServerMetadata/Mongo.Replica.SecondaryBaseline.swift +++ b/Sources/MongoClusters/ServerMetadata/Mongo.Replica.SecondaryBaseline.swift @@ -40,6 +40,6 @@ extension Mongo.Replica.SecondaryBaseline // we get rid of the “update” terms. // // (0 - candidate.write) - (0 - self.write) - return .init(rawValue: self.write.value - candidate.write.value) + .init(rawValue: self.write.value - candidate.write.value) } } diff --git a/Sources/MongoClusters/ServerMetadata/Mongo.Replica.Timings.swift b/Sources/MongoClusters/ServerMetadata/Mongo.Replica.Timings.swift index bbf58d47..0521f821 100644 --- a/Sources/MongoClusters/ServerMetadata/Mongo.Replica.Timings.swift +++ b/Sources/MongoClusters/ServerMetadata/Mongo.Replica.Timings.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON extension Mongo.Replica { diff --git a/Sources/MongoClusters/ServerMetadata/Mongo.ServerDescription.swift b/Sources/MongoClusters/ServerMetadata/Mongo.ServerDescription.swift index 51e8d183..ee416e9b 100644 --- a/Sources/MongoClusters/ServerMetadata/Mongo.ServerDescription.swift +++ b/Sources/MongoClusters/ServerMetadata/Mongo.ServerDescription.swift @@ -18,11 +18,11 @@ extension Mongo.ServerDescription { if case .connected(let metadata, _) = self { - return metadata + metadata } else { - return nil + nil } } @inlinable public @@ -30,11 +30,11 @@ extension Mongo.ServerDescription { if case .connected(_, let owner) = self { - return owner + owner } else { - return nil + nil } } /// Returns the stored error, if this descriptor currently has one. @@ -43,11 +43,11 @@ extension Mongo.ServerDescription { if case .errored(let error) = self { - return error + error } else { - return nil + nil } } } @@ -66,14 +66,14 @@ extension Mongo.ServerDescription case .connected(_, let owner): self = .connected(metadata, owner) return .accepted - + case .errored, .queued: return .dropped } } /// Places this descriptor in an ``case errored(_:)`` or ``case queued`` - /// state. If `status` is [`nil`]() and the descriptor is already in + /// state. If `status` is nil and the descriptor is already in /// an errored state, the descriptor will remain in that state, and the /// stored error will not be overwritten. mutating diff --git a/Sources/MongoClusters/ServerSelection/Mongo.Unreachable.swift b/Sources/MongoClusters/ServerSelection/Mongo.Unreachable.swift index 4b0af72f..5626b809 100644 --- a/Sources/MongoClusters/ServerSelection/Mongo.Unreachable.swift +++ b/Sources/MongoClusters/ServerSelection/Mongo.Unreachable.swift @@ -18,18 +18,18 @@ extension Mongo.Unreachable:Equatable switch (lhs, rhs) { case (.queued, .queued): - return true + true case (.errored(let lhs), .errored(let rhs)): - return lhs == rhs + lhs == rhs case (_, _): - return false + false } } } extension Mongo.Unreachable { - /// Updates the stored error with the given error, if non-[`nil`](). - /// If `status` is [`nil`]() and the descriptor is already in an + /// Updates the stored error with the given error, if non-nil. + /// If `status` is nil and the descriptor is already in an /// errored state, the descriptor will remain in that state, and the /// stored error will not be overwritten. @inlinable public mutating diff --git a/Sources/MongoClusters/Topologies/Mongo.Topology.Unknown.swift b/Sources/MongoClusters/Topologies/Mongo.Topology.Unknown.swift index cde4fc87..f50bb744 100644 --- a/Sources/MongoClusters/Topologies/Mongo.Topology.Unknown.swift +++ b/Sources/MongoClusters/Topologies/Mongo.Topology.Unknown.swift @@ -27,11 +27,11 @@ extension Mongo.Topology.Unknown { if case ()? = self.ghosts[host]?.clear(status: status) { - return .accepted + .accepted } else { - return .rejected + .rejected } } func topology( @@ -42,25 +42,25 @@ extension Mongo.Topology.Unknown switch $0 { case .errored(let error): - return .errored(error) + .errored(error) case .queued: - return .queued + .queued } } } - /// Removes the given host from the topology if present, returning [`true`]() - /// if it was the only ghost in the topology. Returns [`false`]() otherwise. + /// Removes the given host from the topology if present, returning `true` + /// if it was the only ghost in the topology. Returns `false` otherwise. @discardableResult mutating func pick(host:Mongo.Host) -> Bool { if case _? = self.ghosts.removeValue(forKey: host), self.ghosts.isEmpty { - return true + true } else { - return false + false } } } diff --git a/Sources/MongoClusters/TopologyUpdates/Mongo.Host.swift b/Sources/MongoClusters/TopologyUpdates/Mongo.Host.swift index 54a2cdbb..d12d2f6c 100644 --- a/Sources/MongoClusters/TopologyUpdates/Mongo.Host.swift +++ b/Sources/MongoClusters/TopologyUpdates/Mongo.Host.swift @@ -1,22 +1,21 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { - @frozen public + @frozen public struct Host:Hashable, Sendable { - /// The hostname, such as [`"localhost"`](), [`"example.com"`](), + /// The hostname, such as [`"localhost"`](), [`"example.com"`](), /// or [`"127.0.0.1"`](). - public + public var name:String /// The port. The default MongoDB port is 27017. - public + public var port:Int - @inlinable public - init(name:String, port:Int? = nil) + @inlinable public + init(name:String, port:Int? = nil) { self.name = name self.port = port ?? 27017 @@ -32,7 +31,7 @@ extension Mongo.Host:Comparable } } extension Mongo.Host -{ +{ // @inlinable public static // func srv(_ name:String) -> Self // { diff --git a/Sources/MongoClusters/TopologyUpdates/Mongo.TopologyUpdate.Slave.swift b/Sources/MongoClusters/TopologyUpdates/Mongo.TopologyUpdate.Slave.swift index 8cc344c2..c943aa58 100644 --- a/Sources/MongoClusters/TopologyUpdates/Mongo.TopologyUpdate.Slave.swift +++ b/Sources/MongoClusters/TopologyUpdates/Mongo.TopologyUpdate.Slave.swift @@ -16,9 +16,9 @@ extension Mongo.TopologyUpdate.Slave { switch self { - case .secondary(let replica): return .secondary(replica) - case .arbiter: return .arbiter - case .other: return .other + case .secondary(let replica): .secondary(replica) + case .arbiter: .arbiter + case .other: .other } } } diff --git a/Sources/MongoConfiguration/Authentication/Mongo.Authentication.SASL.swift b/Sources/MongoConfiguration/Authentication/Mongo.Authentication.SASL.swift index 3b95cb3f..95969ef8 100644 --- a/Sources/MongoConfiguration/Authentication/Mongo.Authentication.SASL.swift +++ b/Sources/MongoConfiguration/Authentication/Mongo.Authentication.SASL.swift @@ -1,12 +1,11 @@ -import BSONDecoding -import BSONEncoding +import BSON import Mongo extension Mongo.Authentication { /// A namespace for SASL (Simple Authentication and Security Layer) types. @frozen public - enum SASL:String, Hashable, Sendable + enum SASL:String, Hashable, Sendable { case aws = "MONGODB-AWS" case gssapi = "GSSAPI" diff --git a/Sources/MongoConfiguration/Authentication/Mongo.Authentication.swift b/Sources/MongoConfiguration/Authentication/Mongo.Authentication.swift index e4860ffb..6b117b6f 100644 --- a/Sources/MongoConfiguration/Authentication/Mongo.Authentication.swift +++ b/Sources/MongoConfiguration/Authentication/Mongo.Authentication.swift @@ -14,8 +14,8 @@ extension Mongo.Authentication:RawRepresentable { switch self { - case .sasl(let sasl): return sasl.rawValue - case .x509: return "MONGODB-X509" + case .sasl(let sasl): sasl.rawValue + case .x509: "MONGODB-X509" } } @inlinable public diff --git a/Sources/MongoConfiguration/Authentication/Mongo.Credentials.swift b/Sources/MongoConfiguration/Authentication/Mongo.Credentials.swift index 159e47b8..2afaede6 100644 --- a/Sources/MongoConfiguration/Authentication/Mongo.Credentials.swift +++ b/Sources/MongoConfiguration/Authentication/Mongo.Credentials.swift @@ -1,4 +1,4 @@ -import MongoSchema +import MongoABI extension Mongo { diff --git a/Sources/MongoConfiguration/Authentication/Mongo.User.swift b/Sources/MongoConfiguration/Authentication/Mongo.User.swift index 4035d516..c1ee9e90 100644 --- a/Sources/MongoConfiguration/Authentication/Mongo.User.swift +++ b/Sources/MongoConfiguration/Authentication/Mongo.User.swift @@ -1,4 +1,4 @@ -import MongoSchema +import MongoABI extension Mongo { diff --git a/Sources/MongoConfiguration/URI/Mongo.URI.Authority.swift b/Sources/MongoConfiguration/URI/Mongo.URI.Authority.swift index 9a8941e5..1a2c19fa 100644 --- a/Sources/MongoConfiguration/URI/Mongo.URI.Authority.swift +++ b/Sources/MongoConfiguration/URI/Mongo.URI.Authority.swift @@ -1,10 +1,10 @@ -import MongoSchema +import MongoABI extension Mongo.URI { /// The first two significant components of a connection string URI. /// It contains login credentials (which may be empty), and a list of - /// domains. To append a path component to it, use the ``/(self:path:)`` + /// domains. To append a path component to it, use the ``Authority//(_:_:) [1DYBW]`` /// operator, which returns a ``Location``. @frozen public struct Authority where LoginMode:MongoLoginMode diff --git a/Sources/MongoConfiguration/URI/Mongo.URI.Location.swift b/Sources/MongoConfiguration/URI/Mongo.URI.Location.swift index f6e8e188..0b9795c8 100644 --- a/Sources/MongoConfiguration/URI/Mongo.URI.Location.swift +++ b/Sources/MongoConfiguration/URI/Mongo.URI.Location.swift @@ -1,4 +1,4 @@ -import MongoSchema +import MongoABI extension Mongo.URI { diff --git a/Sources/MongoDB/exports.swift b/Sources/MongoDB/exports.swift index a8c733ba..e9e65fb1 100644 --- a/Sources/MongoDB/exports.swift +++ b/Sources/MongoDB/exports.swift @@ -1,5 +1,5 @@ -@_exported import MongoConfiguration @_exported import MongoClusters +@_exported import MongoConfiguration @_exported import MongoDriver @_exported import MongoLibrary @_exported import MongoQL diff --git a/Sources/MongoDBTests/Aggregate/Aggregate.Article.swift b/Sources/MongoDBTests/Aggregate/Aggregate.Article.swift index e8cbff6a..72ef26d5 100644 --- a/Sources/MongoDBTests/Aggregate/Aggregate.Article.swift +++ b/Sources/MongoDBTests/Aggregate/Aggregate.Article.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoQL extension Aggregate diff --git a/Sources/MongoDBTests/Aggregate/Aggregate.AuthorStats.swift b/Sources/MongoDBTests/Aggregate/Aggregate.AuthorStats.swift index fc50d697..f7286113 100644 --- a/Sources/MongoDBTests/Aggregate/Aggregate.AuthorStats.swift +++ b/Sources/MongoDBTests/Aggregate/Aggregate.AuthorStats.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Aggregate { diff --git a/Sources/MongoDBTests/Aggregate/Aggregate.TagStats.swift b/Sources/MongoDBTests/Aggregate/Aggregate.TagStats.swift index 00169514..e9164aeb 100644 --- a/Sources/MongoDBTests/Aggregate/Aggregate.TagStats.swift +++ b/Sources/MongoDBTests/Aggregate/Aggregate.TagStats.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoQL extension Aggregate diff --git a/Sources/MongoDBTests/CausalConsistency/CausalConsistency.AnyTimeoutError.swift b/Sources/MongoDBTests/CausalConsistency/CausalConsistency.AnyTimeoutError.swift new file mode 100644 index 00000000..38be7dba --- /dev/null +++ b/Sources/MongoDBTests/CausalConsistency/CausalConsistency.AnyTimeoutError.swift @@ -0,0 +1,36 @@ +import MongoDB + +extension CausalConsistency +{ + struct AnyTimeoutError:Error + { + private + init() + { + } + } +} +extension CausalConsistency.AnyTimeoutError +{ + init?(_ error:any Error) + { + switch error + { + case is Mongo.DriverTimeoutError, + is Mongo.WireTimeoutError: + self.init() + + case let error as Mongo.ServerError: + guard error.code.indicatesTimeLimitExceeded + else + { + return nil + } + + self.init() + + case _: + return nil + } + } +} diff --git a/Sources/MongoDBTests/CausalConsistency/CausalConsistency.swift b/Sources/MongoDBTests/CausalConsistency/CausalConsistency.swift index dd788613..fe4c785e 100644 --- a/Sources/MongoDBTests/CausalConsistency/CausalConsistency.swift +++ b/Sources/MongoDBTests/CausalConsistency/CausalConsistency.swift @@ -190,9 +190,16 @@ struct CausalConsistency:MongoTestBattery // secondary, we should be asking it for data from a time in its future // that it doesn’t have. We should have prevented it from getting the // new data by locking it earlier. - await (tests ! "timeout").do(catching: Mongo.TimeoutError.self) + await (tests ! "timeout").do(catching: AnyTimeoutError.self) { - let _:[Letter] = try await ReadLetters() + do + { + let _:[Letter] = try await ReadLetters() + } + catch let error + { + throw AnyTimeoutError.init(error) ?? error + } } // We should be able to read all four writes from a different, @@ -217,9 +224,16 @@ struct CausalConsistency:MongoTestBattery // We should still receive a timeout error if we try to read from the // locked secondary from the current session, because running the last // command didn’t lower the precondition time. - await (tests ! "timeout-again").do(catching: Mongo.TimeoutError.self) + await (tests ! "timeout-again").do(catching: AnyTimeoutError.self) { - let _:[Letter] = try await ReadLetters() + do + { + let _:[Letter] = try await ReadLetters() + } + catch let error + { + throw AnyTimeoutError.init(error) ?? error + } } // We should still be able to read from the locked secondary/slave from // a different session. Because that session’s timeline should have no @@ -247,7 +261,7 @@ struct CausalConsistency:MongoTestBattery // We should still receive a timeout error if we try to read from the // locked secondary/slave using a session that was forked from the // current session, however. - await (tests ! "timeout-forked").do(catching: Mongo.TimeoutError.self) + await (tests ! "timeout-forked").do(catching: AnyTimeoutError.self) { let forked:Mongo.Session = try await .init(from: pool, forking: session) @@ -255,11 +269,18 @@ struct CausalConsistency:MongoTestBattery // time as the session it was forked from. tests.expect(forked.preconditionTime ==? head) - let _:[Letter] = try await forked.run( - command: Mongo.Find>.init(collection, - limit: 10), - against: database, - on: secondary) + do + { + let _:[Letter] = try await forked.run( + command: Mongo.Find>.init(collection, + limit: 10), + against: database, + on: secondary) + } + catch let error + { + throw AnyTimeoutError.init(error) ?? error + } } // We should be able to unlock the secondary/slave. diff --git a/Sources/MongoDBTests/Cursors/Cursors.swift b/Sources/MongoDBTests/Cursors/Cursors.swift index 484ff61f..4659e5a3 100644 --- a/Sources/MongoDBTests/Cursors/Cursors.swift +++ b/Sources/MongoDBTests/Cursors/Cursors.swift @@ -102,9 +102,9 @@ struct Cursors:MongoTestBattery // We should have encountered ten batches in total. tests.expect(batch ==? 10) - // The iterator should be [`nil`]() after the last batch is queried, + // The iterator should be nil after the last batch is queried, // and we have exited the loop. The iterator is not guaranteed to be - // [`nil`]() in the last loop iteration, because empty trailing batches + // nil in the last loop iteration, because empty trailing batches // are not provided to the caller. tests.expect(nil: $0.cursor) diff --git a/Sources/MongoDBTests/Delete/Delete.Cake.swift b/Sources/MongoDBTests/Delete/Delete.Cake.swift index 3851cf81..ea4d0888 100644 --- a/Sources/MongoDBTests/Delete/Delete.Cake.swift +++ b/Sources/MongoDBTests/Delete/Delete.Cake.swift @@ -1,5 +1,5 @@ -import BSONDecoding -import BSONEncoding +import BSON + extension Delete { struct Cake:Equatable, Hashable, BSONDocumentDecodable, BSONDocumentEncodable diff --git a/Sources/MongoDBTests/Delete/Delete.swift b/Sources/MongoDBTests/Delete/Delete.swift index 848d0961..33026432 100644 --- a/Sources/MongoDBTests/Delete/Delete.swift +++ b/Sources/MongoDBTests/Delete/Delete.swift @@ -152,22 +152,22 @@ struct Delete:MongoTestBattery let expected:Mongo.DeleteResponse = .init(deleted: 1) let response:Mongo.DeleteResponse = try await session.run( command: Mongo.Delete.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } deletes: - [ - .init - { - $0[.limit] = .one - $0[.q] = .init - { - $0["flavor"] = "styrene" - $0["status"] = "B" - } - }, - ]) + { + $0 { - $0[.ordered] = false - }, + $0[.limit] = .one + $0[.q] = .init + { + $0["flavor"] = "styrene" + $0["status"] = "B" + } + } + }, against: database) tests.expect(response ==? expected) @@ -190,22 +190,22 @@ struct Delete:MongoTestBattery let expected:Mongo.DeleteResponse = .init(deleted: 2) let response:Mongo.DeleteResponse = try await session.run( command: Mongo.Delete.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } deletes: - [ - .init - { - $0[.limit] = .unlimited - $0[.collation] = .init(locale: "fr", strength: .primary) - $0[.q] = .init - { - $0["flavor"] = "acrylic" - } - }, - ]) + { + $0 { - $0[.ordered] = false - }, + $0[.limit] = .unlimited + $0[.collation] = .init(locale: "fr", strength: .primary) + $0[.q] = .init + { + $0["flavor"] = "acrylic" + } + } + }, against: database) tests.expect(response ==? expected) @@ -228,25 +228,25 @@ struct Delete:MongoTestBattery let expected:Mongo.DeleteResponse = .init(deleted: 3) let response:Mongo.DeleteResponse = try await session.run( command: Mongo.Delete.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } deletes: - [ - .init + { + $0 + { + $0[.limit] = .unlimited + $0[.hint] = "points_index" + $0[.q] = .init { - $0[.limit] = .unlimited - $0[.hint] = "points_index" - $0[.q] = .init + $0["points"] = .init { - $0["points"] = .init - { - $0[.lt] = 70 - } + $0[.lt] = 70 } - }, - ]) - { - $0[.ordered] = false - }, + } + } + }, against: database) tests.expect(response ==? expected) diff --git a/Sources/MongoDBTests/FindAndModify/FindAndModify.Ruler.swift b/Sources/MongoDBTests/FindAndModify/FindAndModify.Ruler.swift index 0a08f1d9..5fe39826 100644 --- a/Sources/MongoDBTests/FindAndModify/FindAndModify.Ruler.swift +++ b/Sources/MongoDBTests/FindAndModify/FindAndModify.Ruler.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension FindAndModify { diff --git a/Sources/MongoDBTests/Indexes/Indexes.Product.swift b/Sources/MongoDBTests/Indexes/Indexes.Product.swift index 6b293904..17b0835e 100644 --- a/Sources/MongoDBTests/Indexes/Indexes.Product.swift +++ b/Sources/MongoDBTests/Indexes/Indexes.Product.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Indexes { diff --git a/Sources/MongoDBTests/Letter.swift b/Sources/MongoDBTests/Letter.swift index b1be3e03..8e46a279 100644 --- a/Sources/MongoDBTests/Letter.swift +++ b/Sources/MongoDBTests/Letter.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON struct Letter:Hashable, Sendable { diff --git a/Sources/MongoDBTests/Main.swift b/Sources/MongoDBTests/Main.swift index bb0e25f3..6b5d7e5e 100644 --- a/Sources/MongoDBTests/Main.swift +++ b/Sources/MongoDBTests/Main.swift @@ -1,12 +1,12 @@ -import NIOPosix import MongoDB +import NIOPosix import Testing @main -enum Main:AsyncTests +enum Main:TestMain, TestBattery { static - func run(tests:Tests) async + func run(tests:TestGroup) async { let executors:MultiThreadedEventLoopGroup = .init(numberOfThreads: 2) diff --git a/Sources/MongoDBTests/Record.swift b/Sources/MongoDBTests/Record.swift index 2cb58a2a..1fd7128e 100644 --- a/Sources/MongoDBTests/Record.swift +++ b/Sources/MongoDBTests/Record.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON struct Record:Hashable, Sendable where Value:Hashable & Sendable & BSONDecodable & BSONEncodable diff --git a/Sources/MongoDBTests/Transactions/Transactions.swift b/Sources/MongoDBTests/Transactions/Transactions.swift index 0afdcf1e..a4b31ebb 100644 --- a/Sources/MongoDBTests/Transactions/Transactions.swift +++ b/Sources/MongoDBTests/Transactions/Transactions.swift @@ -69,7 +69,7 @@ struct Transactions:MongoTestBattery // before. let _:Mongo.Timestamp? = tests.expect(value: transaction.preconditionTime) // We should be able to start a transaction with a write command, - // even though it also has a non-[`nil`]() precondition time. + // even though it also has a non-nil precondition time. await (tests ! "insert").do { let response:Mongo.InsertResponse = try await transaction.run( diff --git a/Sources/MongoDBTests/Update/Update.Member.swift b/Sources/MongoDBTests/Update/Update.Member.swift index 74f69efc..ead51089 100644 --- a/Sources/MongoDBTests/Update/Update.Member.swift +++ b/Sources/MongoDBTests/Update/Update.Member.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Update { diff --git a/Sources/MongoDBTests/Update/Update.swift b/Sources/MongoDBTests/Update/Update.swift index 3283d71c..5f301ec4 100644 --- a/Sources/MongoDBTests/Update/Update.swift +++ b/Sources/MongoDBTests/Update/Update.swift @@ -113,31 +113,31 @@ struct Update:MongoTestBattery let expected:Mongo.UpdateResponse = .init(selected: 1, modified: 1) let response:Mongo.UpdateResponse = try await session.run( command: Mongo.Update.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } updates: - [ - .init + { + $0 + { + $0[.q] = .init + { + $0["member"] = "abc123" + } + $0[.u] = .init { - $0[.q] = .init + $0[.set] = .init { - $0["member"] = "abc123" + $0["status"] = "A" } - $0[.u] = .init + $0[.inc] = .init { - $0[.set] = .init - { - $0["status"] = "A" - } - $0[.inc] = .init - { - $0["points"] = 1 - } + $0["points"] = 1 } - }, - ]) - { - $0[.ordered] = false - }, + } + } + }, against: database) tests.expect(response ==? expected) @@ -161,29 +161,29 @@ struct Update:MongoTestBattery let expected:Mongo.UpdateResponse = .init(selected: 2, modified: 2) let response:Mongo.UpdateResponse = try await session.run( command: Mongo.Update.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } updates: - [ - .init + { + $0 + { + $0[.q] = .init() + $0[.u] = .init { - $0[.q] = .init() - $0[.u] = .init + $0[.set] = .init { - $0[.set] = .init - { - $0["status"] = "A" - } - $0[.inc] = .init - { - $0["points"] = 1 - } + $0["status"] = "A" } - $0[.multi] = true - }, - ]) - { - $0[.ordered] = false - }, + $0[.inc] = .init + { + $0["points"] = 1 + } + } + $0[.multi] = true + } + }, against: database) tests.expect(response ==? expected) @@ -207,33 +207,33 @@ struct Update:MongoTestBattery let expected:Mongo.UpdateResponse = .init(selected: 2, modified: 2) let response:Mongo.UpdateResponse = try await session.run( command: Mongo.Update.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } updates: - [ - .init + { + $0 + { + $0[.q] = .init() + $0[.u] = .init { - $0[.q] = .init() - $0[.u] = .init + $0.stage { - $0.stage - { - $0[.set] = .init - { - $0["status"] = "Modified" - $0["comments"] = ["$misc1", "$misc2"] - } - } - $0.stage + $0[.set] = .init { - $0[.unset] = ["misc1", "misc2"] + $0["status"] = "Modified" + $0["comments"] = ["$misc1", "$misc2"] } } - $0[.multi] = true - }, - ]) - { - $0[.ordered] = false - }, + $0.stage + { + $0[.unset] = ["misc1", "misc2"] + } + } + $0[.multi] = true + } + }, against: database) tests.expect(response ==? expected) @@ -259,22 +259,22 @@ struct Update:MongoTestBattery upserted: [.init(index: 0, id: 3)]) let response:Mongo.UpdateResponse = try await session.run( command: Mongo.Update.init(collection, - writeConcern: .majority, + writeConcern: .majority) + { + $0[.ordered] = false + } updates: - [ - .init - { - $0[.q] = .init - { - $0["_id"] = 3 - } - $0[.u] = states.4.last - $0[.upsert] = true - }, - ]) + { + $0 { - $0[.ordered] = false - }, + $0[.q] = .init + { + $0["_id"] = 3 + } + $0[.u] = states.4.last + $0[.upsert] = true + } + }, against: database) tests.expect(response ==? expected) diff --git a/Sources/MongoDriver/Authenticator/Mongo.PolicyError.swift b/Sources/MongoDriver/Authenticator/Mongo.PolicyError.swift index 34d06088..4fa6cfea 100644 --- a/Sources/MongoDriver/Authenticator/Mongo.PolicyError.swift +++ b/Sources/MongoDriver/Authenticator/Mongo.PolicyError.swift @@ -15,14 +15,12 @@ extension Mongo.PolicyError:CustomStringConvertible switch self { case .sha256Iterations(let iterations): - return - """ + """ security policy prohibits connecting to server with SCRAM-SHA-256 \ iteration count set to '\(iterations)' """ case .serverSignature: - return - """ + """ security policy prohibits connecting to server that failed SCRAM \ authentication """ diff --git a/Sources/MongoDriver/Commands/AbortTransaction/Mongo.AbortTransaction.swift b/Sources/MongoDriver/Commands/AbortTransaction/Mongo.AbortTransaction.swift index 68098103..7e2c2f9a 100644 --- a/Sources/MongoDriver/Commands/AbortTransaction/Mongo.AbortTransaction.swift +++ b/Sources/MongoDriver/Commands/AbortTransaction/Mongo.AbortTransaction.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/CommitTransaction/Mongo.CommitTransaction.swift b/Sources/MongoDriver/Commands/CommitTransaction/Mongo.CommitTransaction.swift index 0a423b2f..19dbe8df 100644 --- a/Sources/MongoDriver/Commands/CommitTransaction/Mongo.CommitTransaction.swift +++ b/Sources/MongoDriver/Commands/CommitTransaction/Mongo.CommitTransaction.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/ConfigureFailPoint/Mongo.ConfigureFailpoint.swift b/Sources/MongoDriver/Commands/ConfigureFailPoint/Mongo.ConfigureFailpoint.swift index 000242f3..6a5188f9 100644 --- a/Sources/MongoDriver/Commands/ConfigureFailPoint/Mongo.ConfigureFailpoint.swift +++ b/Sources/MongoDriver/Commands/ConfigureFailPoint/Mongo.ConfigureFailpoint.swift @@ -1,6 +1,6 @@ -import BSONEncoding +import BSON import MongoClusters -import MongoSchema +import MongoABI extension Mongo { diff --git a/Sources/MongoDriver/Commands/EndSessions/Mongo.EndSessions.swift b/Sources/MongoDriver/Commands/EndSessions/Mongo.EndSessions.swift index 84c07956..eba063e9 100644 --- a/Sources/MongoDriver/Commands/EndSessions/Mongo.EndSessions.swift +++ b/Sources/MongoDriver/Commands/EndSessions/Mongo.EndSessions.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/GetMore/Mongo.GetMore.swift b/Sources/MongoDriver/Commands/GetMore/Mongo.GetMore.swift index ee185f37..61d29028 100644 --- a/Sources/MongoDriver/Commands/GetMore/Mongo.GetMore.swift +++ b/Sources/MongoDriver/Commands/GetMore/Mongo.GetMore.swift @@ -1,7 +1,6 @@ -import BSONDecoding -import BSONEncoding +import BSON import Durations -import MongoSchema +import MongoABI extension Mongo { diff --git a/Sources/MongoDriver/Commands/Hello/Mongo.AwaitableHello.swift b/Sources/MongoDriver/Commands/Hello/Mongo.AwaitableHello.swift index 1cc2e9d9..06cba757 100644 --- a/Sources/MongoDriver/Commands/Hello/Mongo.AwaitableHello.swift +++ b/Sources/MongoDriver/Commands/Hello/Mongo.AwaitableHello.swift @@ -1,5 +1,5 @@ +import BSON import Durations -import BSONEncoding extension Mongo { diff --git a/Sources/MongoDriver/Commands/Hello/Mongo.Hello.ClientMetadata.swift b/Sources/MongoDriver/Commands/Hello/Mongo.Hello.ClientMetadata.swift index c2e97c48..cd047dc9 100644 --- a/Sources/MongoDriver/Commands/Hello/Mongo.Hello.ClientMetadata.swift +++ b/Sources/MongoDriver/Commands/Hello/Mongo.Hello.ClientMetadata.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.Hello { diff --git a/Sources/MongoDriver/Commands/Hello/Mongo.Hello.swift b/Sources/MongoDriver/Commands/Hello/Mongo.Hello.swift index 83405782..3ca97ea0 100644 --- a/Sources/MongoDriver/Commands/Hello/Mongo.Hello.swift +++ b/Sources/MongoDriver/Commands/Hello/Mongo.Hello.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/Hello/Mongo.VersionRequirementError.swift b/Sources/MongoDriver/Commands/Hello/Mongo.VersionRequirementError.swift index 190836ab..70fca173 100644 --- a/Sources/MongoDriver/Commands/Hello/Mongo.VersionRequirementError.swift +++ b/Sources/MongoDriver/Commands/Hello/Mongo.VersionRequirementError.swift @@ -6,9 +6,9 @@ extension Mongo struct VersionRequirementError:Equatable, Error { public - let wireVersions:ClosedRange + let wireVersions:ClosedRange - init(invalid:ClosedRange) + init(invalid:ClosedRange) { self.wireVersions = invalid } diff --git a/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursors.swift b/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursors.swift index cc8ce2ee..f3f3fbaf 100644 --- a/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursors.swift +++ b/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursors.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursorsResponse.swift b/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursorsResponse.swift index 2c69ee1a..ef1bbb31 100644 --- a/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursorsResponse.swift +++ b/Sources/MongoDriver/Commands/KillCursors/Mongo.KillCursorsResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/Mongo.CommandType.swift b/Sources/MongoDriver/Commands/Mongo.CommandType.swift index cf225bdb..f60e5d13 100644 --- a/Sources/MongoDriver/Commands/Mongo.CommandType.swift +++ b/Sources/MongoDriver/Commands/Mongo.CommandType.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/Mongo.Database.Admin.swift b/Sources/MongoDriver/Commands/Mongo.Database.Admin.swift index 13604930..838ae5ff 100644 --- a/Sources/MongoDriver/Commands/Mongo.Database.Admin.swift +++ b/Sources/MongoDriver/Commands/Mongo.Database.Admin.swift @@ -1,4 +1,4 @@ -import MongoSchema +import MongoABI extension Mongo.Database { diff --git a/Sources/MongoDriver/Commands/Mongo.Reply.Status.swift b/Sources/MongoDriver/Commands/Mongo.Reply.Status.swift index 903e0cce..b32ee4c1 100644 --- a/Sources/MongoDriver/Commands/Mongo.Reply.Status.swift +++ b/Sources/MongoDriver/Commands/Mongo.Reply.Status.swift @@ -1,18 +1,17 @@ -import BSONDecoding import BSON extension Mongo.Reply { /// A type that can decode a MongoDB status indicator. /// - /// The following BSON values encode a “success” status (``ok`` is [`true`]()): + /// The following BSON values encode a “success” status (``ok`` is `true`): /// /// - [`.bool(true)`](), /// - [`.int32(1)`](), /// - [`.int64(1)`](), and /// - [`.double(1.0)`](). /// - /// The following BSON values encode a “failure” status (``ok`` is [`false`]()): + /// The following BSON values encode a “failure” status (``ok`` is `false`): /// /// - [`.bool(false)`](), /// - [`.int32(0)`](), @@ -20,13 +19,10 @@ extension Mongo.Reply /// - [`.double(0.0)`](). /// /// All other BSON values will produce a decoding error. - @frozen public struct Status:Equatable { - public let ok:Bool - @inlinable public init(ok:Bool) { self.ok = ok @@ -35,7 +31,6 @@ extension Mongo.Reply } extension Mongo.Reply.Status:BSONDecodable { - @inlinable public init(bson:BSON.AnyValue>) throws { self.init(ok: try bson.cast @@ -43,11 +38,11 @@ extension Mongo.Reply.Status:BSONDecodable switch $0 { case .bool(true), .int32(1), .int64(1), .double(1.0): - return true + true case .bool(false), .int32(0), .int64(0), .double(0.0): - return false + false default: - return nil + nil } }) } diff --git a/Sources/MongoDriver/Commands/Mongo.Reply.swift b/Sources/MongoDriver/Commands/Mongo.Reply.swift index 8f0b431e..711b38a9 100644 --- a/Sources/MongoDriver/Commands/Mongo.Reply.swift +++ b/Sources/MongoDriver/Commands/Mongo.Reply.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoWire import NIOCore @@ -7,17 +7,15 @@ extension Mongo @frozen public struct Reply { - private - let result:Result, - Mongo.ServerError> + @usableFromInline internal + let result:Result, any Error> - @usableFromInline + @usableFromInline internal let operationTime:Timestamp? - @usableFromInline + @usableFromInline internal let clusterTime:ClusterTime? - init(result:Result, - Mongo.ServerError>, + init(result:Result, any Error>, operationTime:Timestamp?, clusterTime:ClusterTime?) { @@ -33,46 +31,21 @@ extension Mongo.Reply { switch self.result { - case .success: return true - case .failure: return false + case .success: true + case .failure: false } } - @usableFromInline + @inlinable internal func callAsFunction() throws -> BSON.DocumentDecoder { - switch self.result - { - case .success(let bson): - return bson - - case .failure(let error): - guard - let code:Mongo.ServerError.Code = error.code - else - { - throw error - } - - if code == 26 - { - throw Mongo.NamespaceError.init() - } - else if code.indicatesTimeLimitExceeded - { - throw Mongo.TimeoutError.server(code: code) - } - else - { - throw error - } - } + try self.result.get() } } extension Mongo.Reply { public - init(message:MongoWire.Message) throws + init(message:Mongo.WireMessage) throws { let bson:BSON.DocumentDecoder = try .init( parsing: message.sections.body) @@ -89,14 +62,25 @@ extension Mongo.Reply self.init(result: .success(bson), operationTime: operationTime, clusterTime: clusterTime) + return + } + + let message:String = try bson["errmsg"]?.decode(to: String.self) ?? "" + + if let code:Int32 = try bson["code"]?.decode() + { + self.init(result: .failure(Mongo.ServerError.init( + Mongo.ServerError.Code.init(rawValue: code), + message: message)), + operationTime: operationTime, + clusterTime: clusterTime) } else { - self.init(result: .failure(.init(try bson["code"]?.decode( - to: Mongo.ServerError.Code.self), - message: try bson["errmsg"]?.decode(to: String.self) ?? "")), + self.init(result: .failure(Mongo.ReplyError.uncoded(message: message)), operationTime: operationTime, clusterTime: clusterTime) + return } } } diff --git a/Sources/MongoDriver/Commands/Mongo.ReplyError.swift b/Sources/MongoDriver/Commands/Mongo.ReplyError.swift new file mode 100644 index 00000000..a8bc52e9 --- /dev/null +++ b/Sources/MongoDriver/Commands/Mongo.ReplyError.swift @@ -0,0 +1,9 @@ +extension Mongo +{ + @frozen public + enum ReplyError:Equatable, Error + { + /// The server returned an error that lacks an error code. + case uncoded(message:String) + } +} diff --git a/Sources/MongoDriver/Commands/Mongo.Timestamp.swift b/Sources/MongoDriver/Commands/Mongo.Timestamp.swift index b1646f58..4765f31a 100644 --- a/Sources/MongoDriver/Commands/Mongo.Timestamp.swift +++ b/Sources/MongoDriver/Commands/Mongo.Timestamp.swift @@ -1,5 +1,3 @@ -import BSONDecoding -import BSONEncoding import BSON extension Mongo @@ -7,7 +5,7 @@ extension Mongo /// A typed wrapper around a BSON ``UInt64`` value. Unlike /// ``UInt64`` itself, this type’s ``BSONDecodable`` and /// ``BSONEncodable`` implementations only use the - /// ``BSON.uint64`` data type, and will fail on all other + /// ``BSON.AnyType/uint64`` data type, and will fail on all other /// BSON integer types. /// /// Despite its name, this is not a true ``InstantProtocol``, @@ -40,7 +38,7 @@ extension Mongo.Timestamp:BSONDecodable { /// Attempts to cast a BSON variant backed by some storage type to a /// MongoDB timestamp. The conversion is not a integer case, and will - /// succeed if and only if the variant has type ``BSON.uint64``. + /// succeed if and only if the variant has type ``BSON.AnyType/uint64``. @inlinable public init(bson:BSON.AnyValue>) throws { @@ -48,20 +46,20 @@ extension Mongo.Timestamp:BSONDecodable { if case .uint64(let value) = $0 { - return value + value } else { - return nil + nil } }) } } extension Mongo.Timestamp:BSONEncodable { - /// Encodes this timestamp as a ``BSON.uint64``. + /// Encodes this timestamp as a ``BSON.AnyValue/uint64(_:)``. @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { field.encode(uint64: self.value) } diff --git a/Sources/MongoDriver/Commands/MongoCommand.swift b/Sources/MongoDriver/Commands/MongoCommand.swift index 85d4286a..a88306a3 100644 --- a/Sources/MongoDriver/Commands/MongoCommand.swift +++ b/Sources/MongoDriver/Commands/MongoCommand.swift @@ -1,8 +1,7 @@ -import BSONDecoding -import BSONEncoding +import BSON import Durations +import MongoABI import MongoWire -import MongoSchema import NIOCore /// A type that represents a MongoDB command. All public command types @@ -61,7 +60,7 @@ protocol MongoCommand:Sendable } extension MongoCommand { - /// Returns [`nil`](). + /// Returns nil. @inlinable public var outline:Mongo.OutlineVector? { @@ -152,12 +151,12 @@ extension MongoCommand /// as a field with the key [`"$db"`](). public __consuming func encode(database:Database, labels:Mongo.SessionLabels?, - by deadline:ContinuousClock.Instant) -> MongoWire.Message<[UInt8]>.Sections? + by deadline:ContinuousClock.Instant) -> Mongo.WireMessage<[UInt8]>.Sections? { // do this first, so we never have to access `self` after reading `self.fields` - let outlined:[MongoWire.Message<[UInt8]>.Outline]? = self.outline.map + let outlined:[Mongo.WireMessage<[UInt8]>.Outline]? = self.outline.map { - [.init(id: $0.type.rawValue, slice: $0.documents.slice)] + [.init(id: $0.type.rawValue, slice: $0.bson.destination)] } let now:ContinuousClock.Instant = .now @@ -173,7 +172,7 @@ extension MongoCommand switch $0 { case .auto: - return .init(truncating: now.duration(to: deadline)) + .init(truncating: now.duration(to: deadline)) } } diff --git a/Sources/MongoDriver/Commands/Ping/Mongo.Ping.swift b/Sources/MongoDriver/Commands/Ping/Mongo.Ping.swift index 4da657c7..882f0777 100644 --- a/Sources/MongoDriver/Commands/Ping/Mongo.Ping.swift +++ b/Sources/MongoDriver/Commands/Ping/Mongo.Ping.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/RefreshSessions/Mongo.RefreshSessions.swift b/Sources/MongoDriver/Commands/RefreshSessions/Mongo.RefreshSessions.swift index 66ecb7e9..f9af2f1e 100644 --- a/Sources/MongoDriver/Commands/RefreshSessions/Mongo.RefreshSessions.swift +++ b/Sources/MongoDriver/Commands/RefreshSessions/Mongo.RefreshSessions.swift @@ -1,5 +1,5 @@ -import BSONEncoding -import MongoSchema +import BSON +import MongoABI extension Mongo { diff --git a/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.Member.swift b/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.Member.swift index ddbee589..76788045 100644 --- a/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.Member.swift +++ b/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.Member.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import BSON_Durations import BSON_OrderedCollections import Durations @@ -19,7 +18,7 @@ extension Mongo.ReplicaSetConfiguration let id:Int64 public let host:Mongo.Host - /// Information about this member if it is a replica, [`nil`]() + /// Information about this member if it is a replica, nil /// if (and only if) it is an arbiter. public let replica:Replica? diff --git a/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.swift b/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.swift index 984eea3b..17a1acfc 100644 --- a/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.swift +++ b/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetConfiguration.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetGetConfiguration.swift b/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetGetConfiguration.swift index 59313aa0..29727a84 100644 --- a/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetGetConfiguration.swift +++ b/Sources/MongoDriver/Commands/ReplicaSetGetConfiguration/Mongo.ReplicaSetGetConfiguration.swift @@ -1,6 +1,5 @@ -import BSONDecoding -import BSONEncoding -import MongoSchema +import BSON +import MongoABI import NIOCore extension Mongo diff --git a/Sources/MongoDriver/Commands/SASL/Mongo.SASLContinue.swift b/Sources/MongoDriver/Commands/SASL/Mongo.SASLContinue.swift index d5aa0d38..a35f17ef 100644 --- a/Sources/MongoDriver/Commands/SASL/Mongo.SASLContinue.swift +++ b/Sources/MongoDriver/Commands/SASL/Mongo.SASLContinue.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import SCRAM extension Mongo diff --git a/Sources/MongoDriver/Commands/SASL/Mongo.SASLResponse.swift b/Sources/MongoDriver/Commands/SASL/Mongo.SASLResponse.swift index 0ec26228..988e4770 100644 --- a/Sources/MongoDriver/Commands/SASL/Mongo.SASLResponse.swift +++ b/Sources/MongoDriver/Commands/SASL/Mongo.SASLResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import SCRAM extension Mongo diff --git a/Sources/MongoDriver/Commands/SASL/Mongo.SASLStart.swift b/Sources/MongoDriver/Commands/SASL/Mongo.SASLStart.swift index cb824b47..b5478586 100644 --- a/Sources/MongoDriver/Commands/SASL/Mongo.SASLStart.swift +++ b/Sources/MongoDriver/Commands/SASL/Mongo.SASLStart.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import SCRAM extension Mongo diff --git a/Sources/MongoDriver/Connections/Mongo.Connection.swift b/Sources/MongoDriver/Connections/Mongo.Connection.swift index c429a899..ff8bf507 100644 --- a/Sources/MongoDriver/Connections/Mongo.Connection.swift +++ b/Sources/MongoDriver/Connections/Mongo.Connection.swift @@ -1,5 +1,6 @@ import MongoExecutor import MongoWire +import NIOCore extension Mongo { @@ -94,23 +95,28 @@ extension Mongo.Connection where Command:MongoCommand { let deadline:ContinuousClock.Instant = self.pool.adjust(deadline: deadline) - guard let command:MongoWire.Message<[UInt8]>.Sections = command.encode( - database: database, - labels: labels, - by: deadline) + guard + let command:Mongo.WireMessage<[UInt8]>.Sections = command.encode( + database: database, + labels: labels, + by: deadline) else { - throw Mongo.TimeoutError.driver(written: false) + throw Mongo.DriverTimeoutError.init() } - switch await self.allocation.request(sections: command, deadline: deadline) - { - case .success(let message): - return try .init(message: message) + let message:Mongo.WireMessage - case .failure(let error): + do + { + message = try await self.allocation.request(sections: command, deadline: deadline) + } + catch let error + { self.reuse = false - throw try Mongo.NetworkError.init(triaging: error) + throw error } + + return try .init(message: message) } } diff --git a/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocation.swift b/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocation.swift index 91d0d151..c965618f 100644 --- a/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocation.swift +++ b/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocation.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoExecutor import NIOCore diff --git a/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocations.swift b/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocations.swift index 3cf65ed9..afd59e69 100644 --- a/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocations.swift +++ b/Sources/MongoDriver/Connections/Mongo.ConnectionPool.Allocations.swift @@ -125,15 +125,15 @@ extension Mongo.ConnectionPool.Allocations /// Unblocks an awaiting request with the given channel, if one exists. /// /// - Returns: - /// [`true`]() if there was a request that was unblocked by this call, - /// [`false`]() otherwise. + /// `true` if there was a request that was unblocked by this call, + /// `false` otherwise. mutating func yield(_ allocation:Mongo.ConnectionPool.Allocation) -> Bool { switch self.requests.popFirst()?.value.resume(returning: allocation) { - case nil: return false - case ()?: return true + case nil: false + case ()?: true } } mutating @@ -334,10 +334,10 @@ extension Mongo.ConnectionPool.Allocations switch self.phase { case .connecting(let connector): - return self.reserve(connector: connector, releasing: releasing, settings: settings) + self.reserve(connector: connector, releasing: releasing, settings: settings) case .draining: - return nil + nil } } private mutating @@ -348,11 +348,11 @@ extension Mongo.ConnectionPool.Allocations if self.pending < settings.rate, self.expandable(releasing: releasing, settings: settings) { - return self.reserve(connector: connector) + self.reserve(connector: connector) } else { - return nil + nil } } private mutating @@ -441,7 +441,9 @@ extension Mongo.ConnectionPool.Allocations { for channel:any Channel in [self.retained.values, self.released.values].joined() { - channel.writeAndFlush(MongoIO.Action.cancel(throwing: .crosscancelled(error)), + channel.writeAndFlush(Mongo.WireAction.cancel(throwing: Mongo.NetworkError.init( + underlying: error, + provenance: .crosscancellation)), promise: nil) } } diff --git a/Sources/MongoDriver/Connections/Mongo.ConnectionPool.swift b/Sources/MongoDriver/Connections/Mongo.ConnectionPool.swift index 7947922c..32eaa761 100644 --- a/Sources/MongoDriver/Connections/Mongo.ConnectionPool.swift +++ b/Sources/MongoDriver/Connections/Mongo.ConnectionPool.swift @@ -127,8 +127,8 @@ extension Mongo.ConnectionPool /// The number of non-perished connections, including pending connections, /// currently in the pool. /// - /// Connection count is regulated by the pool’s ``Parameters size`` and - /// ``Parameters rate`` parameters. + /// Connection count is regulated by the pool’s ``Parameters/size`` and + /// ``Parameters/rate`` parameters. public var count:Int { @@ -209,7 +209,7 @@ extension Mongo.ConnectionPool /// pool when the actor services the request. /// /// If the deadline passes while the pool is creating a connection for the - /// caller, the call will return [`nil`](), but the allocation will still be + /// caller, the call will return nil, but the allocation will still be /// created and added to the pool, and may be used to complete a different /// request. func create(by deadline:ContinuousClock.Instant) async throws -> Allocation @@ -253,18 +253,18 @@ extension Mongo.ConnectionPool /// pool. /// - reuse: /// Indicates if the connection should be reused. - /// If [`false`](), the channel will be asynchronously closed and + /// If `false`, the channel will be asynchronously closed and /// removed from the pool. /// /// Every *successful* call to ``create(by:)`` must be paired with a call /// to `destroy(_:reuse:)`. /// - /// If `reuse` is [`true`](), later calls to ``create(by:)`` will wait for + /// If `reuse` is `true`, later calls to ``create(by:)`` will wait for /// the destroyed connection to be re-indexed, instead of establishing a new /// one, provided that there are more un-indexed connections than /// currently-blocked requests. /// - /// If `reuse` is [`false`](), this method does not replace the connection, + /// If `reuse` is `false`, this method does not replace the connection, /// because replacement is performed when the underlying channel closes, /// which may take place before the wrapping connection is destroyed. nonisolated diff --git a/Sources/MongoDriver/Connections/Mongo.ConnectionPoolDrainedError.swift b/Sources/MongoDriver/Connections/Mongo.ConnectionPoolDrainedError.swift index b13439ab..56e9aede 100644 --- a/Sources/MongoDriver/Connections/Mongo.ConnectionPoolDrainedError.swift +++ b/Sources/MongoDriver/Connections/Mongo.ConnectionPoolDrainedError.swift @@ -1,5 +1,5 @@ -import TraceableErrors import MongoClusters +import TraceableErrors extension Mongo { diff --git a/Sources/MongoDriver/Connections/Mongo.ConnectorFactory.swift b/Sources/MongoDriver/Connections/Mongo.ConnectorFactory.swift index f206645b..3697d9e5 100644 --- a/Sources/MongoDriver/Connections/Mongo.ConnectorFactory.swift +++ b/Sources/MongoDriver/Connections/Mongo.ConnectorFactory.swift @@ -15,7 +15,7 @@ extension Mongo let appname:String? private let tls:TLS - + init(executors:any EventLoopGroup, appname:String?, tls:TLS) { self.executors = executors @@ -43,31 +43,31 @@ extension Mongo.ConnectorFactory func bootstrap(timeout:Milliseconds, host:Mongo.Host) -> ClientBootstrap { .init(group: self.executors) - .channelOption(ChannelOptions.Types.ConnectTimeoutOption.init(), + .channelOption(ChannelOptions.Types.ConnectTimeoutOption.init(), value: .milliseconds(timeout.rawValue)) .channelOption(ChannelOptions.Types.SocketOption.init( level: Int.init(SOL_SOCKET), - name: SO_REUSEADDR), + name: SO_REUSEADDR), value: 1) .channelInitializer - { + { (channel:any Channel) in - let decoder:ByteToMessageHandler = .init(.init()) - let router:MongoIO.MessageRouter = .init() + let decoder:ByteToMessageHandler = .init(.init()) + let router:Mongo.WireMessageRouter = .init() guard case .enabled = self.tls else { return channel.pipeline.addHandlers(decoder, router) } - do + do { let tls:NIOSSLClientHandler = try .init(context: .init( - configuration: .clientDefault), + configuration: .clientDefault), serverHostname: host.name) return channel.pipeline.addHandlers(tls, decoder, router) - } + } catch let error { return channel.eventLoop.makeFailedFuture(error) diff --git a/Sources/MongoDriver/Cursors/Mongo.Cursor.Batch.swift b/Sources/MongoDriver/Cursors/Mongo.Cursor.Batch.swift index 84322745..acba07e8 100644 --- a/Sources/MongoDriver/Cursors/Mongo.Cursor.Batch.swift +++ b/Sources/MongoDriver/Cursors/Mongo.Cursor.Batch.swift @@ -1,5 +1,5 @@ -import BSONDecoding -import MongoSchema +import BSON +import MongoABI extension Mongo.Cursor { diff --git a/Sources/MongoDriver/Cursors/Mongo.Cursor.swift b/Sources/MongoDriver/Cursors/Mongo.Cursor.swift index 1dd5b388..11a250b0 100644 --- a/Sources/MongoDriver/Cursors/Mongo.Cursor.swift +++ b/Sources/MongoDriver/Cursors/Mongo.Cursor.swift @@ -1,5 +1,5 @@ +import BSON import Durations -import BSONDecoding extension Mongo { diff --git a/Sources/MongoDriver/Cursors/Mongo.CursorIdentifier.swift b/Sources/MongoDriver/Cursors/Mongo.CursorIdentifier.swift index be69b885..0191280c 100644 --- a/Sources/MongoDriver/Cursors/Mongo.CursorIdentifier.swift +++ b/Sources/MongoDriver/Cursors/Mongo.CursorIdentifier.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Cursors/Mongo.CursorIterator.swift b/Sources/MongoDriver/Cursors/Mongo.CursorIterator.swift index fdd8f63b..51cd5abd 100644 --- a/Sources/MongoDriver/Cursors/Mongo.CursorIterator.swift +++ b/Sources/MongoDriver/Cursors/Mongo.CursorIterator.swift @@ -1,6 +1,6 @@ -import BSONDecoding +import BSON import Durations -import MongoSchema +import MongoABI extension Mongo { @@ -72,27 +72,27 @@ extension Mongo.CursorIterator switch self.lifecycle { case .iterable(let timeout): - return .now.advanced(by: .milliseconds(timeout ?? self.timeout)) + .now.advanced(by: .milliseconds(timeout ?? self.timeout)) case .expires(let deadline): - return deadline + deadline } } } extension Mongo.CursorIterator { /// Runs a ``GetMore`` command from this cursor. This operation will set a - /// deadline for itself depending on the cursor’s ``lifecycle`` mode. + /// deadline for itself depending on the cursor’s lifecycle mode. /// /// - If the cursor is **tailable** with a user-specified timeout - /// (``case CursorLifecycle.iterable(_:)``), the deadline will be the + /// (``CursorLifecycle.iterable(_:)``), the deadline will be the /// current time, advanced by that timeout. /// /// - If the cursor is **tailable** with no user-specified timeout - /// (``case CursorLifecycle.iterable(_:)``), the deadline will be the + /// (``CursorLifecycle.iterable(_:)``), the deadline will be the /// current time, advanced by the default operation timeout. /// - /// - If the cursor is **non-tailable** (``case CursorLifecycle.expires(_:)``), + /// - If the cursor is **non-tailable** (``CursorLifecycle.expires(_:)``), /// the deadline will be the same as the deadline that was set for the /// command that obtained the cursor. /// diff --git a/Sources/MongoDriver/Cursors/Mongo.CursorLifecycle.swift b/Sources/MongoDriver/Cursors/Mongo.CursorLifecycle.swift index 1c9c7704..e146d667 100644 --- a/Sources/MongoDriver/Cursors/Mongo.CursorLifecycle.swift +++ b/Sources/MongoDriver/Cursors/Mongo.CursorLifecycle.swift @@ -19,8 +19,8 @@ extension Mongo.CursorLifecycle { // maxTimeMS can only be sent for tailable // (iteration-based lifecycle) cursors. - case .iterable: return .auto - case .expires: return nil + case .iterable: .auto + case .expires: nil } } } diff --git a/Sources/MongoDriver/Cursors/MongoIterableCommand.swift b/Sources/MongoDriver/Cursors/MongoIterableCommand.swift index 68963724..5fd9c6ba 100644 --- a/Sources/MongoDriver/Cursors/MongoIterableCommand.swift +++ b/Sources/MongoDriver/Cursors/MongoIterableCommand.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import Durations +import BSON public protocol MongoIterableCommand:MongoCommand diff --git a/Sources/MongoDriver/Deployments/Mongo.ClusterTime.swift b/Sources/MongoDriver/Deployments/Mongo.ClusterTime.swift index 18245e6a..8ac961b4 100644 --- a/Sources/MongoDriver/Deployments/Mongo.ClusterTime.swift +++ b/Sources/MongoDriver/Deployments/Mongo.ClusterTime.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Deployments/Mongo.Deployment.swift b/Sources/MongoDriver/Deployments/Mongo.Deployment.swift index 1ceb7023..68ecd63a 100644 --- a/Sources/MongoDriver/Deployments/Mongo.Deployment.swift +++ b/Sources/MongoDriver/Deployments/Mongo.Deployment.swift @@ -189,11 +189,11 @@ extension Mongo.Deployment { if let capabilities:Mongo.DeploymentCapabilities = self.capabilities { - return capabilities + capabilities } else { - return try await self.capabilities(by: deadline).get() + try await self.capabilities(by: deadline).get() } } private @@ -268,8 +268,8 @@ extension Mongo.Deployment /// sending any command if `sessions` is empty. /// /// - Returns: - /// [`true`]() if `sessions` was empty or the command was sent - /// and successfully executed; [`false`]() if at least one session was + /// `true` if `sessions` was empty or the command was sent + /// and successfully executed; `false` if at least one session was /// provided, but there were no suitable servers to send the command /// to, or if the command was sent but it failed on the server’s side. /// diff --git a/Sources/MongoDriver/Deployments/Mongo.DeploymentStateError.swift b/Sources/MongoDriver/Deployments/Mongo.DeploymentStateError.swift index 86fc7a79..3b78e820 100644 --- a/Sources/MongoDriver/Deployments/Mongo.DeploymentStateError.swift +++ b/Sources/MongoDriver/Deployments/Mongo.DeploymentStateError.swift @@ -1,5 +1,5 @@ -import TraceableErrors import MongoClusters +import TraceableErrors extension Mongo { diff --git a/Sources/MongoDriver/Deployments/Mongo.ServerTable.swift b/Sources/MongoDriver/Deployments/Mongo.ServerTable.swift index 6f961e1d..d14690e1 100644 --- a/Sources/MongoDriver/Deployments/Mongo.ServerTable.swift +++ b/Sources/MongoDriver/Deployments/Mongo.ServerTable.swift @@ -28,20 +28,20 @@ extension Mongo.ServerTable { switch self { - case .none(let unreachable): return unreachable - case .single(_): return [:] - case .sharded(let sharded): return sharded.unreachables - case .replicated(let replicated): return replicated.unreachables + case .none(let unreachable): unreachable + case .single(_): [:] + case .sharded(let sharded): sharded.unreachables + case .replicated(let replicated): replicated.unreachables } } var capabilities:Mongo.DeploymentCapabilities? { switch self { - case .none: return nil - case .single(let single): return single.capabilities - case .sharded(let sharded): return sharded.capabilities - case .replicated(let replicated): return replicated.capabilities + case .none: nil + case .single(let single): single.capabilities + case .sharded(let sharded): sharded.capabilities + case .replicated(let replicated): replicated.capabilities } } } @@ -90,37 +90,37 @@ extension Mongo.ServerTable switch self { case .none: - return nil + nil case .single(let standalone): switch preference { case .primary, .primaryPreferred, .nearest, .secondaryPreferred: - return standalone.server.pool + standalone.server.pool case .secondary: - return nil + nil } case .sharded(let routers): - return routers.candidates.first?.pool + routers.candidates.first?.pool case .replicated(let members): switch preference { case .primary: - return members.primary + members.primary case .primaryPreferred (let eligibility, hedge: _): - return members.primary ?? members.secondary(by: eligibility) + members.primary ?? members.secondary(by: eligibility) case .nearest (let eligibility, hedge: _): - return members.nearest(by: eligibility) + members.nearest(by: eligibility) case .secondaryPreferred (let eligibility, hedge: _): - return members.secondary(by: eligibility) ?? members.primary + members.secondary(by: eligibility) ?? members.primary case .secondary (let eligibility, hedge: _): - return members.secondary(by: eligibility) + members.secondary(by: eligibility) } } } diff --git a/Sources/MongoDriver/Failpoints/Mongo.FailCommand.swift b/Sources/MongoDriver/Failpoints/Mongo.FailCommand.swift index c5e7dd2a..1c16e178 100644 --- a/Sources/MongoDriver/Failpoints/Mongo.FailCommand.swift +++ b/Sources/MongoDriver/Failpoints/Mongo.FailCommand.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { @@ -49,14 +49,14 @@ extension Mongo.FailCommand:BSONEncodable, BSONDocumentEncodable { case .error(let code): bson["errorCode"] = code - + case .writeConcernError(let error): bson["writeConcernError"] = error } - + case .closeConnection?: bson["closeConnection"] = true - + case nil: break } diff --git a/Sources/MongoDriver/Failpoints/MongoFailpoint.swift b/Sources/MongoDriver/Failpoints/MongoFailpoint.swift index 33f6ad22..20aaae37 100644 --- a/Sources/MongoDriver/Failpoints/MongoFailpoint.swift +++ b/Sources/MongoDriver/Failpoints/MongoFailpoint.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON public protocol MongoFailpoint:BSONEncodable, Sendable diff --git a/Sources/MongoDriver/MongoExecutor (ext).swift b/Sources/MongoDriver/MongoExecutor (ext).swift index 57c48e4a..52b109e2 100644 --- a/Sources/MongoDriver/MongoExecutor (ext).swift +++ b/Sources/MongoDriver/MongoExecutor (ext).swift @@ -1,11 +1,11 @@ -import BSONEncoding +import BSON import MongoExecutor import MongoWire extension MongoExecutor { /// Encodes the given command to a document, adding the given database - /// as a field with the key [`"$db"`](), sends it over this channel, and + /// as a field with the key `"$db"`, sends it over this channel, and /// awaits its reply. /// /// If the deadline passes before this function can start executing, this @@ -22,28 +22,22 @@ extension MongoExecutor where Command:BSONDocumentEncodable { try Task.checkCancellation() - + let now:ContinuousClock.Instant = .now - let sections:MongoWire.Message<[UInt8]>.Sections + let sections:Mongo.WireMessage<[UInt8]>.Sections - if now < deadline - { - var document:BSON.Document = .init(encoding: command) - document["$db"] = database.name - sections = .init(body: .init(document)) - } + guard now < deadline else { - throw Mongo.TimeoutError.driver(written: false) + throw Mongo.DriverTimeoutError.init() } - switch await self.request(sections: sections, deadline: deadline) - { - case .success(let message): - return try .init(message: message) + var document:BSON.Document = .init(encoding: command) + document["$db"] = database.name + sections = .init(body: .init(document)) - case .failure(let error): - throw try Mongo.NetworkError.init(triaging: error) - } + return try .init(message: try await self.request( + sections: sections, + deadline: deadline)) } } diff --git a/Sources/MongoDriver/Monitoring/Mongo.HelloResponse.swift b/Sources/MongoDriver/Monitoring/Mongo.HelloResponse.swift index 9de45a32..daddf8f7 100644 --- a/Sources/MongoDriver/Monitoring/Mongo.HelloResponse.swift +++ b/Sources/MongoDriver/Monitoring/Mongo.HelloResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import BSON_Durations import Durations import MongoWire @@ -15,7 +15,8 @@ extension Mongo /// Type-specific information about the server and its role in a topology model. let topologyUpdate:TopologyUpdate /// Returns the local server time in UTC. This value is an - /// [ISO date](https://www.mongodb.com/docs/manual/reference/glossary/#std-term-ISODate). + /// [ISO + /// date](https://www.mongodb.com/docs/manual/reference/glossary/#std-term-ISODate). let localTime:BSON.Millisecond init(topologyVersion:TopologyVersion, @@ -32,11 +33,9 @@ extension Mongo.HelloResponse:BSONDocumentDecodable { init(bson:BSON.DocumentDecoder>) throws { - let minWireVersion:MongoWire = try bson["minWireVersion"].decode(as: Int32.self, - with: MongoWire.init(rawValue:)) - let maxWireVersion:MongoWire = try bson["maxWireVersion"].decode(as: Int32.self, - with: MongoWire.init(rawValue:)) - + let minWireVersion:Mongo.WireVersion = try bson["minWireVersion"].decode() + let maxWireVersion:Mongo.WireVersion = try bson["maxWireVersion"].decode() + // consider maxWireVersion authoritative guard maxWireVersion >= 17 else @@ -64,7 +63,7 @@ extension Mongo.HelloResponse:BSONDocumentDecodable { let tags:[String: String]? = try bson["tags"]?.decode( to: [String: String].self) - + let peerlist:Mongo.Peerlist = .init(set: set, primary: try bson["primary"]?.decode(to: Mongo.Host.self), arbiters: try bson["arbiters"]?.decode(to: [Mongo.Host].self) ?? [], @@ -72,8 +71,8 @@ extension Mongo.HelloResponse:BSONDocumentDecodable hosts: try bson["hosts"].decode(to: [Mongo.Host].self), me: try bson["me"].decode(to: Mongo.Host.self)) - if case true? = - try (bson["isWritablePrimary"] ?? bson["ismaster"])?.decode(to: Bool.self) + if case true? = try (bson["isWritablePrimary"] ?? bson["ismaster"])?.decode( + to: Bool.self) { let replica:Mongo.Replica = .init(capabilities: try capabilities(), timings: try bson["lastWrite"].decode(to: Mongo.Replica.Timings.self), @@ -83,14 +82,16 @@ extension Mongo.HelloResponse:BSONDocumentDecodable version: try bson["setVersion"].decode(to: Int64.self))), peerlist) } - else if case true? = try bson["secondary"]?.decode(to: Bool.self) + else if + case true? = try bson["secondary"]?.decode(to: Bool.self) { let replica:Mongo.Replica = .init(capabilities: try capabilities(), timings: try bson["lastWrite"].decode(to: Mongo.Replica.Timings.self), tags: tags ?? [:]) topologyUpdate = .slave(.secondary(replica), peerlist) } - else if case true? = try bson["arbiterOnly"]?.decode(to: Bool.self) + else if + case true? = try bson["arbiterOnly"]?.decode(to: Bool.self) { topologyUpdate = .slave(.arbiter, peerlist) } @@ -101,19 +102,18 @@ extension Mongo.HelloResponse:BSONDocumentDecodable } else { - if case true? = try bson["isreplicaset"]?.decode(to: Bool.self) + if case true? = try bson["isreplicaset"]?.decode(to: Bool.self) { topologyUpdate = .ghost } - else if case "isdbgrid"? = try bson["msg"]?.decode(to: String.self) + else if + case "isdbgrid"? = try bson["msg"]?.decode(to: String.self) { - topologyUpdate = .router(.init( - capabilities: try capabilities())) + topologyUpdate = .router(.init(capabilities: try capabilities())) } else { - topologyUpdate = .standalone(.init( - capabilities: try capabilities())) + topologyUpdate = .standalone(.init(capabilities: try capabilities())) } } diff --git a/Sources/MongoDriver/Monitoring/Mongo.TopologyVersion.swift b/Sources/MongoDriver/Monitoring/Mongo.TopologyVersion.swift index 811fd9a9..dcefd61c 100644 --- a/Sources/MongoDriver/Monitoring/Mongo.TopologyVersion.swift +++ b/Sources/MongoDriver/Monitoring/Mongo.TopologyVersion.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Outlining/Mongo.OutlineDocuments.swift b/Sources/MongoDriver/Outlining/Mongo.OutlineDocuments.swift deleted file mode 100644 index 0bde0a35..00000000 --- a/Sources/MongoDriver/Outlining/Mongo.OutlineDocuments.swift +++ /dev/null @@ -1,65 +0,0 @@ -import BSONEncoding - -extension Mongo -{ - /// A payload is an efficient means of encoding long sequences of BSON - /// documents. Payloads are subject to the maximum wire message size, - /// which is usually 48 MB, and larger than the maximum BSON document - /// size, which is usually 16 MB. - @frozen public - struct OutlineDocuments:Sendable - { - @usableFromInline - var output:BSON.Output<[UInt8]> - - @inlinable public - init() - { - self.output = .init(preallocated: []) - } - } -} -extension Mongo.OutlineDocuments -{ - var slice:[UInt8] - { - self.output.destination - } -} -extension Mongo.OutlineDocuments -{ - @inlinable public - init(_ elements:some Sequence) where Encodable:BSONDocumentEncodable - { - self.init() - for element:Encodable in elements - { - self.append(element) - } - } - @inlinable public - init(_ elements:some Sequence) - where Element:BSONRepresentable - { - self.init() - for element:Element in elements - { - self.append(BSON.DocumentView<[UInt8]>.init(element.bson)) - } - } -} -extension Mongo.OutlineDocuments -{ - @inlinable public mutating - func append(_ element:some BSONDocumentEncodable) - { - element.encode(to: &self.output[ - as: BSON.DocumentEncoder.self, - in: BSON.DocumentFrame.self]) - } - public mutating - func append(_ document:BSON.DocumentView<[UInt8]>) - { - self.output.serialize(document: document) - } -} diff --git a/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Level.swift b/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Level.swift index 5f6ef116..ddfe49df 100644 --- a/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Level.swift +++ b/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Level.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.ReadConcern { @@ -17,16 +17,16 @@ extension Mongo.ReadConcern.Level:CustomStringConvertible switch self { case .snapshot: - return "snapshot" + "snapshot" case .ratification(let level): - return level.rawValue + level.rawValue } } } extension Mongo.ReadConcern.Level:BSONEncodable { public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { self.description.encode(to: &field) } diff --git a/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Options.swift b/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Options.swift index d23564cf..e915c017 100644 --- a/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Options.swift +++ b/Sources/MongoDriver/ReadConcern/Mongo.ReadConcern.Options.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.ReadConcern { diff --git a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Eligibility.swift b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Eligibility.swift index be09d3f1..aa92c92c 100644 --- a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Eligibility.swift +++ b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Eligibility.swift @@ -33,7 +33,7 @@ extension Mongo.ReadPreference.Eligibility { if let maxStaleness:Milliseconds = self.maxStaleness?.milliseconds { - return candidates.reduce(into: [:]) + candidates.reduce(into: [:]) { $0[$1.host] = $1.metadata.staleness > maxStaleness ? .stale($1.metadata.staleness) : @@ -42,7 +42,7 @@ extension Mongo.ReadPreference.Eligibility } else { - return candidates.reduce(into: [:]) + candidates.reduce(into: [:]) { $0[$1.host] = .tags($1.metadata.tags) } diff --git a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Hedging.swift b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Hedging.swift index 1cf47d01..cad24bbe 100644 --- a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Hedging.swift +++ b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Hedging.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.ReadPreference { diff --git a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Mode.swift b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Mode.swift index 8a3b4609..7da16f50 100644 --- a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Mode.swift +++ b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.Mode.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.ReadPreference { diff --git a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.TagSet.swift b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.TagSet.swift index b50fabec..2887f3b1 100644 --- a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.TagSet.swift +++ b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.TagSet.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo.ReadPreference { diff --git a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.swift b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.swift index 98cd40fa..00dad536 100644 --- a/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.swift +++ b/Sources/MongoDriver/ReadPreference/Mongo.ReadPreference.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import Durations extension Mongo @@ -81,11 +81,11 @@ extension Mongo.ReadPreference { switch self { - case .secondary: return .secondary - case .secondaryPreferred: return .secondaryPreferred - case .nearest: return .nearest - case .primaryPreferred: return .primaryPreferred - case .primary: return .primary + case .secondary: .secondary + case .secondaryPreferred: .secondaryPreferred + case .nearest: .nearest + case .primaryPreferred: .primaryPreferred + case .primary: .primary } } var eligibility:Eligibility? @@ -96,9 +96,9 @@ extension Mongo.ReadPreference .secondaryPreferred (let eligibility, hedge: _), .nearest (let eligibility, hedge: _), .primaryPreferred (let eligibility, hedge: _): - return eligibility + eligibility case _: - return nil + nil } } var maxStaleness:Seconds? @@ -117,9 +117,9 @@ extension Mongo.ReadPreference .secondaryPreferred (_, hedge: let hedging), .nearest (_, hedge: let hedging), .primaryPreferred (_, hedge: let hedging): - return hedging + hedging case _: - return nil + nil } } } @@ -142,20 +142,20 @@ extension Mongo.ReadPreference { case .none(let unreachable): return .init(unreachable: unreachable) - + case .single(let standalone): switch self { case .primary, .primaryPreferred, .nearest, .secondaryPreferred: return .init() - + case .secondary: return .init(undesirable: [standalone.server.host: .standalone]) } - + case .sharded(let routers): return .init(unreachable: routers.unreachables) - + case .replicated(let members): let undesirable:[Mongo.Host: Mongo.Undesirable] let unsuitable:[Mongo.Host: Mongo.Unsuitable] @@ -168,12 +168,12 @@ extension Mongo.ReadPreference $0[$1.host] = .secondary } unsuitable = [:] - + case .primaryPreferred (let eligibility, hedge: _), .secondaryPreferred (let eligibility, hedge: _): undesirable = members.undesirables unsuitable = eligibility.diagnose(unsuitable: members.candidates.secondaries) - + case .secondary (let eligibility, hedge: _): var undesirables:[Mongo.Host: Mongo.Undesirable] = members.undesirables if let primary:Mongo.Host = members.candidates.primary?.host @@ -182,7 +182,7 @@ extension Mongo.ReadPreference } undesirable = undesirables unsuitable = eligibility.diagnose(unsuitable: members.candidates.secondaries) - + case .nearest (let eligibility, hedge: _): undesirable = members.undesirables unsuitable = eligibility.diagnose(unsuitable: members.candidates.replicas) diff --git a/Sources/MongoDriver/Retryability/Mongo.DriverTimeoutError.swift b/Sources/MongoDriver/Retryability/Mongo.DriverTimeoutError.swift new file mode 100644 index 00000000..382fb807 --- /dev/null +++ b/Sources/MongoDriver/Retryability/Mongo.DriverTimeoutError.swift @@ -0,0 +1,14 @@ +extension Mongo +{ + /// A command was timed-out by the driver because the its deadline has already passed. + /// + /// This error indicates that the command was never sent over the wire to begin with. + @frozen public + struct DriverTimeoutError:Error, Equatable, Sendable + { + @inlinable public + init() + { + } + } +} diff --git a/Sources/MongoDriver/Retryability/Mongo.NamespaceError.swift b/Sources/MongoDriver/Retryability/Mongo.NamespaceError.swift index 84742820..6438370a 100644 --- a/Sources/MongoDriver/Retryability/Mongo.NamespaceError.swift +++ b/Sources/MongoDriver/Retryability/Mongo.NamespaceError.swift @@ -1,24 +1,11 @@ -import TraceableErrors - extension Mongo { + @available(*, deprecated, message: """ + This type no longer functions properly; \ + clients should check for ServerError(26) instead. + """) @frozen public struct NamespaceError:Equatable, Error { - @inlinable public - init() - { - } - } -} -extension Mongo.NamespaceError:NamedError -{ - public - var name:String { "NamespaceError" } - - public - var message:String - { - "namespace does not exist" } } diff --git a/Sources/MongoDriver/Retryability/Mongo.NetworkError (ext).swift b/Sources/MongoDriver/Retryability/Mongo.NetworkError (ext).swift new file mode 100644 index 00000000..d281762a --- /dev/null +++ b/Sources/MongoDriver/Retryability/Mongo.NetworkError (ext).swift @@ -0,0 +1,23 @@ +import MongoIO +import TraceableErrors + +extension Mongo.NetworkError:TraceableError +{ + public + var notes:[String] + { + switch self.provenance + { + case nil: + [] + + case .crosscancellation: + ["error propogated via cross-cancellation"] + } + } +} +extension Mongo.NetworkError:MongoRetryableError +{ + public + var isRetryable:Bool { true } +} diff --git a/Sources/MongoDriver/Retryability/Mongo.NetworkError.swift b/Sources/MongoDriver/Retryability/Mongo.NetworkError.swift deleted file mode 100644 index ee3dcd84..00000000 --- a/Sources/MongoDriver/Retryability/Mongo.NetworkError.swift +++ /dev/null @@ -1,62 +0,0 @@ -import MongoIO -import TraceableErrors - -extension Mongo -{ - @frozen public - struct NetworkError:Error - { - public - let underlying:any Error - public - let provenance:NetworkErrorProvenance? - - @inlinable public - init(underlying:any Error, provenance:NetworkErrorProvenance?) - { - self.underlying = underlying - self.provenance = provenance - } - } -} -extension Mongo.NetworkError -{ - public - init(triaging error:MongoIO.ChannelError) throws - { - switch error - { - case .io(let error, written: _): - self.init(underlying: error, provenance: nil) - - case .cancelled(.timeout): - throw Mongo.TimeoutError.driver(written: true) - - case .cancelled(.cancel): - throw CancellationError.init() - - case .crosscancelled(let error): - self.init(underlying: error, provenance: .crosscancellation) - } - } -} -extension Mongo.NetworkError:TraceableError -{ - public - var notes:[String] - { - switch self.provenance - { - case nil: - return [] - - case .crosscancellation: - return ["error propogated via cross-cancellation"] - } - } -} -extension Mongo.NetworkError:MongoRetryableError -{ - public - var isRetryable:Bool { true } -} diff --git a/Sources/MongoDriver/Retryability/Mongo.ServerError.Code.swift b/Sources/MongoDriver/Retryability/Mongo.ServerError.Code.swift index 9b3ed5aa..62cdec68 100644 --- a/Sources/MongoDriver/Retryability/Mongo.ServerError.Code.swift +++ b/Sources/MongoDriver/Retryability/Mongo.ServerError.Code.swift @@ -1,5 +1,4 @@ -import BSONEncoding -import BSONDecoding +import BSON extension Mongo.ServerError { @@ -45,9 +44,9 @@ extension Mongo.ServerError.Code { case 6, 7, 89, 91, 134, 189, 262, 317, 358, 384, 9001, 10107, 11600, 11602, 13435, 13436, 50915: - return true + true case _: - return false + false } } var indicatesInterruption:Bool @@ -55,19 +54,25 @@ extension Mongo.ServerError.Code switch self.rawValue { case 24, 50, 237, 262, 279, 281, 282, 290, 355, 11600, 11601, 11602, 46841: - return true + true case _: - return false + false } } + /// A command was timed-out by the server, most likely according to `maxTimeMS` + /// (``MaxTime``). + /// + /// Server-side timeouts are efficient, because the driver can reuse the connection used to + /// run the original command to run another command. + public var indicatesTimeLimitExceeded:Bool { switch self.rawValue { case 50, 202, 262, 290: - return true + true case _: - return false + false } } var indicatesNotPrimary:Bool @@ -75,9 +80,9 @@ extension Mongo.ServerError.Code switch self.rawValue { case 189, 10107, 11602, 13435, 13436: - return true + true case _: - return false + false } } } diff --git a/Sources/MongoDriver/Retryability/Mongo.ServerError.swift b/Sources/MongoDriver/Retryability/Mongo.ServerError.swift index 263cff05..abbcd936 100644 --- a/Sources/MongoDriver/Retryability/Mongo.ServerError.swift +++ b/Sources/MongoDriver/Retryability/Mongo.ServerError.swift @@ -8,10 +8,10 @@ extension Mongo public let message:String public - let code:Code? + let code:Code public - init(_ code:Code?, message:String) + init(_ code:Code, message:String) { self.message = message self.code = code @@ -23,7 +23,7 @@ extension Mongo.ServerError:NamedError public var name:String { - self.code.map { "ServerError (\($0))" } ?? "ServerError" + "ServerError (\(self.code))" } } extension Mongo.ServerError:MongoRetryableError @@ -31,6 +31,6 @@ extension Mongo.ServerError:MongoRetryableError public var isRetryable:Bool { - self.code?.indicatesRetryability ?? false + self.code.indicatesRetryability } } diff --git a/Sources/MongoDriver/Retryability/Mongo.TimeoutError.swift b/Sources/MongoDriver/Retryability/Mongo.TimeoutError.swift deleted file mode 100644 index 57e3039b..00000000 --- a/Sources/MongoDriver/Retryability/Mongo.TimeoutError.swift +++ /dev/null @@ -1,23 +0,0 @@ -extension Mongo -{ - /// A command timed out during execution. - public - enum TimeoutError:Error, Equatable, Sendable - { - /// A command was timed-out by the driver. If `written` is false, - /// then the command was never written to a channel in the first - /// place, because the deadline for the command had already passed. - /// - /// Driver-side timeouts are expensive, because the driver needs to - /// tear down and re-establish connections after enforcing them. - case driver(written:Bool) - /// A command was timed-out by the server, most likely according to - /// `maxTimeMS` (``MaxTime``). The payload indicates the exact type - /// of timeout reported by the server. - /// - /// Server-side timeouts are efficient, because the driver can reuse - /// the connection used to run the original command to run another - /// command. - case server(code:ServerError.Code) - } -} diff --git a/Sources/MongoDriver/Sessions/Mongo.Session.swift b/Sources/MongoDriver/Sessions/Mongo.Session.swift index 7179e23f..f77eb7cd 100644 --- a/Sources/MongoDriver/Sessions/Mongo.Session.swift +++ b/Sources/MongoDriver/Sessions/Mongo.Session.swift @@ -14,7 +14,7 @@ extension Mongo /// on a database, you want each task to checkout its own session from a /// ``SessionPool``, which is ``Sendable``. /// - /// Create a session by calling ``init(from:)`` with a session pool. + /// Create a session by calling ``init(from:forking:by:)`` with a session pool. /// /// ```swift /// bootstrap.withSessionPool @@ -116,7 +116,7 @@ extension Mongo.Session fork: original) } /// Fast-forwards this session’s precondition time to the other session’s - /// precondition time, if it is non-[`nil`]() and greater than this + /// precondition time, if it is non-nil and greater than this /// session’s precondition time. The other session’s precondition time /// is unaffected. public @@ -142,7 +142,7 @@ extension Mongo.Session /// - preference: /// The read preference to use for server selection. /// - deadline: - /// A deadline used to enforce operation timeouts. If [`nil`](), + /// A deadline used to enforce operation timeouts. If nil, /// the default driver connection timeout will also be used as /// the timeout for the entire operation. @inlinable public @@ -198,7 +198,7 @@ extension Mongo.Session /// /// - Parameters: /// - consumer: - /// A closure that will be called with an iterable sequence of ``Batches``. + /// A closure that will be called with an iterable ``Mongo.Cursor``. /// if the closure returns while the cursor is still open, this method will /// run ``KillCursors`` and wait for it to either complete or error. /// @@ -502,7 +502,7 @@ extension Mongo.Session /// transaction is in progress, otherwise the transaction’s read level /// takes precedence. /// If the outer optional is inhabited, the precondition time will be - /// encoded even if the inner optional is [`nil`](). + /// encoded even if the inner optional is nil. /// /// If the transaction state was in the starting phase, it will transition to /// the started phase, and this transition will be sticky — it will not revert @@ -604,6 +604,6 @@ extension Mongo.Session /// Observed operation times will not necessarily be monotonic, if commands /// are being sent to different servers across the same session. Therefore, /// to enforce causal consistency, this method only updates the precondition - /// time if the operation time is non-[`nil`]() and greater than the current + /// time if the operation time is non-nil and greater than the current /// precondition time. } diff --git a/Sources/MongoDriver/Sessions/Mongo.SessionIdentifier.swift b/Sources/MongoDriver/Sessions/Mongo.SessionIdentifier.swift index 7bc72d64..5ed98586 100644 --- a/Sources/MongoDriver/Sessions/Mongo.SessionIdentifier.swift +++ b/Sources/MongoDriver/Sessions/Mongo.SessionIdentifier.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import BSON_UUID import UUID diff --git a/Sources/MongoDriver/Sessions/Mongo.SessionLabels.swift b/Sources/MongoDriver/Sessions/Mongo.SessionLabels.swift index c5530c9d..bfe9470a 100644 --- a/Sources/MongoDriver/Sessions/Mongo.SessionLabels.swift +++ b/Sources/MongoDriver/Sessions/Mongo.SessionLabels.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/Sessions/Mongo.SessionPool.swift b/Sources/MongoDriver/Sessions/Mongo.SessionPool.swift index ecd4a27e..104727b8 100644 --- a/Sources/MongoDriver/Sessions/Mongo.SessionPool.swift +++ b/Sources/MongoDriver/Sessions/Mongo.SessionPool.swift @@ -1,6 +1,6 @@ import Atomics -import Durations import DequeModule +import Durations extension Mongo { @@ -20,7 +20,7 @@ extension Mongo let deployment:Deployment /// The number of sessions currently being re-indexed by this pool. - /// Session deinitializers increment this counter, and the pool + /// Session deinitializers increment this counter, and the pool /// decrements it once the session has been re-indexed and made available /// for reuse. private nonisolated @@ -42,7 +42,7 @@ extension Mongo private var phase:Phase - init(deployment:Deployment) + init(deployment:Deployment) { self.deployment = deployment self.releasing = .create(0) @@ -133,7 +133,7 @@ extension Mongo.SessionPool by: deadlines.connection) // this creates the connection before creating the session, because // connections are limited but sessions are unlimited. so ordering it - // like this + // like this let connection:Mongo.Connection = try await .init(from: connections, by: deadlines.connection) let session:Mongo.Session = try await .init(from: self) @@ -180,18 +180,18 @@ extension Mongo.SessionPool /// Unblocks an awaiting request with the given session, if one exists. /// /// - Returns: - /// [`true`]() if there was a request that was unblocked by this call, - /// [`false`]() otherwise. + /// `true` if there was a request that was unblocked by this call, + /// `false` otherwise. private func yield(_ allocation:Allocation) -> Bool { if case ()? = self.requests.popFirst()?.resume(returning: allocation) { - return true + true } else { - return false + false } } } @@ -302,7 +302,7 @@ extension Mongo.SessionPool self.released.append(allocation) return } - + case .draining: self.retained.remove(allocation.id) self.released.append(allocation) diff --git a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Acknowledgement.swift b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Acknowledgement.swift index b51e1333..9b8be0c7 100644 --- a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Acknowledgement.swift +++ b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Acknowledgement.swift @@ -1,5 +1,3 @@ -import BSONDecoding -import BSONEncoding import BSON extension Mongo.WriteConcern @@ -19,7 +17,7 @@ extension Mongo.WriteConcern.Acknowledgement { if 0 < votes { - return .votes(votes) + .votes(votes) } else { @@ -30,13 +28,13 @@ extension Mongo.WriteConcern.Acknowledgement extension Mongo.WriteConcern.Acknowledgement:BSONEncodable { public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { switch self { case .mode(let mode): mode.encode(to: &field) - + case .votes(let votes): votes.encode(to: &field) } diff --git a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Options.swift b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Options.swift index 369d32b2..c267b2f1 100644 --- a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Options.swift +++ b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcern.Options.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.WriteConcern { diff --git a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.Details.swift b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.Details.swift index f8f95ded..6b497a97 100644 --- a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.Details.swift +++ b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.Details.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.WriteConcernError { diff --git a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.swift b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.swift index 849fd472..bccb3337 100644 --- a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.swift +++ b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernError.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernProvenance.swift b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernProvenance.swift index be7d4bbd..c141b582 100644 --- a/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernProvenance.swift +++ b/Sources/MongoDriver/WriteConcern/Mongo.WriteConcernProvenance.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoDriverTests/Main.swift b/Sources/MongoDriverTests/Main.swift index a5e9478c..3253b258 100644 --- a/Sources/MongoDriverTests/Main.swift +++ b/Sources/MongoDriverTests/Main.swift @@ -9,10 +9,10 @@ var mongodb:Mongo.URI.Base } @main -enum Main:AsyncTests +enum Main:TestMain, TestBattery { static - func run(tests:Tests) async + func run(tests:TestGroup) async { let executors:MultiThreadedEventLoopGroup = .init(numberOfThreads: 2) if let tests:TestGroup = tests / "replicated" diff --git a/Sources/MongoExecutor/MongoExecutor.swift b/Sources/MongoExecutor/MongoExecutor.swift index de09e531..b0fd0975 100644 --- a/Sources/MongoExecutor/MongoExecutor.swift +++ b/Sources/MongoExecutor/MongoExecutor.swift @@ -1,6 +1,6 @@ -import NIOCore import MongoIO import MongoWire +import NIOCore public protocol MongoExecutor @@ -9,25 +9,24 @@ protocol MongoExecutor } extension MongoExecutor { - /// @import(MongoIO) - /// Sends the given command document over this connection, unchanged, and - /// awaits its message-response. + /// Sends the given command document over this connection, unchanged, and awaits its + /// message-response. /// - /// If the task the caller of this function is running on gets cancelled, - /// this function will close ``channel`` and return failure. This function - /// does not check for cancellation before sending the request; it is the - /// responsibility of the caller to check for cancellation. + /// If the task the caller of this function is running on gets cancelled, this function will + /// close ``channel`` and return failure. This function does not check for cancellation + /// before sending the request; it is the responsibility of the caller to check for + /// cancellation. /// - /// If the deadline passes without a reply from the server, this function - /// will close ``channel`` and return failure. This function does not check - /// if the deadline has already passed before sending the request; it is - /// the responsibility of the caller to check if the deadline is sensible. + /// If the deadline passes without a reply from the server, this function will close + /// ``channel`` and throw a ``Mongo.WireTimeoutError``. This function does not check if the + /// deadline has already passed before sending the request; it is the responsibility of the + /// caller to check if the deadline is sensible. public - func request(sections:__owned MongoWire.Message<[UInt8]>.Sections, - deadline:ContinuousClock.Instant) - async -> Result, MongoIO.ChannelError> + func request( + sections:__owned Mongo.WireMessage<[UInt8]>.Sections, + deadline:ContinuousClock.Instant) async throws -> Mongo.WireMessage { - await Self.request(self.channel, sections: sections, deadline: deadline) + try await Self.request(self.channel, sections: sections, deadline: deadline) } /// Interrupts this channel, forcing it to close (asynchronously), but @@ -52,47 +51,39 @@ extension MongoExecutor func timeout(_ channel:any Channel, by deadline:ContinuousClock.Instant) async throws { try await Task.sleep(until: deadline, clock: .continuous) - channel.writeAndFlush(MongoIO.Action.cancel(throwing: .cancelled(.timeout)), + channel.writeAndFlush(Mongo.WireAction.cancel(throwing: Mongo.WireTimeoutError.init()), promise: nil) } - /// Sends the given command document over this connection, unchanged, and - /// awaits its message-response. + /// Sends the given command document over this connection, unchanged, and awaits its + /// message-response. /// - /// If the deadline passes without a reply from the server, the channel - /// will be closed. This will happen even if the deadline has already passed; - /// therefore it is the responsibility of the calling code to check if the - /// deadline is sensible. + /// If the deadline passes without a reply from the server, the channel will be closed. This + /// will happen even if the deadline has already passed; therefore it is the responsibility + /// of the calling code to check if the deadline is sensible. private static func request(_ channel:any Channel, - sections:__owned MongoWire.Message<[UInt8]>.Sections, - deadline:ContinuousClock.Instant) - async -> Result, MongoIO.ChannelError> + sections:__owned Mongo.WireMessage<[UInt8]>.Sections, + deadline:ContinuousClock.Instant) async throws -> Mongo.WireMessage { async let _:Void = Self.timeout(channel, by: deadline) - return await withTaskCancellationHandler + return try await withTaskCancellationHandler { - await withCheckedContinuation - { - (continuation:CheckedContinuation< - Result, MongoIO.ChannelError>, - Never>) in + let promise:EventLoopPromise> = + channel.eventLoop.makePromise() - channel.writeAndFlush(MongoIO.Action.request(sections, continuation)) - .whenComplete - { - // don’t leak the continuation! - if case .failure(let error) = $0 - { - continuation.resume(returning: .failure(.io(error, written: false))) - } - } + channel.writeAndFlush(Mongo.WireAction.request(sections, promise)).whenFailure + { + // don’t leak the promise! + promise.fail(Mongo.NetworkError.init(underlying: $0)) } + + return try await promise.futureResult.get() } onCancel: { - channel.writeAndFlush(MongoIO.Action.cancel(throwing: .cancelled(.cancel)), + channel.writeAndFlush(Mongo.WireAction.cancel(throwing: CancellationError.init()), promise: nil) } } @@ -102,7 +93,9 @@ extension MongoExecutor @usableFromInline internal static func crosscancel(_ channel:any Channel, throwing error:any Error) { - channel.writeAndFlush(MongoIO.Action.cancel(throwing: .crosscancelled(error)), + channel.writeAndFlush(Mongo.WireAction.cancel(throwing: Mongo.NetworkError.init( + underlying: error, + provenance: .crosscancellation)), promise: nil) } diff --git a/Sources/MongoIO/ByteBuffer (ext).swift b/Sources/MongoIO/ByteBuffer (ext).swift new file mode 100644 index 00000000..06c460b8 --- /dev/null +++ b/Sources/MongoIO/ByteBuffer (ext).swift @@ -0,0 +1,31 @@ +import BSON +import NIOCore + +extension ByteBuffer +{ + mutating + func readWithInput( + parser parse:(inout BSON.Input) throws -> T) rethrows -> T + { + var input:BSON.Input = .init(self.readableBytesView) + let parsed:T = try parse(&input) + self.moveReaderIndex(forwardBy: input.source.distance(from: input.source.startIndex, + to: input.index)) + return parsed + } + mutating + func readWithUnsafeInput( + parser parse:(inout BSON.Input) throws -> T) rethrows -> T + { + try self.readWithUnsafeReadableBytes + { + (buffer:UnsafeRawBufferPointer) throws -> (Int, T) in + + var input:BSON.Input = .init(buffer) + let parsed:T = try parse(&input) + let advanced:Int = input.source.distance(from: input.source.startIndex, + to: input.index) + return (advanced, parsed) + } + } +} diff --git a/Sources/MongoIO/Mongo.NetworkError.swift b/Sources/MongoIO/Mongo.NetworkError.swift new file mode 100644 index 00000000..6ba1a606 --- /dev/null +++ b/Sources/MongoIO/Mongo.NetworkError.swift @@ -0,0 +1,18 @@ +extension Mongo +{ + @frozen public + struct NetworkError:Error + { + public + let underlying:any Error + public + let provenance:NetworkErrorProvenance? + + @inlinable public + init(underlying:any Error, provenance:NetworkErrorProvenance? = nil) + { + self.underlying = underlying + self.provenance = provenance + } + } +} diff --git a/Sources/MongoDriver/Retryability/Mongo.NetworkErrorProvenance.swift b/Sources/MongoIO/Mongo.NetworkErrorProvenance.swift similarity index 100% rename from Sources/MongoDriver/Retryability/Mongo.NetworkErrorProvenance.swift rename to Sources/MongoIO/Mongo.NetworkErrorProvenance.swift diff --git a/Sources/MongoIO/Mongo.WireAction.swift b/Sources/MongoIO/Mongo.WireAction.swift new file mode 100644 index 00000000..a2f5fa81 --- /dev/null +++ b/Sources/MongoIO/Mongo.WireAction.swift @@ -0,0 +1,14 @@ +import MongoWire +import NIOCore + +extension Mongo +{ + public + enum WireAction + { + case request(Mongo.WireMessage<[UInt8]>.Sections, + EventLoopPromise>) + + case cancel(throwing:any Error) + } +} diff --git a/Sources/MongoIO/Mongo.WireMessageDecoder.swift b/Sources/MongoIO/Mongo.WireMessageDecoder.swift new file mode 100644 index 00000000..da28d77c --- /dev/null +++ b/Sources/MongoIO/Mongo.WireMessageDecoder.swift @@ -0,0 +1,69 @@ +import BSON +import MongoWire +import NIOCore + +extension Mongo +{ + public + struct WireMessageDecoder + { + public + typealias InboundOut = Mongo.WireMessage + + private + var header:Mongo.WireHeader? + + public + init() + { + self.header = nil + } + } +} +extension Mongo.WireMessageDecoder:ByteToMessageDecoder +{ + public mutating + func decode(context:ChannelHandlerContext, + buffer:inout ByteBuffer) throws -> DecodingState + { + let header:Mongo.WireHeader + if let seen:Mongo.WireHeader = self.header + { + header = seen + } + else if Mongo.WireHeader.size <= buffer.readableBytes + { + header = try buffer.readWithUnsafeInput + { + try $0.parse(as: Mongo.WireHeader.self) + } + } + else + { + return .needMoreData + } + + guard header.count <= buffer.readableBytes + else + { + self.header = header + return .needMoreData + } + + self.header = nil + let message:Mongo.WireMessage = try buffer.readWithInput + { + try $0.parse(as: Mongo.WireMessage.self, header: header) + } + context.fireChannelRead(self.wrapInboundOut(message)) + return .continue + } + + public mutating + func decodeLast(context:ChannelHandlerContext, + buffer:inout ByteBuffer, + seenEOF _:Bool) throws -> DecodingState + { + try self.decode(context: context, buffer: &buffer) + } +} diff --git a/Sources/MongoIO/Mongo.WireMessageRouter.State.swift b/Sources/MongoIO/Mongo.WireMessageRouter.State.swift new file mode 100644 index 00000000..016bc128 --- /dev/null +++ b/Sources/MongoIO/Mongo.WireMessageRouter.State.swift @@ -0,0 +1,11 @@ +import MongoWire +import NIOCore + +extension Mongo.WireMessageRouter +{ + enum State + { + case perished((any Error)?) + case awaiting(EventLoopPromise>?) + } +} diff --git a/Sources/MongoIO/MongoIO.MessageRouter.swift b/Sources/MongoIO/Mongo.WireMessageRouter.swift similarity index 61% rename from Sources/MongoIO/MongoIO.MessageRouter.swift rename to Sources/MongoIO/Mongo.WireMessageRouter.swift index e0a0b3ac..27853ca1 100644 --- a/Sources/MongoIO/MongoIO.MessageRouter.swift +++ b/Sources/MongoIO/Mongo.WireMessageRouter.swift @@ -1,15 +1,14 @@ import BSON -import BSON import MongoWire import NIOCore -extension MongoIO +extension Mongo { public final - class MessageRouter + class WireMessageRouter { private - var request:MongoWire.MessageIdentifier + var request:Mongo.WireMessageIdentifier private var state:State @@ -25,55 +24,61 @@ extension MongoIO { if case .awaiting(_?) = self.state { - fatalError("unreachable (deinitialized channel handler while a continuation is still awaiting") + fatalError(""" + unreachable (deinitialized channel handler while a continuation is still \ + awaiting) + """) } } } } -extension MongoIO.MessageRouter +extension Mongo.WireMessageRouter { - func perish(throwing error:MongoIO.ChannelError) + func perish(throwing error:any Error) { switch self.state { - case .awaiting(let continuation): - continuation?.resume(returning: .failure(error)) - self.state = .perished - + case .awaiting(let caller?): + caller.fail(error) + self.state = .perished(nil) + + case .awaiting(nil): + self.state = .perished(error) + case .perished: break } } } -extension MongoIO.MessageRouter:ChannelInboundHandler +extension Mongo.WireMessageRouter:ChannelInboundHandler { public - typealias InboundIn = MongoWire.Message + typealias InboundIn = Mongo.WireMessage public typealias InboundOut = Never public func channelRead(context:ChannelHandlerContext, data:NIOAny) { - let message:MongoWire.Message = self.unwrapInboundIn(data) + let message:Mongo.WireMessage = self.unwrapInboundIn(data) switch self.state { - case .awaiting(let continuation?): + case .awaiting(let caller?): guard self.request == message.header.request else { fallthrough } - continuation.resume(returning: .success(message)) + caller.succeed(message) self.state = .awaiting(nil) - + case .awaiting(nil): - self.state = .perished - context.fireErrorCaught(MongoIO.MessageRoutingError.init( + self.state = .perished(nil) + context.fireErrorCaught(Mongo.WireMessageRoutingError.init( unknown: message.header.request)) - + case .perished: break } @@ -81,22 +86,23 @@ extension MongoIO.MessageRouter:ChannelInboundHandler public func channelInactive(context:ChannelHandlerContext) { - self.perish(throwing: .io(ChannelError.outputClosed, written: true)) + self.perish(throwing: Mongo.NetworkError.init( + underlying: Mongo.WireProtocolError.init())) context.fireChannelInactive() } public func errorCaught(context:ChannelHandlerContext, error:any Error) { - self.perish(throwing: .io(error, written: true)) - + self.perish(throwing: Mongo.NetworkError.init(underlying: error)) + context.fireErrorCaught(error) } } -extension MongoIO.MessageRouter:ChannelOutboundHandler +extension Mongo.WireMessageRouter:ChannelOutboundHandler { public - typealias OutboundIn = MongoIO.Action + typealias OutboundIn = Mongo.WireAction public typealias OutboundOut = ByteBuffer @@ -109,32 +115,31 @@ extension MongoIO.MessageRouter:ChannelOutboundHandler self.perish(throwing: error) context.channel.close(mode: .all, promise: nil) - - case .request(let command, let continuation): - let request:MongoWire.MessageIdentifier = self.request.next() - let message:MongoWire.Message<[UInt8]> = .init( + + case .request(let command, let caller): + let request:Mongo.WireMessageIdentifier = self.request.next() + let message:Mongo.WireMessage<[UInt8]> = .init( sections: command, checksum: false, id: request) - + switch self.state { case .perished: - continuation.resume(returning: .failure(.io(ChannelError.alreadyClosed, - written: true))) + caller.fail(Mongo.NetworkError.init(underlying: Mongo.WireProtocolError.init())) return - + case .awaiting(_?): fatalError("submitted a command to a channel that is already running a command") - + case .awaiting(nil): - self.state = .awaiting(continuation) + self.state = .awaiting(caller) } var output:BSON.Output = .init( preallocated: .init(context.channel.allocator.buffer( capacity: .init(message.header.size)))) - + output.serialize(message: message) context.writeAndFlush(self.wrapOutboundOut(ByteBuffer.init(output.destination)), promise: promise) diff --git a/Sources/MongoIO/Mongo.WireMessageRoutingError.swift b/Sources/MongoIO/Mongo.WireMessageRoutingError.swift new file mode 100644 index 00000000..b38cd9f8 --- /dev/null +++ b/Sources/MongoIO/Mongo.WireMessageRoutingError.swift @@ -0,0 +1,23 @@ +import MongoWire + +extension Mongo +{ + public + struct WireMessageRoutingError:Error + { + let request:Mongo.WireMessageIdentifier + + init(unknown request:Mongo.WireMessageIdentifier) + { + self.request = request + } + } +} +extension Mongo.WireMessageRoutingError:CustomStringConvertible +{ + public + var description:String + { + "Received response to unknown request (id: \(self.request))." + } +} diff --git a/Sources/MongoIO/Mongo.WireProtocolError.swift b/Sources/MongoIO/Mongo.WireProtocolError.swift new file mode 100644 index 00000000..1a062d45 --- /dev/null +++ b/Sources/MongoIO/Mongo.WireProtocolError.swift @@ -0,0 +1,11 @@ +extension Mongo +{ + @frozen public + struct WireProtocolError:Equatable, Error + { + @inlinable public + init() + { + } + } +} diff --git a/Sources/MongoIO/Mongo.WireTimeoutError.swift b/Sources/MongoIO/Mongo.WireTimeoutError.swift new file mode 100644 index 00000000..18517811 --- /dev/null +++ b/Sources/MongoIO/Mongo.WireTimeoutError.swift @@ -0,0 +1,15 @@ +extension Mongo +{ + /// An operation timed out while waiting for a wire response from the server. + /// + /// Wire timeouts are expensive, because the driver needs to tear down and re-establish + /// connections after enforcing them. + @frozen public + struct WireTimeoutError:Error, Equatable + { + @inlinable public + init() + { + } + } +} diff --git a/Sources/MongoIO/MongoIO.Action.swift b/Sources/MongoIO/MongoIO.Action.swift deleted file mode 100644 index 10840233..00000000 --- a/Sources/MongoIO/MongoIO.Action.swift +++ /dev/null @@ -1,14 +0,0 @@ -import MongoWire -import NIOCore - -extension MongoIO -{ - public - enum Action - { - case request(MongoWire.Message<[UInt8]>.Sections, CheckedContinuation< - Result, ChannelError>, - Never>) - case cancel(throwing:MongoIO.ChannelError) - } -} diff --git a/Sources/MongoIO/MongoIO.Cancellation.swift b/Sources/MongoIO/MongoIO.Cancellation.swift deleted file mode 100644 index 98e44d96..00000000 --- a/Sources/MongoIO/MongoIO.Cancellation.swift +++ /dev/null @@ -1,9 +0,0 @@ -extension MongoIO -{ - @frozen public - enum Cancellation:Equatable, Sendable - { - case timeout - case cancel - } -} diff --git a/Sources/MongoIO/MongoIO.ChannelError.swift b/Sources/MongoIO/MongoIO.ChannelError.swift deleted file mode 100644 index f2603f83..00000000 --- a/Sources/MongoIO/MongoIO.ChannelError.swift +++ /dev/null @@ -1,18 +0,0 @@ -extension MongoIO -{ - @frozen public - enum ChannelError:Error - { - /// The channel experienced some sort of IO error. The caller - /// should generally retry the request if desired. - case io(any Error, written:Bool) - /// The channel was closed because the task awaiting the relevant - /// request was cancelled, either due to task cancellation, or - /// network timeout. - case cancelled(Cancellation) - /// The channel was closed because a task besides the one awaiting - /// the relevant request cancelled the request, due to some - /// condition external to the caller. - case crosscancelled(any Error) - } -} diff --git a/Sources/MongoIO/MongoIO.MessageDecoder.swift b/Sources/MongoIO/MongoIO.MessageDecoder.swift deleted file mode 100644 index 37161fff..00000000 --- a/Sources/MongoIO/MongoIO.MessageDecoder.swift +++ /dev/null @@ -1,98 +0,0 @@ -import BSON -import MongoWire -import NIOCore - -extension ByteBuffer -{ - mutating - func readWithInput( - parser parse:(inout BSON.Input) throws -> T) rethrows -> T - { - var input:BSON.Input = .init(self.readableBytesView) - let parsed:T = try parse(&input) - self.moveReaderIndex(forwardBy: input.source.distance(from: input.source.startIndex, - to: input.index)) - return parsed - } - mutating - func readWithUnsafeInput( - parser parse:(inout BSON.Input) throws -> T) rethrows -> T - { - try self.readWithUnsafeReadableBytes - { - (buffer:UnsafeRawBufferPointer) throws -> (Int, T) in - - var input:BSON.Input = .init(buffer) - let parsed:T = try parse(&input) - let advanced:Int = input.source.distance(from: input.source.startIndex, - to: input.index) - return (advanced, parsed) - } - } -} - -extension MongoIO -{ - public - struct MessageDecoder - { - public - typealias InboundOut = MongoWire.Message - - private - var header:MongoWire.Header? - - public - init() - { - self.header = nil - } - } -} -extension MongoIO.MessageDecoder:ByteToMessageDecoder -{ - public mutating - func decode(context:ChannelHandlerContext, - buffer:inout ByteBuffer) throws -> DecodingState - { - let header:MongoWire.Header - if let seen:MongoWire.Header = self.header - { - header = seen - } - else if MongoWire.Header.size <= buffer.readableBytes - { - header = try buffer.readWithUnsafeInput - { - try $0.parse(as: MongoWire.Header.self) - } - } - else - { - return .needMoreData - } - - guard header.count <= buffer.readableBytes - else - { - self.header = header - return .needMoreData - } - - self.header = nil - let message:MongoWire.Message = try buffer.readWithInput - { - try $0.parse(as: MongoWire.Message.self, header: header) - } - context.fireChannelRead(self.wrapInboundOut(message)) - return .continue - } - - public mutating - func decodeLast(context:ChannelHandlerContext, - buffer:inout ByteBuffer, - seenEOF _:Bool) throws -> DecodingState - { - return try decode(context: context, buffer: &buffer) - } -} diff --git a/Sources/MongoIO/MongoIO.MessageRouter.State.swift b/Sources/MongoIO/MongoIO.MessageRouter.State.swift deleted file mode 100644 index c89f7efa..00000000 --- a/Sources/MongoIO/MongoIO.MessageRouter.State.swift +++ /dev/null @@ -1,13 +0,0 @@ -import MongoWire -import NIOCore - -extension MongoIO.MessageRouter -{ - enum State - { - case perished - case awaiting(CheckedContinuation< - Result, MongoIO.ChannelError>, - Never>?) - } -} diff --git a/Sources/MongoIO/MongoIO.MessageRoutingError.swift b/Sources/MongoIO/MongoIO.MessageRoutingError.swift deleted file mode 100644 index 7c672a3c..00000000 --- a/Sources/MongoIO/MongoIO.MessageRoutingError.swift +++ /dev/null @@ -1,23 +0,0 @@ -import MongoWire - -extension MongoIO -{ - public - struct MessageRoutingError:Error - { - let request:MongoWire.MessageIdentifier - - init(unknown request:MongoWire.MessageIdentifier) - { - self.request = request - } - } -} -extension MongoIO.MessageRoutingError:CustomStringConvertible -{ - public - var description:String - { - "Received response to unknown request (id: \(self.request))." - } -} diff --git a/Sources/MongoIO/MongoIO.swift b/Sources/MongoIO/MongoIO.swift deleted file mode 100644 index acb5a5db..00000000 --- a/Sources/MongoIO/MongoIO.swift +++ /dev/null @@ -1,4 +0,0 @@ -public -enum MongoIO -{ -} diff --git a/Sources/MongoIO/exports.swift b/Sources/MongoIO/exports.swift new file mode 100644 index 00000000..a7e14112 --- /dev/null +++ b/Sources/MongoIO/exports.swift @@ -0,0 +1 @@ +@_exported import enum Mongo.Mongo diff --git a/Sources/MongoLibrary/Collation/Mongo.Collation.Alternate.swift b/Sources/MongoLibrary/Collation/Mongo.Collation.Alternate.swift index 58377484..8a4d01de 100644 --- a/Sources/MongoLibrary/Collation/Mongo.Collation.Alternate.swift +++ b/Sources/MongoLibrary/Collation/Mongo.Collation.Alternate.swift @@ -1,9 +1,8 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.Collation { - @frozen public + @frozen public enum Alternate:String, Sendable { /// Whitespace and punctuation are considered base characters. diff --git a/Sources/MongoLibrary/Collation/Mongo.Collation.CaseFirst.swift b/Sources/MongoLibrary/Collation/Mongo.Collation.CaseFirst.swift index bb52c7ba..fe46a1b0 100644 --- a/Sources/MongoLibrary/Collation/Mongo.Collation.CaseFirst.swift +++ b/Sources/MongoLibrary/Collation/Mongo.Collation.CaseFirst.swift @@ -1,10 +1,9 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.Collation { - @frozen public - enum CaseFirst:String, Sendable + @frozen public + enum CaseFirst:String, Sendable { case lower case upper diff --git a/Sources/MongoLibrary/Collation/Mongo.Collation.MaxVariable.swift b/Sources/MongoLibrary/Collation/Mongo.Collation.MaxVariable.swift index 95f460df..5ac7c3cd 100644 --- a/Sources/MongoLibrary/Collation/Mongo.Collation.MaxVariable.swift +++ b/Sources/MongoLibrary/Collation/Mongo.Collation.MaxVariable.swift @@ -1,10 +1,9 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.Collation { - @frozen public - enum MaxVariable:String, Sendable + @frozen public + enum MaxVariable:String, Sendable { /// Both whitespace and punctuation are ignorable and not considered /// base characters. diff --git a/Sources/MongoLibrary/Collation/Mongo.Collation.Strength.swift b/Sources/MongoLibrary/Collation/Mongo.Collation.Strength.swift index 70285d1f..3d5cd674 100644 --- a/Sources/MongoLibrary/Collation/Mongo.Collation.Strength.swift +++ b/Sources/MongoLibrary/Collation/Mongo.Collation.Strength.swift @@ -1,33 +1,32 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.Collation { - @frozen public - enum Strength:Int32, Sendable + @frozen public + enum Strength:Int32, Sendable { - /// Primary level of comparison. Collation performs comparisons of the base + /// Primary level of comparison. Collation performs comparisons of the base /// characters only, ignoring other differences such as diacritics and case. case primary = 1 - /// Secondary level of comparison. Collation performs comparisons up to - /// secondary differences, such as diacritics. That is, collation performs - /// comparisons of base characters (primary differences) and diacritics - /// (secondary differences). Differences between base characters takes + /// Secondary level of comparison. Collation performs comparisons up to + /// secondary differences, such as diacritics. That is, collation performs + /// comparisons of base characters (primary differences) and diacritics + /// (secondary differences). Differences between base characters takes /// precedence over secondary differences. case secondary = 2 - /// Tertiary level of comparison. Collation performs comparisons up to - /// tertiary differences, such as case and letter variants. That is, - /// collation performs comparisons of base characters (primary differences), - /// diacritics (secondary differences), and case and variants (tertiary differences). - /// Differences between base characters takes precedence over secondary differences, + /// Tertiary level of comparison. Collation performs comparisons up to + /// tertiary differences, such as case and letter variants. That is, + /// collation performs comparisons of base characters (primary differences), + /// diacritics (secondary differences), and case and variants (tertiary differences). + /// Differences between base characters takes precedence over secondary differences, /// which takes precedence over tertiary differences. /// /// This is the default level. case tertiary = 3 - /// Quaternary Level. Limited for specific use case to consider punctuation + /// Quaternary Level. Limited for specific use case to consider punctuation /// when levels 1-3 ignore punctuation or for processing Japanese text. case quaternary = 4 diff --git a/Sources/MongoLibrary/Collation/Mongo.Collation.swift b/Sources/MongoLibrary/Collation/Mongo.Collation.swift index f091d1e8..5e7a0694 100644 --- a/Sources/MongoLibrary/Collation/Mongo.Collation.swift +++ b/Sources/MongoLibrary/Collation/Mongo.Collation.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { @@ -18,8 +17,8 @@ extension Mongo public let caseLevel:Bool? /// Determines up to which characters are considered ignorable when - /// ``alternate`` is ``Alternate/.shifted``. - /// Has no effect ``Alternate/.nonignorable``. + /// ``alternate`` is ``Alternate/shifted``. Has no effect when using + /// ``Alternate/nonignorable``. /// /// This is modeled as a separate property from ``alternate`` because /// it depends the value of ``alternate``, rather than its presence. diff --git a/Sources/MongoLibrary/Collections/Mongo.CollectionType.swift b/Sources/MongoLibrary/Collections/Mongo.CollectionType.swift index a64a8778..7e612de1 100644 --- a/Sources/MongoLibrary/Collections/Mongo.CollectionType.swift +++ b/Sources/MongoLibrary/Collections/Mongo.CollectionType.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Collections/Mongo.ValidationAction.swift b/Sources/MongoLibrary/Collections/Mongo.ValidationAction.swift index 3b9a0448..cbfd29a5 100644 --- a/Sources/MongoLibrary/Collections/Mongo.ValidationAction.swift +++ b/Sources/MongoLibrary/Collections/Mongo.ValidationAction.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Collections/Mongo.ValidationLevel.swift b/Sources/MongoLibrary/Collections/Mongo.ValidationLevel.swift index 8e519604..8f0434f4 100644 --- a/Sources/MongoLibrary/Collections/Mongo.ValidationLevel.swift +++ b/Sources/MongoLibrary/Collections/Mongo.ValidationLevel.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Commands/Aggregate/Mongo.Aggregate.swift b/Sources/MongoLibrary/Commands/Aggregate/Mongo.Aggregate.swift index b6743d22..9f825e2a 100644 --- a/Sources/MongoLibrary/Commands/Aggregate/Mongo.Aggregate.swift +++ b/Sources/MongoLibrary/Commands/Aggregate/Mongo.Aggregate.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore diff --git a/Sources/MongoLibrary/Commands/Create/Mongo.Create.swift b/Sources/MongoLibrary/Commands/Create/Mongo.Create.swift index 11836018..2299763c 100644 --- a/Sources/MongoLibrary/Commands/Create/Mongo.Create.swift +++ b/Sources/MongoLibrary/Commands/Create/Mongo.Create.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.Granularity.swift b/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.Granularity.swift index d387022e..6485f67a 100644 --- a/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.Granularity.swift +++ b/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.Granularity.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo.Timeseries { diff --git a/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.swift b/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.swift index 9c26910b..41247d37 100644 --- a/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.swift +++ b/Sources/MongoLibrary/Commands/Create/Mongo.Timeseries.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexStatement.swift b/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexStatement.swift index dac90edf..922b4c26 100644 --- a/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexStatement.swift +++ b/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexStatement.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoQL extension Mongo diff --git a/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexes.swift b/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexes.swift index 3950221a..6960a0ef 100644 --- a/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexes.swift +++ b/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexes.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexesResponse.swift b/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexesResponse.swift index 23c2fcbb..f6869245 100644 --- a/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexesResponse.swift +++ b/Sources/MongoLibrary/Commands/CreateIndexes/Mongo.CreateIndexesResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver extension Mongo diff --git a/Sources/MongoLibrary/Commands/Delete/Mongo.Delete.swift b/Sources/MongoLibrary/Commands/Delete/Mongo.Delete.swift index a04825bf..fd363fc6 100644 --- a/Sources/MongoLibrary/Commands/Delete/Mongo.Delete.swift +++ b/Sources/MongoLibrary/Commands/Delete/Mongo.Delete.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore @@ -11,15 +10,16 @@ extension Mongo { public let writeConcern:WriteConcern? - public - let deletes:Mongo.OutlineDocuments + + @usableFromInline internal + var deletes:BSON.Output<[UInt8]> public var fields:BSON.Document @usableFromInline internal init(writeConcern:WriteConcern?, - deletes:Mongo.OutlineDocuments, + deletes:BSON.Output<[UInt8]>, fields:BSON.Document) { self.writeConcern = writeConcern @@ -43,7 +43,7 @@ extension Mongo.Delete:MongoImplicitSessionCommand, MongoTransactableCommand, Mo @inlinable public var outline:Mongo.OutlineVector? { - .init(self.deletes, type: .deletes) + .init(bson: self.deletes, type: .deletes) } } extension Mongo.Delete @@ -51,22 +51,24 @@ extension Mongo.Delete @inlinable public init(_ collection:Mongo.Collection, writeConcern:Mongo.WriteConcern? = nil, - deletes statements:some Sequence>) + deletes encode:(inout Mongo.DeleteEncoder) throws -> ()) rethrows { + var deletes:Mongo.DeleteEncoder = .init() + try encode(&deletes) + self.init(writeConcern: writeConcern, - deletes: .init(statements), + deletes: deletes.move(), fields: Self.type(collection)) } + @inlinable public init(_ collection:Mongo.Collection, writeConcern:Mongo.WriteConcern? = nil, - deletes statements:some Sequence>, - with populate:(inout Self) throws -> ()) rethrows + with configure:(inout Self) throws -> (), + deletes encode:(inout Mongo.DeleteEncoder) throws -> ()) rethrows { - self.init(collection, - writeConcern: writeConcern, - deletes: statements) - try populate(&self) + try self.init(collection, writeConcern: writeConcern, deletes: encode) + try configure(&self) } } extension Mongo.Delete diff --git a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteEncoder.swift b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteEncoder.swift new file mode 100644 index 00000000..6534f0fb --- /dev/null +++ b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteEncoder.swift @@ -0,0 +1,33 @@ +import BSON + +extension Mongo +{ + @frozen public + struct DeleteEncoder where Effect:MongoWriteEffect + { + @usableFromInline internal + var output:BSON.Output<[UInt8]> + + @inlinable internal + init() + { + self.output = .init(preallocated: []) + } + } +} +extension Mongo.DeleteEncoder +{ + @inlinable internal consuming + func move() -> BSON.Output<[UInt8]> { self.output } +} +extension Mongo.DeleteEncoder +{ + @inlinable public mutating + func callAsFunction( + with yield:(inout Mongo.DeleteStatement) throws -> T) rethrows -> T + { + try yield(&self.output[ + as: Mongo.DeleteStatement.self, + in: BSON.DocumentFrame.self]) + } +} diff --git a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteLimit.swift b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteLimit.swift index 43d9523c..f8d2919c 100644 --- a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteLimit.swift +++ b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteLimit.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteOne.swift b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteOne.swift index f69ff8c2..c14a2c67 100644 --- a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteOne.swift +++ b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteOne.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteResponse.swift b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteResponse.swift index 91995027..fb47fb06 100644 --- a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteResponse.swift +++ b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver extension Mongo diff --git a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteStatement.swift b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteStatement.swift index aa46b4c3..799214d8 100644 --- a/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteStatement.swift +++ b/Sources/MongoLibrary/Commands/Delete/Mongo.DeleteStatement.swift @@ -1,34 +1,36 @@ -import BSONEncoding +import BSON import MongoQL extension Mongo { @frozen public - struct DeleteStatement:MongoDocumentDSL, Sendable where Effect:MongoWriteEffect + struct DeleteStatement where Effect:MongoWriteEffect { - public - var bson:BSON.Document + @usableFromInline internal + var bson:BSON.DocumentEncoder @inlinable public - init(_ bson:BSON.Document) + init(_ output:consuming BSON.Output<[UInt8]>) { - self.bson = bson + self.bson = .init(output) } } } +extension Mongo.DeleteStatement:BSON.Encoder +{ + @inlinable public consuming + func move() -> BSON.Output<[UInt8]> { self.bson.move() } + + @inlinable public static + var type:BSON.AnyType { .document } +} extension Mongo.DeleteStatement { @inlinable public subscript(key:Limit) -> Effect.DeletePlurality? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } } extension Mongo.DeleteStatement @@ -36,51 +38,28 @@ extension Mongo.DeleteStatement @inlinable public subscript(key:Q) -> Mongo.PredicateDocument? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:Collation) -> Mongo.Collation? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:Hint) -> String? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } + @inlinable public subscript(key:Hint) -> Mongo.SortDocument? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } } diff --git a/Sources/MongoLibrary/Commands/DropDatabase/Mongo.DropDatabase.swift b/Sources/MongoLibrary/Commands/DropDatabase/Mongo.DropDatabase.swift index 4a446265..b31f3b52 100644 --- a/Sources/MongoLibrary/Commands/DropDatabase/Mongo.DropDatabase.swift +++ b/Sources/MongoLibrary/Commands/DropDatabase/Mongo.DropDatabase.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/DropIndexes/Mongo.DropIndexes.swift b/Sources/MongoLibrary/Commands/DropIndexes/Mongo.DropIndexes.swift index b76a86a2..bf94f5e1 100644 --- a/Sources/MongoLibrary/Commands/DropIndexes/Mongo.DropIndexes.swift +++ b/Sources/MongoLibrary/Commands/DropIndexes/Mongo.DropIndexes.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/Explain/Mongo.Explain.swift b/Sources/MongoLibrary/Commands/Explain/Mongo.Explain.swift index 26b9254d..08d474a0 100644 --- a/Sources/MongoLibrary/Commands/Explain/Mongo.Explain.swift +++ b/Sources/MongoLibrary/Commands/Explain/Mongo.Explain.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver import NIOCore diff --git a/Sources/MongoLibrary/Commands/Explain/Mongo.ExplainMode.swift b/Sources/MongoLibrary/Commands/Explain/Mongo.ExplainMode.swift index 4195914c..827f8466 100644 --- a/Sources/MongoLibrary/Commands/Explain/Mongo.ExplainMode.swift +++ b/Sources/MongoLibrary/Commands/Explain/Mongo.ExplainMode.swift @@ -1,5 +1,4 @@ -import BSONEncoding -import BSONDecoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Commands/Find/Mongo.Find.swift b/Sources/MongoLibrary/Commands/Find/Mongo.Find.swift index 04207220..572addff 100644 --- a/Sources/MongoLibrary/Commands/Find/Mongo.Find.swift +++ b/Sources/MongoLibrary/Commands/Find/Mongo.Find.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore diff --git a/Sources/MongoLibrary/Commands/FindAndModify/Mongo.FindAndModify.swift b/Sources/MongoLibrary/Commands/FindAndModify/Mongo.FindAndModify.swift index ddc6a3df..1b5a0c4d 100644 --- a/Sources/MongoLibrary/Commands/FindAndModify/Mongo.FindAndModify.swift +++ b/Sources/MongoLibrary/Commands/FindAndModify/Mongo.FindAndModify.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore diff --git a/Sources/MongoLibrary/Commands/Fsync/Mongo.Fsync.swift b/Sources/MongoLibrary/Commands/Fsync/Mongo.Fsync.swift index 694cc0f6..287ceaa7 100644 --- a/Sources/MongoLibrary/Commands/Fsync/Mongo.Fsync.swift +++ b/Sources/MongoLibrary/Commands/Fsync/Mongo.Fsync.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/Fsync/Mongo.FsyncLock.swift b/Sources/MongoLibrary/Commands/Fsync/Mongo.FsyncLock.swift index bb8e9351..6de4245f 100644 --- a/Sources/MongoLibrary/Commands/Fsync/Mongo.FsyncLock.swift +++ b/Sources/MongoLibrary/Commands/Fsync/Mongo.FsyncLock.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Commands/FsyncUnlock/Mongo.FsyncUnlock.swift b/Sources/MongoLibrary/Commands/FsyncUnlock/Mongo.FsyncUnlock.swift index 5553482c..dde43d6b 100644 --- a/Sources/MongoLibrary/Commands/FsyncUnlock/Mongo.FsyncUnlock.swift +++ b/Sources/MongoLibrary/Commands/FsyncUnlock/Mongo.FsyncUnlock.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/Insert/Mongo.Insert.swift b/Sources/MongoLibrary/Commands/Insert/Mongo.Insert.swift index 3029be5b..5b0872ad 100644 --- a/Sources/MongoLibrary/Commands/Insert/Mongo.Insert.swift +++ b/Sources/MongoLibrary/Commands/Insert/Mongo.Insert.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL @@ -13,15 +13,16 @@ extension Mongo { public let writeConcern:WriteConcern? - public - let documents:Mongo.OutlineDocuments + + @usableFromInline internal + var documents:BSON.Output<[UInt8]> public var fields:BSON.Document @usableFromInline internal init(writeConcern:WriteConcern?, - documents:Mongo.OutlineDocuments, + documents:BSON.Output<[UInt8]>, fields:BSON.Document) { self.writeConcern = writeConcern @@ -45,30 +46,52 @@ extension Mongo.Insert:MongoImplicitSessionCommand, MongoTransactableCommand, Mo @inlinable public var outline:Mongo.OutlineVector? { - .init(self.documents, type: .documents) + .init(bson: self.documents, type: .documents) } } extension Mongo.Insert { + @inlinable public + init(_ collection:Mongo.Collection, + writeConcern:Mongo.WriteConcern? = nil, + documents encode:(inout Mongo.InsertEncoder) throws -> ()) rethrows + { + var documents:Mongo.InsertEncoder = .init() + try encode(&documents) + + self.init(writeConcern: writeConcern, + documents: documents.move(), + fields: Self.type(collection)) + } + + @inlinable public + init(_ collection:Mongo.Collection, + writeConcern:Mongo.WriteConcern? = nil, + with configure:(inout Self) throws -> (), + documents encode:(inout Mongo.InsertEncoder) throws -> ()) rethrows + { + try self.init(collection, writeConcern: writeConcern, documents: encode) + try configure(&self) + } + @inlinable public init(_ collection:Mongo.Collection, writeConcern:Mongo.WriteConcern? = nil, encoding elements:Elements) where Elements:Sequence, Elements.Element:BSONDocumentEncodable { - self.init(writeConcern: writeConcern, - documents: .init(elements), - fields: Self.type(collection)) + self.init(collection, writeConcern: writeConcern) { $0 += elements } } + @inlinable public init(_ collection:Mongo.Collection, writeConcern:Mongo.WriteConcern? = nil, encoding elements:Elements, - with populate:(inout Self) throws -> ()) rethrows + with configure:(inout Self) throws -> ()) rethrows where Elements:Sequence, Elements.Element:BSONDocumentEncodable { self.init(collection, writeConcern: writeConcern, encoding: elements) - try populate(&self) + try configure(&self) } } extension Mongo.Insert diff --git a/Sources/MongoLibrary/Commands/Insert/Mongo.InsertEncoder.swift b/Sources/MongoLibrary/Commands/Insert/Mongo.InsertEncoder.swift new file mode 100644 index 00000000..3bd83a60 --- /dev/null +++ b/Sources/MongoLibrary/Commands/Insert/Mongo.InsertEncoder.swift @@ -0,0 +1,54 @@ +import BSON + +extension Mongo +{ + @frozen public + struct InsertEncoder + { + @usableFromInline internal + var output:BSON.Output<[UInt8]> + + @inlinable internal + init() + { + self.output = .init(preallocated: []) + } + } +} +extension Mongo.InsertEncoder +{ + @inlinable internal consuming + func move() -> BSON.Output<[UInt8]> { self.output } +} +extension Mongo.InsertEncoder +{ + @inlinable public static + func += (self:inout Self, elements:some Sequence) + { + for element:some BSONDocumentEncodable in elements + { + self.append(element) + } + } + + @inlinable public + subscript(_:CodingKey.Type, + yield:(inout BSON.DocumentEncoder) -> ()) -> Void + { + mutating + get + { + yield(&self.output[ + as: BSON.DocumentEncoder.self, + in: BSON.DocumentFrame.self]) + } + } + + @inlinable public mutating + func append(_ element:some BSONDocumentEncodable) + { + element.encode(to: &self.output[ + as: BSON.DocumentEncoder.self, + in: BSON.DocumentFrame.self]) + } +} diff --git a/Sources/MongoLibrary/Commands/Insert/Mongo.InsertResponse.swift b/Sources/MongoLibrary/Commands/Insert/Mongo.InsertResponse.swift index e817e87d..bdb4feb9 100644 --- a/Sources/MongoLibrary/Commands/Insert/Mongo.InsertResponse.swift +++ b/Sources/MongoLibrary/Commands/Insert/Mongo.InsertResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver extension Mongo diff --git a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionBinding.swift b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionBinding.swift index 9e1fa573..ab24c367 100644 --- a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionBinding.swift +++ b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionBinding.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoQL extension Mongo diff --git a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.Info.swift b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.Info.swift index bb7401da..1260384b 100644 --- a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.Info.swift +++ b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.Info.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import BSON_UUID import UUID diff --git a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.swift b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.swift index 876c2e32..36d663e1 100644 --- a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.swift +++ b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionMetadata.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoQL extension Mongo diff --git a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.Variant.swift b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.Variant.swift index da88e69b..4c5a070c 100644 --- a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.Variant.swift +++ b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.Variant.swift @@ -22,9 +22,9 @@ extension Mongo.CollectionOptions.Variant { switch self { - case .collection: return .collection - case .timeseries: return .timeseries - case .view: return .view + case .collection: .collection + case .timeseries: .timeseries + case .view: .view } } @inlinable public @@ -33,9 +33,9 @@ extension Mongo.CollectionOptions.Variant switch self { case .collection(cap: let cap?, validationAction: _, validationLevel: _, validator: _): - return cap + cap default: - return nil + nil } } @inlinable public @@ -44,9 +44,9 @@ extension Mongo.CollectionOptions.Variant switch self { case .timeseries(let timeseries): - return timeseries + timeseries default: - return nil + nil } } @inlinable public @@ -55,9 +55,9 @@ extension Mongo.CollectionOptions.Variant switch self { case .view(let view): - return view + view default: - return nil + nil } } } diff --git a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.swift b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.swift index 981dca8e..46509491 100644 --- a/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.swift +++ b/Sources/MongoLibrary/Commands/ListCollections/Mongo.CollectionOptions.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver import MongoQL @@ -42,11 +42,11 @@ extension Mongo.CollectionOptions { if case _? = self.variant.cap { - return true + true } else { - return false + false } } @inlinable public @@ -65,9 +65,9 @@ extension Mongo.CollectionOptions switch self.variant { case .collection(cap: _, validationAction: let action, validationLevel: _, validator: _): - return action + action default: - return nil + nil } } @inlinable public @@ -76,9 +76,9 @@ extension Mongo.CollectionOptions switch self.variant { case .collection(cap: _, validationAction: _, validationLevel: let level, validator: _): - return level + level default: - return nil + nil } } @inlinable public diff --git a/Sources/MongoLibrary/Commands/ListCollections/Mongo.ListCollections.swift b/Sources/MongoLibrary/Commands/ListCollections/Mongo.ListCollections.swift index 4a88aa41..2a3858d1 100644 --- a/Sources/MongoLibrary/Commands/ListCollections/Mongo.ListCollections.swift +++ b/Sources/MongoLibrary/Commands/ListCollections/Mongo.ListCollections.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL @@ -18,10 +17,10 @@ extension Mongo /// projection. /// /// > See: - /// https://www.mongodb.com/docs/manual/reference/command/listCollections/ + /// https://www.mongodb.com/docs/manual/reference/command/listCollections/ /// /// > See: - /// https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.rst + /// https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.rst public struct ListCollections:Sendable where Element:BSONDocumentViewDecodable & Sendable { diff --git a/Sources/MongoLibrary/Commands/ListDatabases/Mongo.DatabaseMetadata.swift b/Sources/MongoLibrary/Commands/ListDatabases/Mongo.DatabaseMetadata.swift index 10987dca..79d0b968 100644 --- a/Sources/MongoLibrary/Commands/ListDatabases/Mongo.DatabaseMetadata.swift +++ b/Sources/MongoLibrary/Commands/ListDatabases/Mongo.DatabaseMetadata.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoQL extension Mongo diff --git a/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.NameOnly.swift b/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.NameOnly.swift index 79cb6162..3165ee5c 100644 --- a/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.NameOnly.swift +++ b/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.NameOnly.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore diff --git a/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.swift b/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.swift index 79a7687b..a7b54f6b 100644 --- a/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.swift +++ b/Sources/MongoLibrary/Commands/ListDatabases/Mongo.ListDatabases.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore @@ -10,7 +9,7 @@ extension Mongo /// This command must run against the `admin` database. /// /// This command never enables the `nameOnly` option. To enable it, use the - /// ``ListDatabaseNames`` command. + /// ``ListDatabases.NameOnly`` command. /// /// > See: https://www.mongodb.com/docs/manual/reference/command/listDatabases/ public diff --git a/Sources/MongoLibrary/Commands/Modify/Mongo.Modify.swift b/Sources/MongoLibrary/Commands/Modify/Mongo.Modify.swift index a480d5ba..7b1ddc27 100644 --- a/Sources/MongoLibrary/Commands/Modify/Mongo.Modify.swift +++ b/Sources/MongoLibrary/Commands/Modify/Mongo.Modify.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/RenameCollection/Mongo.RenameCollection.swift b/Sources/MongoLibrary/Commands/RenameCollection/Mongo.RenameCollection.swift index e710ba0c..7098dd38 100644 --- a/Sources/MongoLibrary/Commands/RenameCollection/Mongo.RenameCollection.swift +++ b/Sources/MongoLibrary/Commands/RenameCollection/Mongo.RenameCollection.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver import MongoQL diff --git a/Sources/MongoLibrary/Commands/Update/Mongo.Update.swift b/Sources/MongoLibrary/Commands/Update/Mongo.Update.swift index 6110fdbf..9b207b30 100644 --- a/Sources/MongoLibrary/Commands/Update/Mongo.Update.swift +++ b/Sources/MongoLibrary/Commands/Update/Mongo.Update.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON import MongoDriver import MongoQL import NIOCore @@ -14,15 +13,16 @@ extension Mongo { public let writeConcern:WriteConcern? - public - let updates:Mongo.OutlineDocuments + + @usableFromInline internal + var updates:BSON.Output<[UInt8]> public var fields:BSON.Document @usableFromInline internal init(writeConcern:WriteConcern?, - updates:Mongo.OutlineDocuments, + updates:BSON.Output<[UInt8]>, fields:BSON.Document) { self.writeConcern = writeConcern @@ -46,7 +46,7 @@ extension Mongo.Update:MongoImplicitSessionCommand, MongoTransactableCommand, Mo @inlinable public var outline:Mongo.OutlineVector? { - .init(self.updates, type: .updates) + .init(bson: self.updates, type: .updates) } } extension Mongo.Update @@ -54,22 +54,24 @@ extension Mongo.Update @inlinable public init(_ collection:Mongo.Collection, writeConcern:Mongo.WriteConcern? = nil, - updates statements:some Sequence>) + updates encode:(inout Mongo.UpdateEncoder) throws -> ()) rethrows { + var updates:Mongo.UpdateEncoder = .init() + try encode(&updates) + self.init(writeConcern: writeConcern, - updates: .init(statements), + updates: updates.move(), fields: Self.type(collection)) } + @inlinable public init(_ collection:Mongo.Collection, writeConcern:Mongo.WriteConcern? = nil, - updates statements:some Sequence>, - with populate:(inout Self) throws -> ()) rethrows + with configure:(inout Self) throws -> (), + updates encode:(inout Mongo.UpdateEncoder) throws -> ()) rethrows { - self.init(collection, - writeConcern: writeConcern, - updates: statements) - try populate(&self) + try self.init(collection, writeConcern: writeConcern, updates: encode) + try configure(&self) } } extension Mongo.Update diff --git a/Sources/MongoLibrary/Commands/Update/Mongo.UpdateEncoder.swift b/Sources/MongoLibrary/Commands/Update/Mongo.UpdateEncoder.swift new file mode 100644 index 00000000..829e2f09 --- /dev/null +++ b/Sources/MongoLibrary/Commands/Update/Mongo.UpdateEncoder.swift @@ -0,0 +1,33 @@ +import BSON + +extension Mongo +{ + @frozen public + struct UpdateEncoder where Effect:MongoWriteEffect + { + @usableFromInline internal + var output:BSON.Output<[UInt8]> + + @inlinable internal + init() + { + self.output = .init(preallocated: []) + } + } +} +extension Mongo.UpdateEncoder +{ + @inlinable internal consuming + func move() -> BSON.Output<[UInt8]> { self.output } +} +extension Mongo.UpdateEncoder +{ + @inlinable public mutating + func callAsFunction( + with yield:(inout Mongo.UpdateStatement) throws -> T) rethrows -> T + { + try yield(&self.output[ + as: Mongo.UpdateStatement.self, + in: BSON.DocumentFrame.self]) + } +} diff --git a/Sources/MongoLibrary/Commands/Update/Mongo.UpdateResponse.swift b/Sources/MongoLibrary/Commands/Update/Mongo.UpdateResponse.swift index 4adb1a41..fcc56019 100644 --- a/Sources/MongoLibrary/Commands/Update/Mongo.UpdateResponse.swift +++ b/Sources/MongoLibrary/Commands/Update/Mongo.UpdateResponse.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver extension Mongo diff --git a/Sources/MongoLibrary/Commands/Update/Mongo.UpdateStatement.swift b/Sources/MongoLibrary/Commands/Update/Mongo.UpdateStatement.swift index dc123cd9..d51949bb 100644 --- a/Sources/MongoLibrary/Commands/Update/Mongo.UpdateStatement.swift +++ b/Sources/MongoLibrary/Commands/Update/Mongo.UpdateStatement.swift @@ -1,34 +1,36 @@ -import BSONEncoding +import BSON import MongoQL extension Mongo { @frozen public - struct UpdateStatement:MongoDocumentDSL, Sendable where Effect:MongoWriteEffect + struct UpdateStatement:Sendable where Effect:MongoWriteEffect { - public - var bson:BSON.Document + @usableFromInline internal + var bson:BSON.DocumentEncoder @inlinable public - init(_ bson:BSON.Document) + init(_ output:consuming BSON.Output<[UInt8]>) { - self.bson = bson + self.bson = .init(output) } } } +extension Mongo.UpdateStatement:BSON.Encoder +{ + @inlinable public consuming + func move() -> BSON.Output<[UInt8]> { self.bson.move() } + + @inlinable public static + var type:BSON.AnyType { .document } +} extension Mongo.UpdateStatement { @inlinable public subscript(key:Multi) -> Effect.UpdatePlurality? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } } extension Mongo.UpdateStatement @@ -36,140 +38,90 @@ extension Mongo.UpdateStatement @inlinable public subscript(key:C) -> Mongo.LetDocument? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:Q) -> Mongo.PredicateDocument? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:U) -> Mongo.UpdateDocument? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } + } + + @inlinable public + subscript(key:U, yield:(inout Mongo.PipelineEncoder) -> ()) -> Void + { + mutating + get { yield(&self.bson[with: key][as: Mongo.PipelineEncoder.self]) } } @inlinable public subscript(key:U) -> Mongo.Pipeline? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } + @inlinable public subscript(key:U) -> Replacement? where Replacement:BSONEncodable { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } + @inlinable public + subscript(key:ArrayFilters, yield:(inout Mongo.PredicateListEncoder) -> ()) -> Void + { + mutating + get { yield(&self.bson[with: key][as: Mongo.PredicateListEncoder.self]) } + } @inlinable public subscript(key:ArrayFilters) -> Mongo.PredicateList? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } + @inlinable public subscript(key:ArrayFilters) -> [Mongo.PredicateDocument] { - get - { - [] - } - set(value) - { - self.bson.append(key, value) - } + get { [] } + set (value) { value.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:Collation) -> Mongo.Collation? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:Hint) -> String? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } + @inlinable public subscript(key:Hint) -> Mongo.SortDocument? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } @inlinable public subscript(key:Upsert) -> Bool? { - get - { - nil - } - set(value) - { - self.bson.push(key, value) - } + get { nil } + set (value) { value?.encode(to: &self.bson[with: key]) } } } diff --git a/Sources/MongoLibrary/Commands/Update/Mongo.Updates.Upsertion.swift b/Sources/MongoLibrary/Commands/Update/Mongo.Updates.Upsertion.swift index ae91bf2a..5d44d8df 100644 --- a/Sources/MongoLibrary/Commands/Update/Mongo.Updates.Upsertion.swift +++ b/Sources/MongoLibrary/Commands/Update/Mongo.Updates.Upsertion.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver extension Mongo.Updates diff --git a/Sources/MongoLibrary/Effects/Modifying/Mongo.Existing.swift b/Sources/MongoLibrary/Effects/Modifying/Mongo.Existing.swift index 55445e50..d4c9f7a3 100644 --- a/Sources/MongoLibrary/Effects/Modifying/Mongo.Existing.swift +++ b/Sources/MongoLibrary/Effects/Modifying/Mongo.Existing.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Effects/Modifying/Mongo.RemovePhase.swift b/Sources/MongoLibrary/Effects/Modifying/Mongo.RemovePhase.swift index 1ec27d11..ee2c59cb 100644 --- a/Sources/MongoLibrary/Effects/Modifying/Mongo.RemovePhase.swift +++ b/Sources/MongoLibrary/Effects/Modifying/Mongo.RemovePhase.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { @@ -11,7 +11,7 @@ extension Mongo extension Mongo.RemovePhase:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { (true).encode(to: &field) } diff --git a/Sources/MongoLibrary/Effects/Modifying/Mongo.Removing.swift b/Sources/MongoLibrary/Effects/Modifying/Mongo.Removing.swift index 7dbf8752..b15b0ccb 100644 --- a/Sources/MongoLibrary/Effects/Modifying/Mongo.Removing.swift +++ b/Sources/MongoLibrary/Effects/Modifying/Mongo.Removing.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Effects/Modifying/Mongo.UpdatePhase.swift b/Sources/MongoLibrary/Effects/Modifying/Mongo.UpdatePhase.swift index f9cf0bb8..8fc3f6f4 100644 --- a/Sources/MongoLibrary/Effects/Modifying/Mongo.UpdatePhase.swift +++ b/Sources/MongoLibrary/Effects/Modifying/Mongo.UpdatePhase.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON extension Mongo { @@ -12,7 +12,7 @@ extension Mongo extension Mongo.UpdatePhase:BSONEncodable { @inlinable public - func encode(to field:inout BSON.Field) + func encode(to field:inout BSON.FieldEncoder) { (self == .new).encode(to: &field) } diff --git a/Sources/MongoLibrary/Effects/Modifying/Mongo.Upserting.swift b/Sources/MongoLibrary/Effects/Modifying/Mongo.Upserting.swift index efe4d4e0..0531219e 100644 --- a/Sources/MongoLibrary/Effects/Modifying/Mongo.Upserting.swift +++ b/Sources/MongoLibrary/Effects/Modifying/Mongo.Upserting.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Effects/Modifying/MongoModificationEffect.swift b/Sources/MongoLibrary/Effects/Modifying/MongoModificationEffect.swift index ff683545..b26f1d4f 100644 --- a/Sources/MongoLibrary/Effects/Modifying/MongoModificationEffect.swift +++ b/Sources/MongoLibrary/Effects/Modifying/MongoModificationEffect.swift @@ -1,5 +1,4 @@ -import BSONDecoding -import BSONEncoding +import BSON public protocol MongoModificationEffect diff --git a/Sources/MongoLibrary/Effects/Modifying/MongoModificationPhase.swift b/Sources/MongoLibrary/Effects/Modifying/MongoModificationPhase.swift index 65aba351..0f9bfc64 100644 --- a/Sources/MongoLibrary/Effects/Modifying/MongoModificationPhase.swift +++ b/Sources/MongoLibrary/Effects/Modifying/MongoModificationPhase.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON public protocol MongoModificationPhase:BSONEncodable diff --git a/Sources/MongoLibrary/Effects/Reading/Mongo.ExplainOnly.swift b/Sources/MongoLibrary/Effects/Reading/Mongo.ExplainOnly.swift index 6aefc035..ab1bd891 100644 --- a/Sources/MongoLibrary/Effects/Reading/Mongo.ExplainOnly.swift +++ b/Sources/MongoLibrary/Effects/Reading/Mongo.ExplainOnly.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import BSONReflection import NIOCore @@ -27,7 +27,7 @@ extension Mongo.ExplainOnly:MongoReadEffect { var output:String = "" let indent:BSON.Indent = " " + 1 - for field:BSON.ExplicitField in + for field:BSON.FieldDecoder in reply.sorted(by: { $0.key < $1.key }) { switch field.key diff --git a/Sources/MongoLibrary/Effects/Reading/Mongo.Single.swift b/Sources/MongoLibrary/Effects/Reading/Mongo.Single.swift index 7c778e55..e8ab3344 100644 --- a/Sources/MongoLibrary/Effects/Reading/Mongo.Single.swift +++ b/Sources/MongoLibrary/Effects/Reading/Mongo.Single.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver import NIOCore diff --git a/Sources/MongoLibrary/Effects/Reading/Mongo.SingleBatch.swift b/Sources/MongoLibrary/Effects/Reading/Mongo.SingleBatch.swift index 8d3ff47c..7d42783c 100644 --- a/Sources/MongoLibrary/Effects/Reading/Mongo.SingleBatch.swift +++ b/Sources/MongoLibrary/Effects/Reading/Mongo.SingleBatch.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver import NIOCore diff --git a/Sources/MongoLibrary/Effects/Reading/Mongo.SingleOutputError.swift b/Sources/MongoLibrary/Effects/Reading/Mongo.SingleOutputError.swift index b11c569e..4a8b97a2 100644 --- a/Sources/MongoLibrary/Effects/Reading/Mongo.SingleOutputError.swift +++ b/Sources/MongoLibrary/Effects/Reading/Mongo.SingleOutputError.swift @@ -17,9 +17,9 @@ extension Mongo.SingleOutputError:CustomStringConvertible switch self { case .cursor(let cursor): - return "single-output command returned additional batches (with cursor '\(cursor)')" + "single-output command returned additional batches (with cursor '\(cursor)')" case .count(let count): - return "single-output command returned \(count) elements" + "single-output command returned \(count) elements" } } } diff --git a/Sources/MongoLibrary/Effects/Reading/MongoReadEffect.swift b/Sources/MongoLibrary/Effects/Reading/MongoReadEffect.swift index 6bc7a271..57204154 100644 --- a/Sources/MongoLibrary/Effects/Reading/MongoReadEffect.swift +++ b/Sources/MongoLibrary/Effects/Reading/MongoReadEffect.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON import MongoDriver import NIOCore diff --git a/Sources/MongoLibrary/Effects/Writing/Mongo.Many.swift b/Sources/MongoLibrary/Effects/Writing/Mongo.Many.swift index ee51b711..968b05fc 100644 --- a/Sources/MongoLibrary/Effects/Writing/Mongo.Many.swift +++ b/Sources/MongoLibrary/Effects/Writing/Mongo.Many.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver extension Mongo diff --git a/Sources/MongoLibrary/Effects/Writing/Mongo.One.swift b/Sources/MongoLibrary/Effects/Writing/Mongo.One.swift index 246b89c5..050dd514 100644 --- a/Sources/MongoLibrary/Effects/Writing/Mongo.One.swift +++ b/Sources/MongoLibrary/Effects/Writing/Mongo.One.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver extension Mongo diff --git a/Sources/MongoLibrary/Effects/Writing/Mongo.WriteError.swift b/Sources/MongoLibrary/Effects/Writing/Mongo.WriteError.swift index ae65200d..993afe7e 100644 --- a/Sources/MongoLibrary/Effects/Writing/Mongo.WriteError.swift +++ b/Sources/MongoLibrary/Effects/Writing/Mongo.WriteError.swift @@ -1,4 +1,4 @@ -import BSONDecoding +import BSON extension Mongo { diff --git a/Sources/MongoLibrary/Effects/Writing/MongoWriteEffect.swift b/Sources/MongoLibrary/Effects/Writing/MongoWriteEffect.swift index cf700886..c83bc83b 100644 --- a/Sources/MongoLibrary/Effects/Writing/MongoWriteEffect.swift +++ b/Sources/MongoLibrary/Effects/Writing/MongoWriteEffect.swift @@ -1,4 +1,4 @@ -import BSONEncoding +import BSON import MongoDriver public diff --git a/Sources/MongoQL/exports.swift b/Sources/MongoQL/exports.swift index b087a84a..a2cff9bd 100644 --- a/Sources/MongoQL/exports.swift +++ b/Sources/MongoQL/exports.swift @@ -1,3 +1,3 @@ @_exported import enum Mongo.Mongo @_exported import MongoBuiltins -@_exported import MongoSchema +@_exported import MongoABI diff --git a/Sources/MongoWire/BSON.Input (ext).swift b/Sources/MongoWire/BSON.Input (ext).swift new file mode 100644 index 00000000..9871f37b --- /dev/null +++ b/Sources/MongoWire/BSON.Input (ext).swift @@ -0,0 +1,89 @@ +import BSON +import CRC + +extension BSON.Input +{ + @inlinable public mutating + func parse( + as _:Mongo.WireHeader.Type = Mongo.WireHeader.self) throws -> Mongo.WireHeader + { + // total size, including this + let size:Int32 = try self.parse(as: Int32.self) + let id:Int32 = try self.parse(as: Int32.self) + let request:Int32 = try self.parse(as: Int32.self) + let type:Int32 = try self.parse(as: Int32.self) + return try .init(size: size, id: id, request: request, type: type) + } +} +extension BSON.Input +{ + @inlinable public mutating + func parse( + as _:Mongo.WireMessage.Sections.Type = + Mongo.WireMessage.Sections.self) + throws -> Mongo.WireMessage.Sections + { + var body:BSON.DocumentView? = nil + var outlined:[Mongo.WireMessage.Outline] = [] + + while let section:UInt8 = self.next() + { + guard let section:Mongo.WireSection = .init(rawValue: section) + else + { + throw Mongo.WireSectionError.init(invalid: section) + } + switch section + { + case .body: + if case nil = body + { + body = try self.parse(as: BSON.DocumentView.self) + } + else + { + throw Mongo.WireBodyCountError.multiple + } + + case .sequence: + var sequence:BSON.Input = .init( + try self.parse(Mongo.WireSequenceFrame.self)) + + let id:String = try sequence.parse(as: String.self) + + outlined.append(.init(id: id, slice: sequence.remaining)) + } + } + + // ''' + // A fully constructed OP_MSG MUST contain exactly one Payload Type 0, and optionally + // any number of Payload Type 1 where each identifier MUST be unique per message. + // ''' + guard let body:BSON.DocumentView + else + { + throw Mongo.WireBodyCountError.none + } + + return .init(body: body, outlined: outlined) + } +} +extension BSON.Input +{ + @inlinable public mutating + func parse( + as _:Mongo.WireMessage.Type = + Mongo.WireMessage.self, + header:Mongo.WireHeader) throws -> Mongo.WireMessage + { + let flags:Mongo.WireFlags = try .init(validating: try self.parse(as: UInt32.self)) + + let sections:Mongo.WireMessage.Sections = try self.parse( + as: Mongo.WireMessage.Sections.self) + + let checksum:CRC32? = flags.contains(.checksumPresent) ? + .init(checksum: try self.parse(as: UInt32.self)) : nil + + return .init(header: header, flags: flags, sections: sections, checksum: checksum) + } +} diff --git a/Sources/MongoWire/BSON.Output (ext).swift b/Sources/MongoWire/BSON.Output (ext).swift new file mode 100644 index 00000000..7da77363 --- /dev/null +++ b/Sources/MongoWire/BSON.Output (ext).swift @@ -0,0 +1,49 @@ +import BSON +import CRC + +extension BSON.Output +{ + @inlinable public mutating + func serialize(header:Mongo.WireHeader) + { + // the `as` coercions are here to prevent us from accidentally + // changing the types of the various integers, which ``serialize(integer:)`` + // depends on. + self.serialize(integer: header.size as Int32) + self.serialize(integer: header.id.value as Int32) + self.serialize(integer: header.request.value as Int32) + self.serialize(integer: header.type.rawValue as Int32) + } +} +extension BSON.Output +{ + @inlinable public mutating + func serialize(sections:Mongo.WireMessage.Sections) + { + self.append(Mongo.WireSection.body.rawValue) + self.serialize(document: sections.body) + + for outline:Mongo.WireMessage.Outline in sections.outlined + { + self.append(Mongo.WireSection.sequence.rawValue) + + self.serialize(integer: Int32.init(outline.size)) + self.serialize(cString: outline.id) + self.append(outline.slice) + } + } +} +extension BSON.Output +{ + @inlinable public mutating + func serialize(message:Mongo.WireMessage>) + { + self.serialize(header: message.header) + self.serialize(integer: message.flags.rawValue as UInt32) + self.serialize(sections: message.sections) + if let crc32:CRC32 = message.checksum + { + self.serialize(integer: crc32.checksum as UInt32) + } + } +} diff --git a/Sources/MongoWire/Mongo.WireBodyCountError.swift b/Sources/MongoWire/Mongo.WireBodyCountError.swift new file mode 100644 index 00000000..d1b0c38e --- /dev/null +++ b/Sources/MongoWire/Mongo.WireBodyCountError.swift @@ -0,0 +1,23 @@ +extension Mongo +{ + public + enum WireBodyCountError:Equatable, Error + { + case none + case multiple + } +} +extension Mongo.WireBodyCountError:CustomStringConvertible +{ + public + var description:String + { + switch self + { + case .none: + "reply contained no body document" + case .multiple: + "reply contained multiple body documents" + } + } +} diff --git a/Sources/MongoWire/MongoWire.Flags.swift b/Sources/MongoWire/Mongo.WireFlags.swift similarity index 90% rename from Sources/MongoWire/MongoWire.Flags.swift rename to Sources/MongoWire/Mongo.WireFlags.swift index 7886e5bf..36e5632e 100644 --- a/Sources/MongoWire/MongoWire.Flags.swift +++ b/Sources/MongoWire/Mongo.WireFlags.swift @@ -1,11 +1,11 @@ -extension MongoWire +extension Mongo { @frozen public - struct Flags:OptionSet, Sendable + struct WireFlags:OptionSet, Sendable { public var rawValue:UInt32 - + @inlinable public init(rawValue:UInt32) { @@ -13,14 +13,14 @@ extension MongoWire } } } -extension MongoWire.Flags +extension Mongo.WireFlags { /// The message ends with 4 bytes containing a CRC-32C [1] checksum. /// See [Checksum](https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/#std-label-wire-msg-checksum) /// for details. public static let checksumPresent:Self = .init(rawValue: 1 << 0) - + /// Another message will follow this one without further action from the receiver. /// The receiver MUST NOT send another message until receiving one with `moreToCome` /// set to 0 as sends may block, causing deadlock. Requests with the `moreToCome` @@ -28,7 +28,7 @@ extension MongoWire.Flags /// to requests with the `exhaustAllowed` bit set. public static let moreToCome:Self = .init(rawValue: 1 << 1) - + /// The client is prepared for multiple replies to this request using the /// `moreToCome` bit. The server will never produce replies with the `moreToCome` /// bit set unless the request has this bit set. @@ -41,7 +41,7 @@ extension MongoWire.Flags @inlinable public init(validating flags:UInt32) throws { - if let error:MongoWire.FlagsError = .init(flags: flags) + if let error:Mongo.WireFlagsError = .init(flags: flags) { throw error } diff --git a/Sources/MongoWire/MongoWire.FlagsError.swift b/Sources/MongoWire/Mongo.WireFlagsError.swift similarity index 82% rename from Sources/MongoWire/MongoWire.FlagsError.swift rename to Sources/MongoWire/Mongo.WireFlagsError.swift index b13b426f..8cf2e6c9 100644 --- a/Sources/MongoWire/MongoWire.FlagsError.swift +++ b/Sources/MongoWire/Mongo.WireFlagsError.swift @@ -1,12 +1,12 @@ -extension MongoWire +extension Mongo { /// The subtype byte of a binary array was matched a reserved bit pattern. @frozen public - struct FlagsError:Equatable, Error + struct WireFlagsError:Equatable, Error { public let reserved:UInt16 - + @inlinable public init?(flags:UInt32) { @@ -18,7 +18,7 @@ extension MongoWire } } } -extension MongoWire.FlagsError:CustomStringConvertible +extension Mongo.WireFlagsError:CustomStringConvertible { public var description:String diff --git a/Sources/MongoWire/Mongo.WireHeader.swift b/Sources/MongoWire/Mongo.WireHeader.swift new file mode 100644 index 00000000..9b30b8e5 --- /dev/null +++ b/Sources/MongoWire/Mongo.WireHeader.swift @@ -0,0 +1,73 @@ +import BSON + +extension Mongo +{ + @frozen public + struct WireHeader:Identifiable, Sendable + { + /// The number of bytes in the message body, *not* including the header. + public + let count:Int + /// The identifier for this message. + public + let id:WireMessageIdentifier + /// The request this message is a response to. + public + let request:WireMessageIdentifier + /// The type of this message. + public + let type:WireMessageType + + @inlinable public + init(count:Int, id:WireMessageIdentifier, + request:WireMessageIdentifier = .none, + type:WireMessageType = .message) + { + self.count = count + self.id = id + self.request = request + self.type = type + } + } +} +extension Mongo.WireHeader +{ + /// The size, 16 bytes, of a MongoDB message header. + public static + let size:Int = 16 + + @inlinable public + var size:Int32 + { + .init(Self.size + self.count) + } + + @inlinable public + init(size:Int32, id:Int32, request:Int32, type:Int32) throws + { + guard let type:Mongo.WireMessageType = .init(rawValue: type) + else + { + throw Mongo.WireMessageTypeError.init(invalid: type) + } + self.init(count: Int.init(size) - Self.size, id: .init(id), request: .init(request), + type: type) + } +} + + +extension Mongo.WireHeader:CustomStringConvertible +{ + public + var description:String + { + """ + { + size: \(self.size) + message id: \(self.id.value) + response to: \(self.request.value) + type: \(self.type) + } + """ + } +} diff --git a/Sources/MongoWire/MongoWire.Message.Outline.swift b/Sources/MongoWire/Mongo.WireMessage.Outline.swift similarity index 84% rename from Sources/MongoWire/MongoWire.Message.Outline.swift rename to Sources/MongoWire/Mongo.WireMessage.Outline.swift index 87cb0e02..1c107a18 100644 --- a/Sources/MongoWire/MongoWire.Message.Outline.swift +++ b/Sources/MongoWire/Mongo.WireMessage.Outline.swift @@ -1,4 +1,4 @@ -extension MongoWire.Message +extension Mongo.WireMessage { @frozen public struct Outline:Identifiable @@ -19,7 +19,7 @@ extension MongoWire.Message } } } -extension MongoWire.Message.Outline +extension Mongo.WireMessage.Outline { /// The size of this outline, in bytes, when encoded in a message /// with its header. @@ -29,6 +29,6 @@ extension MongoWire.Message.Outline 5 + self.id.utf8.count + self.slice.count } } -extension MongoWire.Message.Outline:Sendable where Bytes:Sendable +extension Mongo.WireMessage.Outline:Sendable where Bytes:Sendable { } diff --git a/Sources/MongoWire/Mongo.WireMessage.Sections.swift b/Sources/MongoWire/Mongo.WireMessage.Sections.swift new file mode 100644 index 00000000..5e7935ed --- /dev/null +++ b/Sources/MongoWire/Mongo.WireMessage.Sections.swift @@ -0,0 +1,23 @@ +import BSON + +extension Mongo.WireMessage +{ + @frozen public + struct Sections + { + public + let body:BSON.DocumentView + public + let outlined:[Outline] + + @inlinable public + init(body:BSON.DocumentView, outlined:[Outline] = []) + { + self.body = body + self.outlined = outlined + } + } +} +extension Mongo.WireMessage.Sections:Sendable where Bytes:Sendable +{ +} diff --git a/Sources/MongoWire/Mongo.WireMessage.swift b/Sources/MongoWire/Mongo.WireMessage.swift new file mode 100644 index 00000000..1aae6aea --- /dev/null +++ b/Sources/MongoWire/Mongo.WireMessage.swift @@ -0,0 +1,69 @@ +import BSON +import CRC + +extension Mongo +{ + @frozen public + struct WireMessage where Bytes:RandomAccessCollection + { + public + let header:WireHeader + public + let flags:WireFlags + public + let sections:Sections + public + let checksum:CRC32? + + @inlinable public + init(header:WireHeader, flags:WireFlags, sections:Sections, checksum:CRC32?) + { + self.header = header + self.flags = flags + self.sections = sections + self.checksum = checksum + } + } +} +extension Mongo.WireMessage:Sendable where Bytes:Sendable +{ +} +extension Mongo.WireMessage:Identifiable +{ + @inlinable public + var id:Mongo.WireMessageIdentifier { self.header.id } +} +extension Mongo.WireMessage +{ + @inlinable public + init(sections:Sections, checksum:Bool = false, id:Mongo.WireMessageIdentifier) + { + // 4 bytes of flags + 1 for body section type + var count:Int = 4 + 1 + sections.body.size + + for outline:Outline in sections.outlined + { + // section type + count += 1 + count += outline.size + } + + let flags:Mongo.WireFlags + let crc32:CRC32? + if checksum + { + count += 4 + flags = [.checksumPresent] + fatalError("unimplemented") + } + else + { + flags = [] + crc32 = nil + } + + self.init(header: .init(count: count, id: id), flags: flags, + sections: sections, + checksum: crc32) + } +} diff --git a/Sources/MongoWire/MongoWire.MessageIdentifier.swift b/Sources/MongoWire/Mongo.WireMessageIdentifier.swift similarity index 67% rename from Sources/MongoWire/MongoWire.MessageIdentifier.swift rename to Sources/MongoWire/Mongo.WireMessageIdentifier.swift index 4ff61300..6c59a96c 100644 --- a/Sources/MongoWire/MongoWire.MessageIdentifier.swift +++ b/Sources/MongoWire/Mongo.WireMessageIdentifier.swift @@ -1,7 +1,7 @@ -extension MongoWire +extension Mongo { @frozen public - struct MessageIdentifier:Hashable, Sendable + struct WireMessageIdentifier:Hashable, Sendable { public var value:Int32 @@ -13,7 +13,7 @@ extension MongoWire } } } -extension MongoWire.MessageIdentifier +extension Mongo.WireMessageIdentifier { @inlinable public mutating func next() -> Self @@ -22,12 +22,12 @@ extension MongoWire.MessageIdentifier return self } } -extension MongoWire.MessageIdentifier +extension Mongo.WireMessageIdentifier { public static let none:Self = .init(0) } -extension MongoWire.MessageIdentifier:CustomStringConvertible +extension Mongo.WireMessageIdentifier:CustomStringConvertible { public var description:String diff --git a/Sources/MongoWire/MongoWire.MessageType.swift b/Sources/MongoWire/Mongo.WireMessageType.swift similarity index 63% rename from Sources/MongoWire/MongoWire.MessageType.swift rename to Sources/MongoWire/Mongo.WireMessageType.swift index cad8fea2..6a801b6d 100644 --- a/Sources/MongoWire/MongoWire.MessageType.swift +++ b/Sources/MongoWire/Mongo.WireMessageType.swift @@ -1,7 +1,7 @@ -extension MongoWire +extension Mongo { @frozen public - enum MessageType:Int32, Sendable + enum WireMessageType:Int32, Sendable { // case compressed = 2012 case message = 2013 diff --git a/Sources/MongoWire/MongoWire.MessageTypeError.swift b/Sources/MongoWire/Mongo.WireMessageTypeError.swift similarity index 70% rename from Sources/MongoWire/MongoWire.MessageTypeError.swift rename to Sources/MongoWire/Mongo.WireMessageTypeError.swift index 7d0872d9..3ed23ec2 100644 --- a/Sources/MongoWire/MongoWire.MessageTypeError.swift +++ b/Sources/MongoWire/Mongo.WireMessageTypeError.swift @@ -1,7 +1,7 @@ -extension MongoWire +extension Mongo { @frozen public - struct MessageTypeError:Equatable, Error + struct WireMessageTypeError:Equatable, Error { public let code:Int32 @@ -13,7 +13,7 @@ extension MongoWire } } } -extension MongoWire.MessageTypeError:CustomStringConvertible +extension Mongo.WireMessageTypeError:CustomStringConvertible { public var description:String diff --git a/Sources/MongoWire/MongoWire.Section.swift b/Sources/MongoWire/Mongo.WireSection.swift similarity index 64% rename from Sources/MongoWire/MongoWire.Section.swift rename to Sources/MongoWire/Mongo.WireSection.swift index 8811246c..3f8d1dd0 100644 --- a/Sources/MongoWire/MongoWire.Section.swift +++ b/Sources/MongoWire/Mongo.WireSection.swift @@ -1,7 +1,7 @@ -extension MongoWire +extension Mongo { @frozen public - enum Section:UInt8, Sendable + enum WireSection:UInt8, Sendable { case body = 0x00 case sequence = 0x01 diff --git a/Sources/MongoWire/MongoWire.SectionError.swift b/Sources/MongoWire/Mongo.WireSectionError.swift similarity index 72% rename from Sources/MongoWire/MongoWire.SectionError.swift rename to Sources/MongoWire/Mongo.WireSectionError.swift index b7a7d05a..c85f9bc6 100644 --- a/Sources/MongoWire/MongoWire.SectionError.swift +++ b/Sources/MongoWire/Mongo.WireSectionError.swift @@ -1,12 +1,12 @@ -extension MongoWire +extension Mongo { /// The subtype byte of a binary array was matched a reserved bit pattern. @frozen public - struct SectionError:Equatable, Error + struct WireSectionError:Equatable, Error { public let code:UInt8 - + @inlinable public init(invalid code:UInt8) { @@ -14,9 +14,9 @@ extension MongoWire } } } -extension MongoWire.SectionError:CustomStringConvertible +extension Mongo.WireSectionError:CustomStringConvertible { - public + public var description:String { "invalid MongoDB message section type code (\(self.code))" diff --git a/Sources/MongoWire/MongoWire.SequenceFrame.swift b/Sources/MongoWire/Mongo.WireSequenceFrame.swift similarity index 64% rename from Sources/MongoWire/MongoWire.SequenceFrame.swift rename to Sources/MongoWire/Mongo.WireSequenceFrame.swift index 9b4a0c87..fd1162e8 100644 --- a/Sources/MongoWire/MongoWire.SequenceFrame.swift +++ b/Sources/MongoWire/Mongo.WireSequenceFrame.swift @@ -1,9 +1,9 @@ -import BSONTraversal +import BSON -extension MongoWire +extension Mongo { public - enum SequenceFrame:VariableLengthBSONFrame + enum WireSequenceFrame:BSON.FrameType { @inlinable public static var skipped:Int { -4 } diff --git a/Sources/MongoWire/Mongo.WireVersion.swift b/Sources/MongoWire/Mongo.WireVersion.swift new file mode 100644 index 00000000..807afda2 --- /dev/null +++ b/Sources/MongoWire/Mongo.WireVersion.swift @@ -0,0 +1,44 @@ +import BSON + +@available(*, deprecated, renamed: "Mongo.WireVersion") +public +typealias MongoWire = Mongo.WireVersion + +extension Mongo +{ + /// A MongoDB wire protocol version. This is not the same thing as a server version. + /// + /// > See: + /// https://github.com/mongodb/specifications/blob/master/source/wireversion-featurelist.rst + @frozen public + struct WireVersion:RawRepresentable + { + public + let rawValue:Int32 + + @inlinable public + init(rawValue:Int32) + { + self.rawValue = rawValue + } + } +} +extension Mongo.WireVersion:Comparable +{ + @inlinable public static + func < (lhs:Self, rhs:Self) -> Bool + { + lhs.rawValue < rhs.rawValue + } +} +extension Mongo.WireVersion:ExpressibleByIntegerLiteral +{ + @inlinable public + init(integerLiteral:Int32) + { + self.init(rawValue: integerLiteral) + } +} +extension Mongo.WireVersion:BSONDecodable, BSONEncodable +{ +} diff --git a/Sources/MongoWire/MongoWire.BodyCountError.swift b/Sources/MongoWire/MongoWire.BodyCountError.swift deleted file mode 100644 index 24426ac2..00000000 --- a/Sources/MongoWire/MongoWire.BodyCountError.swift +++ /dev/null @@ -1,23 +0,0 @@ -extension MongoWire -{ - public - enum BodyCountError:Equatable, Error - { - case none - case multiple - } -} -extension MongoWire.BodyCountError:CustomStringConvertible -{ - public - var description:String - { - switch self - { - case .none: - return "reply contained no body document" - case .multiple: - return "reply contained multiple body documents" - } - } -} diff --git a/Sources/MongoWire/MongoWire.Header.swift b/Sources/MongoWire/MongoWire.Header.swift deleted file mode 100644 index cadf95da..00000000 --- a/Sources/MongoWire/MongoWire.Header.swift +++ /dev/null @@ -1,101 +0,0 @@ -import BSON - -extension MongoWire -{ - @frozen public - struct Header:Identifiable, Sendable - { - /// The number of bytes in the message body, *not* including the header. - public - let count:Int - /// The identifier for this message. - public - let id:MessageIdentifier - /// The request this message is a response to. - public - let request:MessageIdentifier - /// The type of this message. - public - let type:MessageType - - @inlinable public - init(count:Int, id:MessageIdentifier, - request:MessageIdentifier = .none, - type:MessageType = .message) - { - self.count = count - self.id = id - self.request = request - self.type = type - } - } -} -extension MongoWire.Header -{ - /// The size, 16 bytes, of a MongoDB message header. - public static - let size:Int = 16 - - @inlinable public - var size:Int32 - { - .init(Self.size + self.count) - } - - @inlinable public - init(size:Int32, id:Int32, request:Int32, type:Int32) throws - { - guard let type:MongoWire.MessageType = .init(rawValue: type) - else - { - throw MongoWire.MessageTypeError.init(invalid: type) - } - self.init(count: Int.init(size) - Self.size, id: .init(id), request: .init(request), - type: type) - } -} - -extension BSON.Input -{ - @inlinable public mutating - func parse( - as _:MongoWire.Header.Type = MongoWire.Header.self) throws -> MongoWire.Header - { - // total size, including this - let size:Int32 = try self.parse(as: Int32.self) - let id:Int32 = try self.parse(as: Int32.self) - let request:Int32 = try self.parse(as: Int32.self) - let type:Int32 = try self.parse(as: Int32.self) - return try .init(size: size, id: id, request: request, type: type) - } -} -extension BSON.Output -{ - @inlinable public mutating - func serialize(header:MongoWire.Header) - { - // the `as` coercions are here to prevent us from accidentally - // changing the types of the various integers, which ``serialize(integer:)`` - // depends on. - self.serialize(integer: header.size as Int32) - self.serialize(integer: header.id.value as Int32) - self.serialize(integer: header.request.value as Int32) - self.serialize(integer: header.type.rawValue as Int32) - } -} - -extension MongoWire.Header:CustomStringConvertible -{ - public - var description:String - { - """ - { - size: \(self.size) - message id: \(self.id.value) - response to: \(self.request.value) - type: \(self.type) - } - """ - } -} diff --git a/Sources/MongoWire/MongoWire.Message.Sections.swift b/Sources/MongoWire/MongoWire.Message.Sections.swift deleted file mode 100644 index f5dac30b..00000000 --- a/Sources/MongoWire/MongoWire.Message.Sections.swift +++ /dev/null @@ -1,96 +0,0 @@ -import BSON - -extension MongoWire.Message -{ - @frozen public - struct Sections - { - public - let body:BSON.DocumentView - public - let outlined:[Outline] - - @inlinable public - init(body:BSON.DocumentView, outlined:[Outline] = []) - { - self.body = body - self.outlined = outlined - } - } -} -extension MongoWire.Message.Sections:Sendable where Bytes:Sendable -{ -} - -extension BSON.Input -{ - @inlinable public mutating - func parse( - as _:MongoWire.Message.Sections.Type = - MongoWire.Message.Sections.self) - throws -> MongoWire.Message.Sections - { - var body:BSON.DocumentView? = nil - var outlined:[MongoWire.Message.Outline] = [] - - while let section:UInt8 = self.next() - { - guard let section:MongoWire.Section = .init(rawValue: section) - else - { - throw MongoWire.SectionError.init(invalid: section) - } - switch section - { - case .body: - if case nil = body - { - body = try self.parse(as: BSON.DocumentView.self) - } - else - { - throw MongoWire.BodyCountError.multiple - } - - case .sequence: - var sequence:BSON.Input = .init( - try self.parse(MongoWire.SequenceFrame.self)) - - let id:String = try sequence.parse(as: String.self) - - outlined.append(.init(id: id, slice: sequence.remaining)) - } - } - - // ''' - // A fully constructed OP_MSG MUST contain exactly one Payload Type 0, and optionally - // any number of Payload Type 1 where each identifier MUST be unique per message. - // ''' - guard let body:BSON.DocumentView - else - { - throw MongoWire.BodyCountError.none - } - - return .init(body: body, outlined: outlined) - } -} - -extension BSON.Output -{ - @inlinable public mutating - func serialize(sections:MongoWire.Message.Sections) - { - self.append(MongoWire.Section.body.rawValue) - self.serialize(document: sections.body) - - for outline:MongoWire.Message.Outline in sections.outlined - { - self.append(MongoWire.Section.sequence.rawValue) - - self.serialize(integer: Int32.init(outline.size)) - self.serialize(cString: outline.id) - self.append(outline.slice) - } - } -} diff --git a/Sources/MongoWire/MongoWire.Message.swift b/Sources/MongoWire/MongoWire.Message.swift deleted file mode 100644 index 519334aa..00000000 --- a/Sources/MongoWire/MongoWire.Message.swift +++ /dev/null @@ -1,107 +0,0 @@ -import CRC -import BSON - -extension MongoWire -{ - @frozen public - struct Message where Bytes:RandomAccessCollection - { - public - let header:Header - public - let flags:Flags - public - let sections:Sections - public - let checksum:CRC32? - - @inlinable public - init(header:Header, flags:Flags, sections:Sections, checksum:CRC32?) - { - self.header = header - self.flags = flags - self.sections = sections - self.checksum = checksum - } - } -} -extension MongoWire.Message:Sendable where Bytes:Sendable -{ -} -extension MongoWire.Message:Identifiable -{ - @inlinable public - var id:MongoWire.MessageIdentifier - { - self.header.id - } -} - -extension BSON.Input -{ - @inlinable public mutating - func parse( - as _:MongoWire.Message.Type = MongoWire.Message.self, - header:MongoWire.Header) throws -> MongoWire.Message - { - let flags:MongoWire.Flags = try .init(validating: try self.parse(as: UInt32.self)) - - let sections:MongoWire.Message.Sections = try self.parse( - as: MongoWire.Message.Sections.self) - - let checksum:CRC32? = flags.contains(.checksumPresent) ? - .init(checksum: try self.parse(as: UInt32.self)) : nil - - return .init(header: header, flags: flags, sections: sections, checksum: checksum) - } -} - -extension MongoWire.Message -{ - @inlinable public - init(sections:Sections, checksum:Bool = false, id:MongoWire.MessageIdentifier) - { - // 4 bytes of flags + 1 for body section type - var count:Int = 4 + 1 + sections.body.size - - for outline:Outline in sections.outlined - { - // section type - count += 1 - count += outline.size - } - - let flags:MongoWire.Flags - let crc32:CRC32? - if checksum - { - count += 4 - flags = [.checksumPresent] - fatalError("unimplemented") - } - else - { - flags = [] - crc32 = nil - } - - self.init(header: .init(count: count, id: id), flags: flags, - sections: sections, - checksum: crc32) - } -} - -extension BSON.Output -{ - @inlinable public mutating - func serialize(message:MongoWire.Message>) - { - self.serialize(header: message.header) - self.serialize(integer: message.flags.rawValue as UInt32) - self.serialize(sections: message.sections) - if let crc32:CRC32 = message.checksum - { - self.serialize(integer: crc32.checksum as UInt32) - } - } -} diff --git a/Sources/MongoWire/MongoWire.swift b/Sources/MongoWire/MongoWire.swift deleted file mode 100644 index 1fc5ffed..00000000 --- a/Sources/MongoWire/MongoWire.swift +++ /dev/null @@ -1,31 +0,0 @@ -/// A MongoDB wire protocol version. This is not the same thing as a server version. -/// -/// See: https://github.com/mongodb/specifications/blob/master/source/wireversion-featurelist.rst -@frozen public -struct MongoWire:RawRepresentable -{ - public - let rawValue:Int32 - - @inlinable public - init(rawValue:Int32) - { - self.rawValue = rawValue - } -} -extension MongoWire:Comparable -{ - @inlinable public static - func < (lhs:Self, rhs:Self) -> Bool - { - lhs.rawValue < rhs.rawValue - } -} -extension MongoWire:ExpressibleByIntegerLiteral -{ - @inlinable public - init(integerLiteral:Int32) - { - self.init(rawValue: integerLiteral) - } -} diff --git a/Sources/MongoWire/exports.swift b/Sources/MongoWire/exports.swift new file mode 100644 index 00000000..a7e14112 --- /dev/null +++ b/Sources/MongoWire/exports.swift @@ -0,0 +1 @@ +@_exported import enum Mongo.Mongo diff --git a/Sources/OnlineCDFTests/Main.swift b/Sources/OnlineCDFTests/Main.swift index 35b643b1..563f50b9 100644 --- a/Sources/OnlineCDFTests/Main.swift +++ b/Sources/OnlineCDFTests/Main.swift @@ -5,13 +5,13 @@ import OnlineCDF import Testing -@main -enum Main:SyncTests +@main +enum Main:TestMain, TestBattery { static - func run(tests:Tests) + func run(tests:TestGroup) { - if let tests:TestGroup = tests / "insert-one" + if let tests:TestGroup = tests / "InsertOne" { let histogram:OnlineCDF = .init(resolution: 100, seed: 2) @@ -30,7 +30,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.999) ==? 2) tests.expect(histogram.estimate(quantile: 1.000) ==? 2) } - if let tests:TestGroup = tests / "insert-many" + if let tests:TestGroup = tests / "InsertMany" { var histogram:OnlineCDF = .init(resolution: 100, seed: 1) @@ -53,7 +53,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.999) ==? 100) tests.expect(histogram.estimate(quantile: 1.000) ==? 100) } - if let tests:TestGroup = tests / "insert-one-batch" + if let tests:TestGroup = tests / "InsertOneBatch" { let histogram:OnlineCDF = .init(resolution: 100, @@ -73,7 +73,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.999) ==? 100) tests.expect(histogram.estimate(quantile: 1.000) ==? 100) } - if let tests:TestGroup = tests / "insert-many-batches" + if let tests:TestGroup = tests / "InsertManyBatches" { var histogram:OnlineCDF = .init(resolution: 100, @@ -95,7 +95,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.999) ==? 200) tests.expect(histogram.estimate(quantile: 1.000) ==? 200) } - if let tests:TestGroup = tests / "large" + if let tests:TestGroup = tests / "Large" { let histogram:OnlineCDF = .init(resolution: 100, @@ -115,7 +115,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.999) ==? 999.5) tests.expect(histogram.estimate(quantile: 1.000) ==? 1000) } - if let tests:TestGroup = tests / "signed" + if let tests:TestGroup = tests / "Signed" { let histogram:OnlineCDF = .init(resolution: 100, @@ -137,7 +137,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.999) ==? 100) tests.expect(histogram.estimate(quantile: 1.000) ==? 100) } - if let tests:TestGroup = tests / "outliers" + if let tests:TestGroup = tests / "Outliers" { var histogram:OnlineCDF = .init(resolution: 100, @@ -151,7 +151,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.900) ==? 90.5) tests.expect(histogram.estimate(quantile: 1.000) ==? 1_000_000) } - if let tests:TestGroup = tests / "steps-balanced" + if let tests:TestGroup = tests / "StepsBalanced" { var histogram:OnlineCDF = .init(resolution: 100, @@ -173,7 +173,7 @@ enum Main:SyncTests tests.expect(histogram.estimate(quantile: 0.9) ==? 3) tests.expect(histogram.estimate(quantile: 1.0) ==? 3) } - if let tests:TestGroup = tests / "steps-unbalanced" + if let tests:TestGroup = tests / "StepsUnbalanced" { var histogram:OnlineCDF = .init(resolution: 100, diff --git a/Sources/SCRAM/SCRAM.ChallengeError.swift b/Sources/SCRAM/SCRAM.ChallengeError.swift index d3bbf814..bdf1bf28 100644 --- a/Sources/SCRAM/SCRAM.ChallengeError.swift +++ b/Sources/SCRAM/SCRAM.ChallengeError.swift @@ -15,10 +15,10 @@ extension SCRAM.ChallengeError:CustomStringConvertible switch self { case .attribute(missing: let attribute): - return "missing expected attribute '\(attribute)'" + "missing expected attribute '\(attribute)'" case .nonce(let received, sent: let sent): - return "received nonce '\(received)' is inconsistent with sent nonce '\(sent)'" + "received nonce '\(received)' is inconsistent with sent nonce '\(sent)'" } } } diff --git a/Sources/SCRAM/SCRAM.ClientResponse.swift b/Sources/SCRAM/SCRAM.ClientResponse.swift index 96c640b0..eaad7f1c 100644 --- a/Sources/SCRAM/SCRAM.ClientResponse.swift +++ b/Sources/SCRAM/SCRAM.ClientResponse.swift @@ -65,7 +65,7 @@ extension SCRAM.ClientResponse } extension SCRAM.ClientResponse { - /// Returns [`true`]() if the given server response is consistent with + /// Returns `true` if the given server response is consistent with /// the server signature computed for this client response. @inlinable public func verify(_ response:SCRAM.ServerResponse) -> Bool