Created
January 4, 2019 19:42
-
-
Save DutchGhost/f7c59eb9d0de0599b7f7c75a7fa5884a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::cell::RefCell; | |
trait Defer { | |
fn call(self: Box<Self>); | |
} | |
impl<F: FnOnce(T), T> Defer for (F, T) { | |
fn call(self: Box<Self>) { | |
(self.0)(self.1); | |
} | |
} | |
#[derive(Default)] | |
pub struct List<'a> { | |
inner: RefCell<Vec<Box<dyn Defer + 'a>>>, | |
} | |
unsafe fn transmute_lifetimes_mut<'a, 'b, T: ?Sized>(x: &'a mut T) -> &'b mut T { | |
std::mem::transmute(x) | |
} | |
impl<'a> List<'a> { | |
fn new() -> Self { | |
Self { | |
inner: RefCell::new(Vec::new()), | |
} | |
} | |
fn push<T: 'a>(&self, item: T, closure: impl FnOnce(T) + 'a) -> &'a mut T { | |
let mut def = Box::new((closure, item)); | |
let ret = unsafe { transmute_lifetimes_mut(&mut def.1) }; | |
self.inner.borrow_mut().push(def); | |
ret | |
} | |
fn execute(mut self) { | |
let v = std::mem::replace(self.inner.get_mut(), vec![]); | |
for d in v { | |
d.call(); | |
} | |
} | |
} | |
/// A guard is a handle to schedule callbacks on, from an outer scope. | |
#[derive(Default)] | |
pub struct Guard<'a> { | |
/// Callbacks to be run on a scope's success. | |
on_scope_success: List<'a>, | |
/// Callbacks to be run on a scope's failure. | |
on_scope_failure: List<'a>, | |
/// Callbacks to be run on a scope's exit. | |
on_scope_exit: List<'a>, | |
} | |
impl<'a> Guard<'a> { | |
/// Schedules a closure to run on a scope's success. | |
pub fn on_scope_success<T: 'a>(&self, item: T, closure: impl FnOnce(T) + 'a) -> &mut T { | |
self.on_scope_success.push(item, closure) | |
} | |
/// Schedules a closure to run on a scope's exit. | |
pub fn on_scope_exit<T: 'a>(&self, item: T, closure: impl FnOnce(T) + 'a) -> &mut T { | |
self.on_scope_exit.push(item, closure) | |
} | |
/// Schedules a closure to run on a scope's failure. | |
pub fn on_scope_failure<T: 'a>(&self, item: T, closure: impl FnOnce(T) + 'a) -> &mut T { | |
self.on_scope_failure.push(item, closure) | |
} | |
} | |
/// A trait to annotate whether a type is `success` or `failure`. | |
pub trait RetTrait { | |
fn is_error(&self) -> bool; | |
} | |
impl<T, E> RetTrait for Result<T, E> { | |
/// `Ok(T)` is success, `Err(E)` is failure. | |
fn is_error(&self) -> bool { | |
self.is_err() | |
} | |
} | |
impl<T> RetTrait for Option<T> { | |
/// `Some(T)` is success, `None` is failure. | |
fn is_error(&self) -> bool { | |
self.is_none() | |
} | |
} | |
/// Executes the scope `scope`. | |
/// A scope is a closure, in which access to a guard is granted. | |
/// A guard is used to schedule callbacks to run on a scope's success, failure, or exit, using | |
/// [`Guard::on_scope_success`], [`Guard::on_scope_failure`], [`Guard::on_scope_exit`]. | |
/// | |
/// Its important to note that callbacks schedules with [`Guard::on_scope_exit`] will *always* run, and will always run last. | |
/// | |
/// # Examples | |
/// ``` | |
/// use scoped::{Guard, scoped}; | |
/// | |
/// fn main() { | |
/// use std::cell::Cell; | |
/// | |
/// let mut number = Cell::new(0); | |
/// | |
/// scoped(|guard| -> Result<(), ()> { | |
/// | |
/// guard.on_scope_exit(&number, move |n| { | |
/// assert_eq!(n.get(), 2); | |
/// n.set(3); | |
/// }); | |
/// | |
/// guard.on_scope_success(&number, move |n| { | |
/// assert_eq!(n.get(), 1); | |
/// n.set(2); | |
/// }); | |
/// | |
/// number.set(1); | |
/// Ok(()) | |
/// }); | |
/// assert_eq!(number.get(), 3); | |
/// } | |
/// ``` | |
pub fn scoped<'a, R: RetTrait>(scope: impl FnOnce(&mut Guard<'a>) -> R + 'a) -> R { | |
let mut guard = Guard::default(); | |
let ret = scope(&mut guard); | |
if !ret.is_error() { | |
guard.on_scope_success.execute(); | |
} else { | |
guard.on_scope_failure.execute(); | |
} | |
guard.on_scope_exit.execute(); | |
ret | |
} | |
pub type ScopeResult<E> = Result<(), E>; | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_list() { | |
let scope = scoped(|guard| { | |
let v = guard.on_scope_success(vec![1, 2, 3, 4, 5], |mut v| { | |
println!("SUCCES!"); | |
assert_eq!(v, vec![1, 2, 3, 4, 5, 6, 7]); | |
v.push(10); | |
}); | |
let boxed = guard.on_scope_exit(Box::new(1), move |boxed| { | |
assert_eq!(*boxed, 12); | |
}); | |
v.push(6); | |
v.push(7); | |
**boxed = 12; | |
Some(10) | |
}); | |
} | |
#[test] | |
fn main_test() { | |
use std::cell::Cell; | |
let number = Cell::new(0); | |
let n = scoped(|guard| { | |
let n = Some(1); | |
guard.on_scope_success(&number, move |b| { | |
assert!(2 == b.get()); | |
b.set(0); | |
}); | |
guard.on_scope_failure(&number, move |b| { | |
b.set(-1); | |
}); | |
guard.on_scope_exit(&number, move |b| { | |
b.set(0); | |
}); | |
number.set(2); | |
n | |
}); | |
assert!(number.get() == 0); | |
assert_eq!(n, Some(1)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment