2026-08-04 15:42:49 +02:00
import FluentPostgresDriver
2026-07-11 09:15:58 +00:00
import FluentSQLiteDriver
import HummingbirdFluent
import Logging
2026-08-04 16:59:43 +02:00
import PostgresNIO
2026-07-11 09:15:58 +00:00
/// A factory building the `Fluent` service the application persists through.
///
2026-07-30 06:33:57 +00:00
/// Built once around the driver the executable picks at startup and called as a function to produce the configured service: `let fluent = service()`.
2026-07-11 09:15:58 +00:00
public struct Service : Sendable {
2026-08-04 16:59:43 +02:00
// 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
}
2026-07-11 09:15:58 +00:00
// MARK: Properties
2026-08-04 16:59:43 +02:00
/// The resolved persistence backend to register.
private let backend : Backend
2026-07-11 09:15:58 +00:00
/// The logger the database emits through.
private let logger : Logger
// MARK: Initializers
/// Creates a factory for a `Fluent` service backed by the given driver.
2026-08-04 16:59:43 +02:00
///
/// The TLS context for the PostgreSQL backend is built here, once — the factory holds only resolved configuration, so producing the
/// service afterwards cannot fail.
2026-07-11 09:15:58 +00:00
/// - Parameters:
/// - driver: the persistence backend to register.
/// - logger: the logger the database emits through.
2026-08-04 16:59:43 +02:00
/// - Throws: an error when the TLS context for the PostgreSQL backend cannot be built.
2026-07-11 09:15:58 +00:00
public init (
driver : Driver ,
logger : Logger
2026-08-04 16:59:43 +02:00
) throws {
switch driver {
case . postgres ( let configuration ):
self . backend = . postgres (
configuration ,
try configuration . tls . postgresTLS ()
)
case . inMemory :
self . backend = . inMemory
}
2026-07-11 09:15:58 +00:00
self . logger = logger
}
// MARK: Methods
/// Builds a `Fluent` service configured for the driver.
///
2026-07-30 06:33:57 +00:00
/// 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.
2026-07-11 09:15:58 +00:00
/// - Returns: the configured `Fluent` service, ready to be added to the service group.
2026-08-04 16:59:43 +02:00
public func callAsFunction () -> Fluent {
2026-07-11 09:15:58 +00:00
let fluent = Fluent (
logger : logger
)
2026-08-04 16:59:43 +02:00
switch backend {
case . postgres ( let configuration , let tls ):
2026-07-11 09:15:58 +00:00
fluent . databases . use (
2026-08-04 15:42:49 +02:00
. postgres (
2026-07-11 09:15:58 +00:00
configuration : . init (
hostname : configuration . host ,
port : configuration . port ,
username : configuration . username ,
password : configuration . password ,
database : configuration . name ,
2026-08-04 16:59:43 +02:00
tls : tls
2026-07-11 09:15:58 +00:00
),
maxConnectionsPerEventLoop : configuration . maxConnectionsPerEventLoop
),
2026-08-04 15:42:49 +02:00
as : . psql ,
2026-07-11 09:15:58 +00:00
isDefault : true
)
case . inMemory :
2026-07-30 06:33:57 +00:00
// A single connection keeps every query pointed at the same in-memory store, rather than each pooled
// connection getting its own private database.
2026-07-11 09:15:58 +00:00
fluent . databases . use (
. sqlite (. memory , maxConnectionsPerEventLoop : 1 ),
as : . sqlite ,
isDefault : true
)
}
return fluent
}
}