pub struct Defer<F: FnOnce()> {
f: Option<F>,
}
impl<F: FnOnce()> Defer<F> {
pub fn new(f: F) -> Self {
Defer { f: Some(f) }
}
}
impl<F: FnOnce()> Drop for Defer<F> {
fn drop(&mut self) {
if let Some(f) = self.f.take() {
f();
}
}
}
#[macro_export]
macro_rules! defer {
($($tt:tt)*) => {
let _goish_defer = $crate::defer::Defer::new(|| { $($tt)* });
};
}
#[macro_export]
macro_rules! recover {
($($body:tt)*) => {{
let __result = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
$($body)*
}));
match __result {
::std::result::Result::Ok(_) => ::std::option::Option::<::std::string::String>::None,
::std::result::Result::Err(e) => {
let msg = if let ::std::option::Option::Some(s) = e.downcast_ref::<&str>() {
s.to_string()
} else if let ::std::option::Option::Some(s) = e.downcast_ref::<::std::string::String>() {
s.clone()
} else {
"unknown panic".to_string()
};
::std::option::Option::Some(msg)
}
}
}};
}
#[cfg(test)]
mod recover_tests {
#[test]
fn recover_returns_none_on_no_panic() {
let r = crate::recover!{ let _x = 1 + 1; };
assert!(r.is_none());
}
#[test]
fn recover_captures_panic_message() {
let r = crate::recover!{ panic!("boom"); };
assert_eq!(r.as_deref(), Some("boom"));
}
#[test]
fn recover_captures_string_panic() {
let r = crate::recover!{ panic!("{}", "formatted {}"); };
assert!(r.is_some());
}
#[test]
fn recover_matches_go_illegal_base_pattern() {
let r = crate::recover!{ crate::strconv::FormatUint(12345678, 1); };
assert!(r.is_some());
}
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
#[test]
fn runs_at_scope_end() {
let log: Arc<Mutex<Vec<i32>>> = Arc::new(Mutex::new(Vec::new()));
{
let log = log.clone();
crate::defer!{ log.lock().unwrap().push(99); }
log.lock().unwrap().push(1);
log.lock().unwrap().push(2);
}
assert_eq!(*log.lock().unwrap(), vec![1, 2, 99]);
}
#[test]
fn lifo_order() {
let log: Arc<Mutex<Vec<i32>>> = Arc::new(Mutex::new(Vec::new()));
{
let l1 = log.clone();
crate::defer!{ l1.lock().unwrap().push(1); }
let l2 = log.clone();
crate::defer!{ l2.lock().unwrap().push(2); }
let l3 = log.clone();
crate::defer!{ l3.lock().unwrap().push(3); }
}
assert_eq!(*log.lock().unwrap(), vec![3, 2, 1]);
}
#[test]
fn runs_on_early_return() {
fn inner(log: Arc<Mutex<Vec<i32>>>, early: bool) -> i32 {
let l = log.clone();
crate::defer!{ l.lock().unwrap().push(100); }
if early {
return 1;
}
log.lock().unwrap().push(50);
2
}
let log: Arc<Mutex<Vec<i32>>> = Arc::new(Mutex::new(Vec::new()));
let r = inner(log.clone(), true);
assert_eq!(r, 1);
assert_eq!(*log.lock().unwrap(), vec![100]);
let log: Arc<Mutex<Vec<i32>>> = Arc::new(Mutex::new(Vec::new()));
let r = inner(log.clone(), false);
assert_eq!(r, 2);
assert_eq!(*log.lock().unwrap(), vec![50, 100]);
}
}