// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// A dependency-free, in-memory reference [`Driver`]. It exists for two reasons:
/// to *prove the interface is implementable* end to end, and to give query layers
/// built on moondb (moonorm and friends) a real test double they can run against
/// with no database and no native backend — it compiles on every target.
///
/// It is a deliberately naive echo store, **not** a SQL engine: it does not parse
/// `sql`. Each `execute` appends one [`Row`] built from the bound `params` (columns
/// named `c0`, `c1`, …) and hands back an [`ExecResult`] with a monotonically
/// increasing `last_insert_id`; each `query` returns every stored row. What it
/// *does* model faithfully is the transaction bracket: `begin` snapshots the store,
/// `rollback` restores it, `commit` keeps the changes — so a test can assert real
/// rollback semantics against it.
pub struct MockDriver {
mut rows : Array[Row]
mut next_id : Int64
savepoints : Array[Int]
mut closed : Bool
}
///|
/// A fresh, empty mock connection.
pub fn MockDriver::new() -> MockDriver {
{ rows: [], next_id: 0, savepoints: [], closed: false }
}
///|
/// How many rows the store currently holds. A test-facing helper, not part of the
/// [`Driver`] contract.
pub fn MockDriver::row_count(self : MockDriver) -> Int {
self.rows.length()
}
///|
/// Whether the connection has been closed.
pub fn MockDriver::is_closed(self : MockDriver) -> Bool {
self.closed
}
///|
/// Whether a transaction is currently open (at least one un-committed `begin`).
pub fn MockDriver::in_transaction(self : MockDriver) -> Bool {
self.savepoints.length() > 0
}
///|
pub impl Driver for MockDriver with fn execute(
self : MockDriver,
_sql : String,
params : Array[Value],
) -> ExecResult raise DbError {
if self.closed {
raise Closed
}
let columns = []
for i in 0.. Array[Row] raise DbError {
if self.closed {
raise Closed
}
self.rows[:].to_owned()
}
///|
pub impl Driver for MockDriver with fn begin(self : MockDriver) -> Unit raise DbError {
if self.closed {
raise Closed
}
self.savepoints.push(self.rows.length())
}
///|
pub impl Driver for MockDriver with fn commit(self : MockDriver) -> Unit raise DbError {
if self.closed {
raise Closed
}
if self.savepoints.pop() is None {
raise QueryError("no transaction to commit")
}
}
///|
pub impl Driver for MockDriver with fn rollback(self : MockDriver) -> Unit raise DbError {
if self.closed {
raise Closed
}
match self.savepoints.pop() {
Some(n) => self.rows = self.rows[:n].to_owned()
None => raise QueryError("no transaction to roll back")
}
}
///|
pub impl Driver for MockDriver with fn close(self : MockDriver) -> Unit {
self.closed = true
}