import FluentPostgresDriver import FluentSQLiteDriver import HummingbirdFluent import Logging import PostgresNIO /// A factory building the `Fluent` service the application persists through. /// /// Built once around the driver the executable picks at startup and called as a function to produce the configured service: `let fluent = service()`. public struct Service: Sendable { // MARK: Enumerations /// The persistence backend resolved at construction, with the PostgreSQL TLS mode already built. private enum Backend { case postgres(Configuration, PostgresConnection.Configuration.TLS) case inMemory } // MARK: Properties /// The resolved persistence backend to register. private let backend: Backend /// The logger the database emits through. private let logger: Logger // MARK: Initializers /// Creates a factory for a `Fluent` service backed by the given driver. /// /// The TLS context for the PostgreSQL backend is built here, once — the factory holds only resolved configuration, so producing the /// service afterwards cannot fail. /// - Parameters: /// - driver: the persistence backend to register. /// - logger: the logger the database emits through. /// - Throws: an error when the TLS context for the PostgreSQL backend cannot be built. public init( driver: Driver, logger: Logger ) throws { switch driver { case .postgres(let configuration): self.backend = .postgres( configuration, try configuration.tls.postgresTLS() ) case .inMemory: self.backend = .inMemory } self.logger = logger } // MARK: Methods /// Builds a `Fluent` service configured for the driver. /// /// The selected backend is registered as the *default* database, so repositories resolve it with a plain `fluent.db()` and stay agnostic of which /// driver is in use. The returned service is not yet running; add it to the application's service group (`app.addServices(_:)`) so it starts and shuts /// its connection pool down alongside the server. /// - Returns: the configured `Fluent` service, ready to be added to the service group. public func callAsFunction() -> Fluent { let fluent = Fluent( logger: logger ) switch backend { case .postgres(let configuration, let tls): fluent.databases.use( .postgres( configuration: .init( hostname: configuration.host, port: configuration.port, username: configuration.username, password: configuration.password, database: configuration.name, tls: tls ), maxConnectionsPerEventLoop: configuration.maxConnectionsPerEventLoop ), as: .psql, isDefault: true ) case .inMemory: // A single connection keeps every query pointed at the same in-memory store, rather than each pooled // connection getting its own private database. fluent.databases.use( .sqlite(.memory, maxConnectionsPerEventLoop: 1), as: .sqlite, isDefault: true ) } return fluent } }