use pyo3::prelude::*;
use pyo3::types::PyDict;
use crate::Transaction as RustTransaction;
use crate::types::{Node, Edge};
use uuid::Uuid;
use super::to_py_result;
#[pyclass(name = "Transaction")]
pub struct PyTransaction {
inner: Option<RustTransaction>,
}
impl PyTransaction {
pub(crate) fn new(tx: RustTransaction) -> Self {
PyTransaction { inner: Some(tx) }
}
fn take_inner(&mut self) -> PyResult<RustTransaction> {
self.inner.take().ok_or_else(|| {
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
"Transaction already committed or rolled back"
)
})
}
fn get_inner_mut(&mut self) -> PyResult<&mut RustTransaction> {
self.inner.as_mut().ok_or_else(|| {
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
"Transaction already committed or rolled back"
)
})
}
}
#[pymethods]
impl PyTransaction {
fn add_node(
&mut self,
py: Python<'_>,
label: &str,
properties: &Bound<'_, PyDict>
) -> PyResult<String> {
let tx = self.get_inner_mut()?;
let props = super::pydict_to_props(properties)?;
let mut node = Node::new(label);
for(key, value) in props{
node = node.with_property(key, value);
}
let node_id = node.id;
let _ = crate::python::runtime::block_on(py, async {
tx.add_node(node).await
});
to_py_result(Ok(()))?;
Ok(node_id.to_string())
}
#[pyo3(signature = (source, target, edge_type, properties=None))]
fn add_edge(
&mut self,
source: &str,
target: &str,
edge_type: &str,
properties: Option<&Bound<'_, PyDict>>
) -> PyResult<String> {
let tx = self.get_inner_mut()?;
let source_id = Uuid::parse_str(source)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(
format!("Invalid source UUID: {}", e)
))?;
let target_id = Uuid::parse_str(target)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(
format!("Invalid target UUID: {}", e)
))?;
let mut edge = Edge::new(source_id, target_id, edge_type);
if let Some(props_dict) = properties {
edge.properties.extend(super::pydict_to_props(props_dict)?);
}
let edge_id = edge.id;
let _ = tx.add_edge(edge);
Ok(edge_id.to_string())
}
fn commit(&mut self, py: Python<'_>) -> PyResult<()> {
let tx = self.take_inner()?;
let result = crate::python::runtime::block_on(py, async {
tx.commit().await
});
to_py_result(result)
}
fn rollback(&mut self, py: Python<'_>) -> PyResult<()> {
let tx = self.take_inner()?;
let result = crate::python::runtime::block_on(py, async {
tx.rollback()
});
to_py_result(result)
}
fn __repr__(&self) -> String {
if self.inner.is_some() {
"<Transaction: active>".to_string()
} else {
"<Transaction: closed>".to_string()
}
}
}