diff --git a/Package.swift b/Package.swift index 48cc9bef..c54eb0ed 100644 --- a/Package.swift +++ b/Package.swift @@ -1,6 +1,11 @@ // swift-tools-version:6.1 import PackageDescription +/// This list matches the [supported platforms on the Swift 6.1 release of SPM](https://github.com/swiftlang/swift-package-manager/blob/release/6.1/Sources/PackageDescription/SupportedPlatforms.swift). +/// Don't add new platforms here unless raising the swift-tools-version of this manifest. +let allPlatforms: [Platform] = [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS, .driverKit, .linux, .windows, .android, .wasi, .openbsd] +let nonWASIPlatforms: [Platform] = allPlatforms.filter { $0 != .wasi } + let package = Package( name: "sql-kit", platforms: [ @@ -24,7 +29,11 @@ let package = Package( dependencies: [ .product(name: "Collections", package: "swift-collections"), .product(name: "Logging", package: "swift-log"), - .product(name: "NIOCore", package: "swift-nio"), + // SwiftNIO does not support wasm32-unknown-wasip1. Target dependency conditions + // are evaluated per platform, so on WASI NIOCore is simply not linked and the + // `EventLoopFuture` surface drops out via `#if canImport(NIOCore)`; the async + // surface is unaffected. + .product(name: "NIOCore", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)), ], swiftSettings: swiftSettings ), @@ -38,8 +47,9 @@ let package = Package( .testTarget( name: "SQLKitTests", dependencies: [ - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), + // The test suite exercises the SwiftNIO surface, so it is not built for WASI. + .product(name: "NIOCore", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)), + .product(name: "NIOEmbedded", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)), .target(name: "SQLKit"), .target(name: "SQLKitBenchmark"), ], diff --git a/Sources/SQLKit/Builders/Prototypes/SQLQueryBuilder.swift b/Sources/SQLKit/Builders/Prototypes/SQLQueryBuilder.swift index eb258737..f3e13d13 100644 --- a/Sources/SQLKit/Builders/Prototypes/SQLQueryBuilder.swift +++ b/Sources/SQLKit/Builders/Prototypes/SQLQueryBuilder.swift @@ -1,4 +1,7 @@ +// EventLoopFuture is elided on builds without SwiftNIO. +#if canImport(NIOCore) import class NIOCore.EventLoopFuture +#endif /// Base definitions for builders which set up queries and execute them against a given database. /// @@ -10,18 +13,23 @@ public protocol SQLQueryBuilder: AnyObject { /// Connection to execute query on. var database: any SQLDatabase { get } + // N.B.: The `#if` encloses the doc comment; placed between the comment and the declaration it + // would detach the doc from the symbol graph. + #if canImport(NIOCore) /// Execute the query on the connection, ignoring any results. /// /// Although it is a protocol requirement for historical reasons, this is considered a legacy interface /// thanks to its reliance on `EventLoopFuture`. Users should call ``run()-3tldd`` whenever possible. func run() -> EventLoopFuture - + #endif + /// Execute the query on the connection, ignoring any results. func run() async throws } extension SQLQueryBuilder { + #if canImport(NIOCore) /// Execute the query associated with the builder on the builder's database, ignoring any results. /// /// See ``SQLQueryFetcher`` for methods which retrieve results from a query. @@ -29,6 +37,7 @@ extension SQLQueryBuilder { public func run() -> EventLoopFuture { self.database.execute(sql: self.query) { _ in } } + #endif /// Execute the query associated with the builder on the builder's database, ignoring any results. /// diff --git a/Sources/SQLKit/Builders/Prototypes/SQLQueryFetcher.swift b/Sources/SQLKit/Builders/Prototypes/SQLQueryFetcher.swift index f6c9489c..a8066bfd 100644 --- a/Sources/SQLKit/Builders/Prototypes/SQLQueryFetcher.swift +++ b/Sources/SQLKit/Builders/Prototypes/SQLQueryFetcher.swift @@ -1,10 +1,15 @@ +// `EventLoopFuture` is unavailable where SwiftNIO is not linked. The `async` `first()`/`all()`/ +// `run(_:)` families further down are unconditional and carry the whole surface there. +#if canImport(NIOCore) import class NIOCore.EventLoopFuture +#endif /// Common definitions for ``SQLQueryBuilder``s which support retrieving result rows. public protocol SQLQueryFetcher: SQLQueryBuilder {} // MARK: - First (EventLoopFuture) +#if canImport(NIOCore) extension SQLQueryFetcher { /// Returns the named column from the first output row, if any, decoded as a given type. /// @@ -70,6 +75,8 @@ extension SQLQueryFetcher { } } +#endif // canImport(NIOCore) + // MARK: - First (async) extension SQLQueryFetcher { @@ -144,6 +151,7 @@ extension SQLQueryFetcher { // MARK: - All (EventLoopFuture) +#if canImport(NIOCore) extension SQLQueryFetcher { /// Returns the named column from each output row, if any, decoded as a given type. /// @@ -205,6 +213,8 @@ extension SQLQueryFetcher { } } +#endif // canImport(NIOCore) + // MARK: - All (async) extension SQLQueryFetcher { @@ -271,6 +281,7 @@ extension SQLQueryFetcher { // MARK: - Run (EventLoopFuture) +#if canImport(NIOCore) extension SQLQueryFetcher { /// Using a default-configured ``SQLRowDecoder``, call the provided handler closure with the result of decoding /// each output row, if any, as a given type. @@ -337,6 +348,8 @@ extension SQLQueryFetcher { } } +#endif // canImport(NIOCore) + // MARK: - Run (async) extension SQLQueryFetcher { diff --git a/Sources/SQLKit/Database/SQLDatabase.swift b/Sources/SQLKit/Database/SQLDatabase.swift index 376186c4..ef579bc1 100644 --- a/Sources/SQLKit/Database/SQLDatabase.swift +++ b/Sources/SQLKit/Database/SQLDatabase.swift @@ -1,5 +1,9 @@ +// SwiftNIO is not available on every platform SQLKit supports (see Package.swift); the +// EventLoopFuture surface below is elided where it is absent. +#if canImport(NIOCore) import protocol NIOCore.EventLoop import class NIOCore.EventLoopFuture +#endif import struct Logging.Logger /// The common interface to SQLKit for both drivers and client code. @@ -56,6 +60,9 @@ public protocol SQLDatabase: Sendable { /// The `Logger` used for logging all operations relating to a given database. var logger: Logger { get } + // N.B.: Every `#if` in this file encloses the doc comment of the declaration it gates; placed + // between the two it would silently detach the doc from the symbol graph. + #if canImport(NIOCore) /// The `EventLoop` used for asynchronous operations on a given database. /// /// If there is no specific `EventLoop` which handles the database (such as because it is a connection pool which @@ -63,7 +70,8 @@ public protocol SQLDatabase: Sendable { /// Concurrency or some other asynchronous execution technology), a single consistent `EventLoop` must be chosen /// for the database and returned for this property nonetheless. var eventLoop: any EventLoop { get } - + #endif + /// The version number the database reports for itself. /// /// The version must be provided via a type conforming to the ``SQLDatabaseReportedVersion`` protocol. If the @@ -102,6 +110,7 @@ public protocol SQLDatabase: Sendable { /// > it's unavoidable, as there are no direct entry points to SQLKit without a driver. var queryLogLevel: Logger.Level? { get } + #if canImport(NIOCore) /// Requests that the given generic SQL query be serialized and executed on the database, and that /// the `onRow` closure be invoked once for each result row the query returns (if any). /// @@ -118,6 +127,7 @@ public protocol SQLDatabase: Sendable { sql query: any SQLExpression, _ onRow: @escaping @Sendable (any SQLRow) -> () ) -> EventLoopFuture + #endif /// Requests that the given generic SQL query be serialized and executed on the database, and that /// the `onRow` closure be invoked once for each result row the query returns (if any). @@ -200,6 +210,10 @@ extension SQLDatabase { } extension SQLDatabase { + // This default bridges the `async` requirement to the legacy `EventLoopFuture` one, so it can + // only exist where the latter does. Without SwiftNIO there is no future overload to forward to + // and conformers implement the `async` `execute` directly. + #if canImport(NIOCore) /// The default implementation for ``execute(sql:_:)-4eg19``. @inlinable public func execute( @@ -208,7 +222,8 @@ extension SQLDatabase { ) async throws { try await self.execute(sql: query, onRow).get() } - + #endif + /// The default implementation for ``withSession(_:)-9b68j``. @inlinable public func withSession( @@ -227,10 +242,12 @@ private struct CustomLoggerSQLDatabase: SQLDatabase { // See `SQLDatabase.logger`. let logger: Logger + #if canImport(NIOCore) // See `SQLDatabase.eventLoop`. var eventLoop: any EventLoop { self.database.eventLoop } + #endif // See `SQLDatabase.version`. var version: (any SQLDatabaseReportedVersion)? { @@ -247,6 +264,7 @@ private struct CustomLoggerSQLDatabase: SQLDatabase { self.database.queryLogLevel } + #if canImport(NIOCore) // See `SQLDatabase.execute(sql:_:)`. func execute( sql query: any SQLExpression, @@ -254,6 +272,7 @@ private struct CustomLoggerSQLDatabase: SQLDatabase { ) -> EventLoopFuture { self.database.execute(sql: query, onRow) } + #endif // See `SQLDatabase.execute(sql:_:)`. func execute( diff --git a/Sources/SQLKit/Exports.swift b/Sources/SQLKit/Exports.swift index 370dd880..f9da3453 100644 --- a/Sources/SQLKit/Exports.swift +++ b/Sources/SQLKit/Exports.swift @@ -1,3 +1,6 @@ +// EventLoop/EventLoopFuture are SwiftNIO types, elided where SwiftNIO is unavailable. +#if canImport(NIOCore) @_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoop @_documentation(visibility: internal) @_exported import class NIOCore.EventLoopFuture +#endif @_documentation(visibility: internal) @_exported import struct Logging.Logger diff --git a/Sources/SQLKitBenchmark/SQLBenchmarker.swift b/Sources/SQLKitBenchmark/SQLBenchmarker.swift index 964de1dc..3eef8610 100644 --- a/Sources/SQLKitBenchmark/SQLBenchmarker.swift +++ b/Sources/SQLKitBenchmark/SQLBenchmarker.swift @@ -1,5 +1,7 @@ import Logging +#if canImport(NIOCore) import NIOCore +#endif public import SQLKit import XCTest @@ -21,6 +23,8 @@ public final class SQLBenchmarker: Sendable { } } + // The deprecated EventLoopFuture bridges are elided where SwiftNIO is unavailable. + #if canImport(NIOCore) @available(*, deprecated, renamed: "runAllTests()", message: "Use `runAllTests()` instead.") public func testAll() throws { try database.eventLoop.makeFutureWithTask { try await self.runAllTests() }.wait() @@ -30,6 +34,7 @@ public final class SQLBenchmarker: Sendable { public func run() throws { try self.testAll() } + #endif func runTest( _ name: String = #function,