use pyo3::exceptions::PyRuntimeError;
use pyo3::prelude::*;
use crate::core::single_file::SingleFileDB as RustSingleFileDB;
use crate::graph::db::GraphDB as RustGraphDB;
use crate::graph::tx::{
begin_read_tx as graph_begin_read_tx, begin_tx as graph_begin_tx, commit as graph_commit,
rollback as graph_rollback, TxHandle as GraphTxHandle,
};
use crate::types::TxState as GraphTxState;
use std::sync::Mutex;
pub trait TransactionOps {
fn begin_impl(&self, read_only: bool) -> PyResult<i64>;
fn commit_impl(&self) -> PyResult<()>;
fn rollback_impl(&self) -> PyResult<()>;
fn has_transaction_impl(&self) -> PyResult<bool>;
}
pub fn with_graph_tx<F, R>(
db: &RustGraphDB,
graph_tx: &Mutex<Option<GraphTxState>>,
f: F,
) -> PyResult<R>
where
F: FnOnce(&mut GraphTxHandle) -> PyResult<R>,
{
let mut guard = graph_tx
.lock()
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
let tx_state = guard
.take()
.ok_or_else(|| PyRuntimeError::new_err("No active transaction"))?;
let mut handle = GraphTxHandle::new(db, tx_state);
let result = f(&mut handle);
*guard = Some(handle.tx);
result
}
pub fn begin_single_file(db: &RustSingleFileDB, read_only: bool) -> PyResult<i64> {
let txid = db
.begin(read_only)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to begin transaction: {e}")))?;
Ok(txid as i64)
}
pub fn begin_graph(
db: &RustGraphDB,
graph_tx: &Mutex<Option<GraphTxState>>,
read_only: bool,
) -> PyResult<i64> {
let mut tx_guard = graph_tx
.lock()
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
if tx_guard.is_some() {
return Err(PyRuntimeError::new_err("Transaction already active"));
}
let handle = if read_only {
graph_begin_read_tx(db)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to begin transaction: {e}")))?
} else {
graph_begin_tx(db)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to begin transaction: {e}")))?
};
let txid = handle.tx.txid as i64;
*tx_guard = Some(handle.tx);
Ok(txid)
}
pub fn commit_single_file(db: &RustSingleFileDB) -> PyResult<()> {
db.commit()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to commit: {e}")))
}
pub fn commit_graph(db: &RustGraphDB, graph_tx: &Mutex<Option<GraphTxState>>) -> PyResult<()> {
let mut tx_guard = graph_tx
.lock()
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
let tx_state = tx_guard
.take()
.ok_or_else(|| PyRuntimeError::new_err("No active transaction"))?;
let mut handle = GraphTxHandle::new(db, tx_state);
graph_commit(&mut handle)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to commit: {e}")))?;
Ok(())
}
pub fn rollback_single_file(db: &RustSingleFileDB) -> PyResult<()> {
db.rollback()
.map_err(|e| PyRuntimeError::new_err(format!("Failed to rollback: {e}")))
}
pub fn rollback_graph(db: &RustGraphDB, graph_tx: &Mutex<Option<GraphTxState>>) -> PyResult<()> {
let mut tx_guard = graph_tx
.lock()
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
let tx_state = tx_guard
.take()
.ok_or_else(|| PyRuntimeError::new_err("No active transaction"))?;
let mut handle = GraphTxHandle::new(db, tx_state);
graph_rollback(&mut handle)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to rollback: {e}")))?;
Ok(())
}
#[cfg(test)]
mod tests {
}