use super::error::DbError;
use super::repo::{
artifact::ArtifactRepoImpl, budget::BudgetRepoImpl, event::EventRepoImpl, goal::GoalRepoImpl,
proof::ProofRepoImpl, task::TaskRepoImpl,
};
#[must_use = "DbTransaction must be explicitly committed or rolled back"]
pub struct DbTransaction {
pub(super) conn: tokio_rusqlite::Connection,
pub(super) active: bool,
}
impl DbTransaction {
pub async fn commit(mut self) -> Result<(), DbError> {
if !self.active {
return Err(DbError::TransactionExpired);
}
self.conn
.call(|conn| -> Result<(), rusqlite::Error> {
conn.execute("COMMIT", [])?;
Ok(())
})
.await
.map_err(DbError::Connection)?;
self.active = false;
Ok(())
}
pub async fn rollback(mut self) -> Result<(), DbError> {
if !self.active {
return Err(DbError::TransactionExpired);
}
self.conn
.call(|conn| -> Result<(), rusqlite::Error> {
conn.execute("ROLLBACK", [])?;
Ok(())
})
.await
.map_err(DbError::Connection)?;
self.active = false;
Ok(())
}
pub fn goal_repo(&self) -> GoalRepoImpl {
GoalRepoImpl {
conn: self.conn.clone(),
}
}
pub fn task_repo(&self) -> TaskRepoImpl {
TaskRepoImpl {
conn: self.conn.clone(),
}
}
pub fn event_repo(&self) -> EventRepoImpl {
EventRepoImpl {
conn: self.conn.clone(),
}
}
pub fn proof_repo(&self) -> ProofRepoImpl {
ProofRepoImpl {
conn: self.conn.clone(),
}
}
pub fn budget_repo(&self) -> BudgetRepoImpl {
BudgetRepoImpl {
conn: self.conn.clone(),
}
}
pub fn artifact_repo(&self) -> ArtifactRepoImpl {
ArtifactRepoImpl {
conn: self.conn.clone(),
}
}
}
impl Drop for DbTransaction {
fn drop(&mut self) {
if self.active {
tracing::warn!("DbTransaction dropped without explicit commit or rollback");
if let Ok(handle) = tokio::runtime::Handle::try_current() {
let conn = self.conn.clone();
handle.spawn(async move {
let _ = conn
.call(|conn| -> Result<(), rusqlite::Error> {
let _ = conn.execute("ROLLBACK", []);
Ok(())
})
.await;
});
}
}
}
}
impl std::fmt::Debug for DbTransaction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DbTransaction")
.field("active", &self.active)
.finish_non_exhaustive()
}
}