diff --git a/Package.resolved b/Package.resolved index 146de01..33959b4 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ec031c0e73c03866bdfcc8adfdfda67f96b863f8ba73e708298a520ec0daf502", + "originHash" : "89777d929ec26bbed6f64b140dce168ca0fbc69d9273ed1edcfaf674a6bb8eb4", "pins" : [ { "identity" : "feather-database", @@ -13,10 +13,19 @@ { "identity" : "sqlite-nio", "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/sqlite-nio.git", + "location" : "https://github.com/vapor/sqlite-nio", "state" : { - "revision" : "2ab61385b70da8ed74958ce62fa9ebf0359cb08b", - "version" : "1.12.2" + "revision" : "b1467926902fe813b9eb118a32e78e7a58d20f25", + "version" : "1.12.3" + } + }, + { + "identity" : "swift-async-algorithms", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-async-algorithms.git", + "state" : { + "revision" : "6c050d5ef8e1aa6342528460db614e9770d7f804", + "version" : "1.1.1" } }, { @@ -42,8 +51,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log", "state" : { - "revision" : "7ee16e465622412764b0ff0c1301801dc71b8f61", - "version" : "1.9.0" + "revision" : "2778fd4e5a12a8aaa30a3ee8285f4ce54c5f3181", + "version" : "1.9.1" } }, { @@ -51,8 +60,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "233f61bc2cfbb22d0edeb2594da27a20d2ce514e", - "version" : "2.93.0" + "revision" : "5e72fc102906ebe75a3487595a653e6f43725552", + "version" : "2.94.0" + } + }, + { + "identity" : "swift-service-lifecycle", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-server/swift-service-lifecycle", + "state" : { + "revision" : "1de37290c0ab3c5a96028e0f02911b672fd42348", + "version" : "2.9.1" } }, { diff --git a/Package.swift b/Package.swift index f856435..016c569 100644 --- a/Package.swift +++ b/Package.swift @@ -34,10 +34,19 @@ let package = Package( products: [ .library(name: "FeatherSQLiteDatabase", targets: ["FeatherSQLiteDatabase"]), ], + traits: [ + "ServiceLifecycleSupport", + .default( + enabledTraits: [ + "ServiceLifecycleSupport", + ] + ), + ], dependencies: [ .package(url: "https://github.com/apple/swift-log", from: "1.6.0"), .package(url: "https://github.com/vapor/sqlite-nio", from: "1.12.0"), .package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-beta.3"), + .package(url: "https://github.com/swift-server/swift-service-lifecycle", from: "2.8.0"), // [docc-plugin-placeholder] ], targets: [ @@ -52,8 +61,13 @@ let package = Package( .target( name: "FeatherSQLiteDatabase", dependencies: [ - .product(name: "FeatherDatabase", package: "feather-database"), .target(name: "SQLiteNIOExtras"), + .product(name: "FeatherDatabase", package: "feather-database"), + .product( + name: "ServiceLifecycle", + package: "swift-service-lifecycle", + condition: .when(traits: ["ServiceLifecycleSupport"]) + ), ], swiftSettings: defaultSwiftSettings ), diff --git a/README.md b/README.md index 1748c5f..a960c2f 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,30 @@ Then add `FeatherSQLiteDatabase` to your target dependencies: .product(name: "FeatherSQLiteDatabase", package: "feather-sqlite-database"), ``` +<<<<<<< HEAD +### Package traits + +This package offers additional integrations you can enable using [package traits](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/addingdependencies#Packages-with-Traits). +To enable an additional trait on the package, update the package dependency: + +```diff +.package( + url: "https://github.com/feather-framework/feather-sqlite-database", + exact: "1.0.0-beta.3", ++ traits: [ ++ .defaults, ++ "ServiceLifecycleSupport", ++ ] +) +``` + +Available traits: + +- `ServiceLifecycleSupport` (default): Adds support for `SQLiteClientService`, a `ServiceLifecycle.Service` implementation for managing SQLite clients. + + +======= +>>>>>>> main ## Usage API documentation is available at the link below: diff --git a/Sources/FeatherSQLiteDatabase/SQLiteDatabaseService.swift b/Sources/FeatherSQLiteDatabase/SQLiteDatabaseService.swift new file mode 100644 index 0000000..8e21b83 --- /dev/null +++ b/Sources/FeatherSQLiteDatabase/SQLiteDatabaseService.swift @@ -0,0 +1,41 @@ +// +// SQLiteDatabaseService.swift +// feather-sqlite-database +// +// Created by Tibor Bödecs on 2026. 01. 29.. +// + +#if ServiceLifecycleSupport + +import SQLiteNIOExtras +import ServiceLifecycle + +/// A `Service` wrapper around an `SQLiteClient`. +public struct SQLiteDatabaseService: Service { + + /// The underlying SQLite client instance. + public var client: SQLiteClient + + /// Creates a new SQLite client service. + /// + /// - Parameter client: The SQLite client to manage for the service lifecycle. + public init( + _ client: SQLiteClient + ) { + self.client = client + } + + /// Runs the SQLite client service. + /// + /// This method starts the SQLite client, waits for a graceful shutdown + /// signal, and then shuts down the client in an orderly manner. + /// + /// - Throws: Rethrows any error produced while starting the SQLite client. + public func run() async throws { + try await client.run() + try? await gracefulShutdown() + await client.shutdown() + } + +} +#endif diff --git a/Sources/SQLiteNIOExtras/SQLiteConnectionPool.swift b/Sources/SQLiteNIOExtras/SQLiteConnectionPool.swift index b4ca9b1..ab8180f 100644 --- a/Sources/SQLiteNIOExtras/SQLiteConnectionPool.swift +++ b/Sources/SQLiteNIOExtras/SQLiteConnectionPool.swift @@ -208,7 +208,9 @@ actor SQLiteConnectionPool { catch { configuration.logger.warning( "Failed to close SQLite connection after setup error", - metadata: ["error": "\(error)"] + metadata: [ + "error": "\(error)" + ] ) } throw error @@ -225,7 +227,9 @@ actor SQLiteConnectionPool { catch { configuration.logger.warning( "Failed to close SQLite connection", - metadata: ["error": "\(error)"] + metadata: [ + "error": "\(error)" + ] ) } } diff --git a/Tests/FeatherSQLiteDatabaseTests/SQLiteDatabaseTestSuite.swift b/Tests/FeatherSQLiteDatabaseTests/FeatherSQLiteDatabaseTestSuite.swift similarity index 93% rename from Tests/FeatherSQLiteDatabaseTests/SQLiteDatabaseTestSuite.swift rename to Tests/FeatherSQLiteDatabaseTests/FeatherSQLiteDatabaseTestSuite.swift index 53a55f7..0b5b853 100644 --- a/Tests/FeatherSQLiteDatabaseTests/SQLiteDatabaseTestSuite.swift +++ b/Tests/FeatherSQLiteDatabaseTests/FeatherSQLiteDatabaseTestSuite.swift @@ -1,5 +1,5 @@ // -// SQLiteDatabaseTestSuite.swift +// FeatherSQLiteDatabaseTestSuite.swift // feather-sqlite-database // // Created by Tibor Bödecs on 2026. 01. 10.. @@ -14,7 +14,7 @@ import Testing @testable import FeatherSQLiteDatabase @Suite -struct SQLiteDatabaseTestSuite { +struct FeatherSQLiteDatabaseTestSuite { private func runUsingTestDatabaseClient( _ closure: ((SQLiteDatabaseClient) async throws -> Void) @@ -775,3 +775,62 @@ struct SQLiteDatabaseTestSuite { } } } + +#if ServiceLifecycleSupport +import ServiceLifecycle + +extension FeatherSQLiteDatabaseTestSuite { + + @Test + func serviceLifecycleSupport() async throws { + var logger = Logger(label: "test") + logger.logLevel = .info + + let configuration = SQLiteClient.Configuration( + storage: .memory, + logger: logger, + ) + let client = SQLiteClient(configuration: configuration) + let database = SQLiteDatabaseClient(client: client, logger: logger) + let service = SQLiteDatabaseService(client) + + let serviceGroup = ServiceGroup( + services: [service], + logger: logger + ) + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await serviceGroup.run() + } + group.addTask { + let result = try await database.withConnection { connection in + try await connection.run( + query: #""" + SELECT + sqlite_version() AS "version" + WHERE + 1=\#(1); + """# + ) + } + + let resultArray = try await result.collect() + #expect(resultArray.count == 1) + + let item = resultArray[0] + let version = try item.decode( + column: "version", + as: String.self + ) + #expect(version.split(separator: ".").count == 3) + } + try await group.next() + + try await Task.sleep(for: .milliseconds(100)) + + await serviceGroup.triggerGracefulShutdown() + } + } +} +#endif