#![doc = include_str!("../README.md")]
#[allow(private_bounds)]
pub struct Transaction<'a, T: Iterator + Clone, P: Policy> {
original_iter: &'a mut T,
iter: T,
policy: P,
}
#[allow(private_bounds)]
impl<'a, T: Iterator + Clone, P: Policy> Transaction<'a, T, P> {
#[must_use]
pub fn new(iter: &'a mut T, policy: P) -> Self {
let new_iter = iter.clone();
Transaction {
original_iter: iter,
iter: new_iter,
policy,
}
}
}
#[allow(private_bounds)]
impl<T: Iterator + Clone, P: Policy> Transaction<'_, T, P> {
#[must_use]
pub fn nest(&mut self) -> Transaction<'_, T, P> {
let iter = self.iter.clone();
Transaction {
original_iter: &mut self.iter,
iter,
policy: self.policy.clone(),
}
}
}
unsafe impl<T: Send + Iterator + Clone, P: Policy> Send for Transaction<'_, T, P> {}
impl<'a, T: Iterator + Clone> Transaction<'a, T, Panic> {
#[must_use]
pub fn new_panic(iter: &'a mut T) -> Self {
let new_iter = iter.clone();
Transaction {
original_iter: iter,
iter: new_iter,
policy: Panic,
}
}
pub fn commit(self) {
let Transaction {
original_iter,
iter,
policy,
} = self;
*original_iter = iter;
std::mem::forget(policy);
}
pub fn rollback(self) {
let Transaction { policy, .. } = self;
std::mem::forget(policy);
}
#[deprecated = "use rollback"]
#[doc(hidden)]
pub fn abort(self) {
self.rollback();
}
pub fn reset(&mut self) {
self.iter = self.original_iter.clone();
}
#[allow(private_bounds)]
pub fn call<S: IsSuccess>(mut self, f: impl FnOnce(&mut Self) -> S) -> S {
let result = f(&mut self);
if result.is_success() {
self.commit();
} else {
self.rollback();
}
result
}
}
impl<'a, T: Iterator + Clone> Iterator for Transaction<'a, T, Panic> {
type Item = T::Item;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<'a, T: Iterator + Clone> Transaction<'a, T, Rollback> {
#[must_use]
pub fn new_rollback(iter: &'a mut T) -> Self {
let new_iter = iter.clone();
Transaction {
original_iter: iter,
iter: new_iter,
policy: Rollback,
}
}
#[deprecated = "use new_rollback"]
#[doc(hidden)]
pub fn new_abort(iter: &'a mut T) -> Self {
Self::new_rollback(iter)
}
pub fn commit(self) {
let Transaction {
original_iter,
iter,
..
} = self;
*original_iter = iter;
}
#[inline]
pub fn rollback(self) {
}
#[deprecated = "use rollback"]
#[doc(hidden)]
pub fn abort(self) {
}
pub fn reset(&mut self) {
self.iter = self.original_iter.clone();
}
#[allow(private_bounds)]
pub fn call<S: IsSuccess>(mut self, f: impl FnOnce(&mut Self) -> S) -> S {
let result = f(&mut self);
if result.is_success() {
self.commit();
} else {
self.rollback();
}
result
}
}
impl<'a, T: Iterator + Clone> Iterator for Transaction<'a, T, Rollback> {
type Item = T::Item;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<'a, T: Iterator + Clone> Transaction<'a, T, AutoCommit> {
#[must_use]
pub fn new_autocommit(iter: &'a mut T) -> Self {
let new_iter = iter.clone();
Transaction {
original_iter: iter,
iter: new_iter,
policy: AutoCommit,
}
}
#[inline]
pub fn commit(self) {
}
pub fn rollback(self) {
let Transaction {
original_iter,
iter,
..
} = self;
*original_iter = iter;
}
#[deprecated = "use rollback"]
#[doc(hidden)]
pub fn abort(self) {
self.rollback();
}
pub fn reset(&mut self) {
*self.original_iter = self.iter.clone();
}
#[allow(private_bounds)]
pub fn call<S: IsSuccess>(mut self, f: impl FnOnce(&mut Self) -> S) -> S {
let result = f(&mut self);
if result.is_success() {
self.commit();
} else {
self.rollback();
}
result
}
}
impl<'a, T: Iterator + Clone> Iterator for Transaction<'a, T, AutoCommit> {
type Item = T::Item;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.original_iter.next()
}
}
#[derive(Clone)]
pub struct AutoCommit;
#[derive(Clone)]
pub struct Rollback;
#[deprecated = "use Rollback"]
#[doc(hidden)]
pub type Abort = Rollback;
#[derive(Clone)]
pub struct Panic;
impl Drop for Panic {
#[cold]
fn drop(&mut self) {
if !std::thread::panicking() {
#[cfg(debug_assertions)]
panic!(
"Transaction not committed or rolled back\n{}",
std::backtrace::Backtrace::force_capture()
);
#[cfg(not(debug_assertions))]
panic!("Transaction not committed or rolled back");
}
}
}
mod sealed {
pub trait Sealed {}
}
use sealed::Sealed;
impl Sealed for Panic {}
impl Sealed for Rollback {}
impl Sealed for AutoCommit {}
pub trait Policy: Clone + Sealed {}
impl Policy for Panic {}
impl Policy for Rollback {}
impl Policy for AutoCommit {}
pub trait IsSuccess {
fn is_success(&self) -> bool;
}
impl<T> IsSuccess for Option<T> {
#[inline]
fn is_success(&self) -> bool {
self.is_some()
}
}
impl<T, E> IsSuccess for Result<T, E> {
#[inline]
fn is_success(&self) -> bool {
self.is_ok()
}
}
impl IsSuccess for bool {
#[inline]
fn is_success(&self) -> bool {
*self
}
}
impl IsSuccess for () {
#[inline]
fn is_success(&self) -> bool {
true
}
}
#[test]
#[should_panic = "Transaction not committed or rolled back"]
fn test_transaction_uncommited_panics() {
let mut iter = vec![1, 2, 3].into_iter();
let mut transaction = Transaction::new(&mut iter, Panic);
assert_eq!(transaction.next(), Some(1));
drop(transaction);
assert_eq!(iter.next(), Some(1));
}
#[test]
fn test_transaction_rollback() {
let mut iter = vec![1, 2, 3].into_iter();
let mut transaction = Transaction::new(&mut iter, Panic);
assert_eq!(transaction.next(), Some(1));
transaction.rollback();
assert_eq!(iter.next(), Some(1));
}
#[test]
fn test_nested_transaction() {
let mut iter = vec![1, 2, 3].into_iter();
let mut transaction = Transaction::new(&mut iter, Panic);
assert_eq!(transaction.next(), Some(1));
let mut nested_transaction = transaction.nest();
assert_eq!(nested_transaction.next(), Some(2));
let mut nested_transaction2 = nested_transaction.nest();
assert_eq!(nested_transaction2.next(), Some(3));
nested_transaction2.commit();
nested_transaction.commit();
transaction.commit();
assert_eq!(iter.next(), None);
}