#![forbid(unsafe_code)]
use crate::core::tds::Tds;
use std::ptr;
pub(crate) trait TdsRollbackOwner<U, V, const D: usize> {
fn rollback_tds(&self) -> &Tds<U, V, D>;
fn rollback_tds_mut(&mut self) -> &mut Tds<U, V, D>;
}
impl<U, V, const D: usize> TdsRollbackOwner<U, V, D> for Tds<U, V, D> {
fn rollback_tds(&self) -> &Self {
self
}
fn rollback_tds_mut(&mut self) -> &mut Self {
self
}
}
struct TdsRollbackSnapshot<U, V, const D: usize> {
owner: *const (),
snapshot: Tds<U, V, D>,
}
impl<U, V, const D: usize> TdsRollbackSnapshot<U, V, D>
where
U: Clone,
V: Clone,
{
fn capture(tds: &Tds<U, V, D>) -> Self {
Self {
owner: ptr::from_ref(tds).cast::<()>(),
snapshot: tds.clone_for_rollback(),
}
}
fn restore_to(&self, tds: &mut Tds<U, V, D>) {
let target_owner = ptr::from_ref(tds).cast::<()>();
assert!(
ptr::eq(self.owner, target_owner),
"rollback snapshot must be restored to the TDS owner location it was captured from"
);
tds.clone_from_for_rollback(&self.snapshot);
}
}
#[must_use = "rollback transactions restore on drop unless explicitly committed or rolled back"]
pub(crate) struct TdsOwnerRollbackTransaction<'owner, O, U, V, const D: usize>
where
O: TdsRollbackOwner<U, V, D>,
U: Clone,
V: Clone,
{
owner: &'owner mut O,
snapshot: TdsRollbackSnapshot<U, V, D>,
finished: bool,
}
impl<'owner, O, U, V, const D: usize> TdsOwnerRollbackTransaction<'owner, O, U, V, D>
where
O: TdsRollbackOwner<U, V, D>,
U: Clone,
V: Clone,
{
pub(crate) fn begin(owner: &'owner mut O) -> Self {
let snapshot = TdsRollbackSnapshot::capture(owner.rollback_tds());
Self {
owner,
snapshot,
finished: false,
}
}
pub(crate) const fn owner_mut(&mut self) -> &mut O {
&mut *self.owner
}
pub(crate) fn restore(&mut self) {
self.snapshot.restore_to(self.owner.rollback_tds_mut());
}
pub(crate) fn commit(mut self) {
self.finished = true;
}
pub(crate) fn rollback(mut self) {
self.restore();
self.finished = true;
}
pub(crate) const fn commit_in_place(&mut self) {
self.finished = true;
}
}
impl<O, U, V, const D: usize> Drop for TdsOwnerRollbackTransaction<'_, O, U, V, D>
where
O: TdsRollbackOwner<U, V, D>,
U: Clone,
V: Clone,
{
fn drop(&mut self) {
if !self.finished {
self.restore();
}
}
}
pub(crate) type TdsRollbackTransaction<'tds, U, V, const D: usize> =
TdsOwnerRollbackTransaction<'tds, Tds<U, V, D>, U, V, D>;
impl<U, V, const D: usize> TdsOwnerRollbackTransaction<'_, Tds<U, V, D>, U, V, D>
where
U: Clone,
V: Clone,
{
pub(crate) const fn tds_mut(&mut self) -> &mut Tds<U, V, D> {
self.owner_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(
expected = "rollback snapshot must be restored to the TDS owner location it was captured from"
)]
fn snapshot_restore_rejects_cross_owner_target() {
let source: Tds<(), (), 2> = Tds::empty();
let snapshot = TdsRollbackSnapshot::capture(&source);
let mut target: Tds<(), (), 2> = Tds::empty();
snapshot.restore_to(&mut target);
}
}