diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 27ce87c..995942f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,8 @@ jobs: contents: read uses: vapor/ci/.github/workflows/run-unit-tests.yml@main secrets: inherit + with: + with_wasm: true # Make sure downstream dependents still work dependents-check: diff --git a/Package.swift b/Package.swift index 3d1b537..a223716 100644 --- a/Package.swift +++ b/Package.swift @@ -1,6 +1,11 @@ // swift-tools-version:5.8 import PackageDescription +/// This list matches the [supported platforms on the Swift 5.8 release of SPM](https://github.com/swiftlang/swift-package-manager/blob/release/5.8/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: "sqlite-kit", platforms: [ @@ -22,8 +27,12 @@ let package = Package( .target( name: "SQLiteKit", dependencies: [ - .product(name: "NIOFoundationCompat", package: "swift-nio"), - .product(name: "AsyncKit", package: "async-kit"), + // Neither SwiftNIO nor AsyncKit builds for wasm32-unknown-wasip1. Target dependency + // conditions are evaluated per platform, so on WASI these are simply not linked; + // SQLiteKit then compiles without the connection pool and without the + // EventLoopFuture surface (see the `#if canImport(...)` gates in Sources/). + .product(name: "NIOFoundationCompat", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)), + .product(name: "AsyncKit", package: "async-kit", condition: .when(platforms: nonWASIPlatforms)), .product(name: "SQLiteNIO", package: "sqlite-nio"), .product(name: "SQLKit", package: "sql-kit"), ], diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index ae4fbb4..1ca2fbc 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -1,6 +1,11 @@ // swift-tools-version:5.9 import PackageDescription +/// This list matches the [supported platforms on the Swift 5.9 release of SPM](https://github.com/swiftlang/swift-package-manager/blob/release/5.9/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: "sqlite-kit", platforms: [ @@ -22,8 +27,12 @@ let package = Package( .target( name: "SQLiteKit", dependencies: [ - .product(name: "NIOFoundationCompat", package: "swift-nio"), - .product(name: "AsyncKit", package: "async-kit"), + // Neither SwiftNIO nor AsyncKit builds for wasm32-unknown-wasip1. Target dependency + // conditions are evaluated per platform, so on WASI these are simply not linked; + // SQLiteKit then compiles without the connection pool and without the + // EventLoopFuture surface (see the `#if canImport(...)` gates in Sources/). + .product(name: "NIOFoundationCompat", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)), + .product(name: "AsyncKit", package: "async-kit", condition: .when(platforms: nonWASIPlatforms)), .product(name: "SQLiteNIO", package: "sqlite-nio"), .product(name: "SQLKit", package: "sql-kit"), ], diff --git a/Sources/SQLiteKit/Exports.swift b/Sources/SQLiteKit/Exports.swift index 6656d7a..1751cb3 100644 --- a/Sources/SQLiteKit/Exports.swift +++ b/Sources/SQLiteKit/Exports.swift @@ -1,4 +1,7 @@ @_documentation(visibility: internal) @_exported import SQLKit @_documentation(visibility: internal) @_exported import SQLiteNIO +// AsyncKit — and with it the connection pool — is unavailable where SwiftNIO is (see Package.swift). +#if canImport(AsyncKit) @_documentation(visibility: internal) @_exported import AsyncKit +#endif @_documentation(visibility: internal) @_exported import struct Logging.Logger diff --git a/Sources/SQLiteKit/SQLiteConnection+SQLKit.swift b/Sources/SQLiteKit/SQLiteConnection+SQLKit.swift index 5bd69ce..19ed883 100644 --- a/Sources/SQLiteKit/SQLiteConnection+SQLKit.swift +++ b/Sources/SQLiteKit/SQLiteConnection+SQLKit.swift @@ -173,11 +173,13 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion { @usableFromInline let decoder: SQLiteDataDecoder + #if canImport(NIOCore) // See `SQLDatabase.eventLoop`. @usableFromInline var eventLoop: any EventLoop { self.database.eventLoop } + #endif // See `SQLDatabase.version`. @usableFromInline @@ -209,6 +211,9 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion { self.queryLogLevel = queryLogLevel } + // The `EventLoopFuture` overload only exists where SwiftNIO does; SQLKit's protocol drops the + // requirement on the other platforms and the `async` overload below carries the whole surface. + #if canImport(NIOCore) // See `SQLDatabase.execute(sql:_:)`. @usableFromInline func execute( @@ -234,6 +239,7 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion { { onRow($0.sql(decoder: self.decoder)) } ) } + #endif // See `SQLDatabase.execute(sql:_:)`. @usableFromInline @@ -254,6 +260,9 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion { ) } + // `withSession(_:)` rides sqlite-nio's protocol-level `withConnection`, which exists only on the + // SwiftNIO build; elsewhere SQLKit's default `withSession` applies. + #if canImport(NIOCore) // See `SQLDatabase.withSession(_:)`. @usableFromInline func withSession(_ closure: @escaping @Sendable (any SQLDatabase) async throws -> R) async throws -> R { @@ -261,4 +270,5 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion { try await closure($0.sql(encoder: self.encoder, decoder: self.decoder, queryLogLevel: self.queryLogLevel)) } } + #endif } diff --git a/Sources/SQLiteKit/SQLiteConnectionSource.swift b/Sources/SQLiteKit/SQLiteConnectionSource.swift index e327a50..c3026de 100644 --- a/Sources/SQLiteKit/SQLiteConnectionSource.swift +++ b/Sources/SQLiteKit/SQLiteConnectionSource.swift @@ -1,3 +1,7 @@ +// ``SQLiteConnectionSource`` is a `ConnectionPoolSource`, built on AsyncKit and SwiftNIO's thread +// pool. Neither is available on every platform SQLiteKit supports; where they are absent, callers +// use sqlite-nio's concrete async `SQLiteConnection` directly and there is no pool. +#if canImport(AsyncKit) #if canImport(Darwin) import Foundation #else @@ -97,3 +101,4 @@ fileprivate extension SQLiteConfiguration.Storage { } } } +#endif // canImport(AsyncKit) diff --git a/Sources/SQLiteKit/SQLiteDataDecoder.swift b/Sources/SQLiteKit/SQLiteDataDecoder.swift index 68fe89a..be1333c 100644 --- a/Sources/SQLiteKit/SQLiteDataDecoder.swift +++ b/Sources/SQLiteKit/SQLiteDataDecoder.swift @@ -1,7 +1,9 @@ import Foundation import SQLiteNIO @_spi(CodableUtilities) import SQLKit +#if canImport(NIOFoundationCompat) import NIOFoundationCompat +#endif /// Translates `SQLiteData` values received from the database into `Decodable` values. /// @@ -51,7 +53,11 @@ public struct SQLiteDataDecoder: Sendable { switch data { case .text(let str): buf = .init(str.utf8) + #if canImport(NIOCore) case .blob(let blob): buf = .init(buffer: blob, byteTransferStrategy: .noCopy) + #else + case .blob(let blob): buf = .init(blob) // `SQLiteData.blob` carries `[UInt8]` without SwiftNIO + #endif // The remaining cases should never happen, but we implement them anyway just in case. case .integer(let n): buf = .init(String(n).utf8) case .float(let n): buf = .init(String(n).utf8) diff --git a/Sources/SQLiteKit/SQLiteDataEncoder.swift b/Sources/SQLiteKit/SQLiteDataEncoder.swift index 97e7ed3..3f4299a 100644 --- a/Sources/SQLiteKit/SQLiteDataEncoder.swift +++ b/Sources/SQLiteKit/SQLiteDataEncoder.swift @@ -1,4 +1,6 @@ +#if canImport(NIOCore) import NIOCore +#endif import Foundation @_spi(CodableUtilities) import SQLKit import SQLiteNIO