// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// A connection descriptor that carries the parameters needed to reach a
/// PostgreSQL backend and conforms to the synchronous [`@moondb.Driver`] seam.
///
/// ## The async wall (why this is a façade)
///
/// `@moondb.Driver`'s methods are **synchronous** (`fn execute(...) raise
/// DbError`), matching an FFI-backed driver like moon-sqlite whose C calls
/// block. PostgreSQL, however, is spoken over TCP, and MoonBit's only socket
/// stack (`moonbitlang/async`) is **async-only**: an `async fn` cannot be
/// called from a synchronous one, and the runtime exposes no public
/// "run-this-async-thunk-to-completion" bridge (`with_event_loop` lives in an
/// import-blocked `internal` package). A synchronous method therefore cannot
/// perform a PostgreSQL round trip.
///
/// The faithful, working driver is the **async [`PgConn`]** (see conn.mbt),
/// which mirrors asyncpg: `PgConn::connect` then `.execute` / `.query` /
/// `.begin` / `.commit` / `.rollback` / `.close`, all `async`, used inside an
/// event loop (`async test` / `async fn main`). The CI integration suite drives
/// a real PostgreSQL through exactly that API.
///
/// `PgDriver` exists to *demonstrate the seam* and to give moondb-based code a
/// stable target: it implements every `@moondb.Driver` method. The row-touching
/// methods raise a precise `ConnectError` explaining that the round trip must
/// run through `PgConn` under an event loop; `close` is a no-op. When moondb
/// grows an async `Driver` variant (or MoonBit ships a blocking socket / a
/// public event-loop entry), this façade becomes a thin adapter over `PgConn`
/// with no behavioural change to callers.
pub struct PgDriver {
host : String
port : Int
user : String
password : String
database : String
}
///|
/// Build a driver descriptor. Does not connect — the async wall means the
/// actual connection is opened by [`PgConn::connect`] inside an event loop.
pub fn PgDriver::new(
host~ : String,
port? : Int = 5432,
user~ : String,
password? : String = "",
database~ : String,
) -> PgDriver {
{ host, port, user, password, database }
}
///|
/// Open the async connection this descriptor points at. This is the intended
/// entry point: call it inside an event loop and use the returned [`PgConn`].
pub async fn PgDriver::connect(self : PgDriver) -> PgConn raise DbError {
PgConn::connect(self.host, self.port, self.user, self.password, self.database)
}
///|
fn async_wall_error(op : String) -> DbError {
@moondb.ConnectError(
"PgDriver::" +
op +
" cannot run synchronously: PostgreSQL I/O is async under MoonBit. Use PgDriver::connect(...) / PgConn inside an event loop (async test / async fn main). See README §\"The async wall\".",
)
}
///|
pub impl @moondb.Driver for PgDriver with fn execute(
_self : PgDriver,
_sql : String,
_params : Array[Value],
) -> ExecResult raise DbError {
raise async_wall_error("execute")
}
///|
pub impl @moondb.Driver for PgDriver with fn query(
_self : PgDriver,
_sql : String,
_params : Array[Value],
) -> Array[Row] raise DbError {
raise async_wall_error("query")
}
///|
pub impl @moondb.Driver for PgDriver with fn begin(_self : PgDriver) -> Unit raise DbError {
raise async_wall_error("begin")
}
///|
pub impl @moondb.Driver for PgDriver with fn commit(_self : PgDriver) -> Unit raise DbError {
raise async_wall_error("commit")
}
///|
pub impl @moondb.Driver for PgDriver with fn rollback(_self : PgDriver) -> Unit raise DbError {
raise async_wall_error("rollback")
}
///|
pub impl @moondb.Driver for PgDriver with fn close(_self : PgDriver) -> Unit {
}