1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
use crate::assertion_hook::AssertionHook; use crate::epsilon_provider::EpsilonProvider; use crate::shoulda_equal::ShouldaEqual; use crate::Should; use std::any::Any; use std::fmt::Debug; use std::marker::PhantomData; use std::panic::{RefUnwindSafe, UnwindSafe}; pub struct Expression<T: RefUnwindSafe, F: FnOnce(&T) + UnwindSafe> { func: F, source: String, _a: PhantomData<T>, } impl<T: RefUnwindSafe, F: FnOnce(&T) + UnwindSafe> Expression<T, F> { pub fn new(func: F, source: String) -> Self { Self { func, source, _a: Default::default(), } } } pub trait Panicable<'a, T: Debug> { fn run(self, inner: &'a T) -> (Option<Box<dyn Any + Send + 'static>>, String); } impl<'a, T: RefUnwindSafe + Debug, F: FnOnce(&T) + UnwindSafe> Panicable<'a, T> for Expression<T, F> { fn run(self, inner: &'a T) -> (Option<Box<dyn Any + Send>>, String) { let (result, _) = self.func.run(inner); (result, self.source) } } impl<'a, T: RefUnwindSafe + Debug, F: FnOnce(&T) + UnwindSafe> Panicable<'a, T> for F { fn run(self, inner: &'a T) -> (Option<Box<dyn Any + Send>>, String) { let previous_hook = std::panic::take_hook(); std::panic::set_hook(Box::new(|_| {})); let result = std::panic::catch_unwind(|| self(inner)); std::panic::set_hook(previous_hook); (result.err(), String::from("anonymous closure")) } } impl<'a, Inner, Hook, Epsilon> Should<'a, Inner, Hook, Epsilon> where Inner: Debug, Inner: RefUnwindSafe, Hook: AssertionHook, Epsilon: EpsilonProvider, { pub fn panic<Expr: Panicable<'a, Inner>>(mut self, e: Expr) -> Self { let (result, message) = e.run(&self.inner); self.internal_assert(result.is_some(), format!("{} didnt panic", message)); self } pub fn panic_with<Expr: Panicable<'a, Inner>, S: AsRef<str>>( mut self, e: Expr, message: S, ) -> Self { let (result, assert_message) = e.run(&self.inner); self.internal_assert(result.is_some(), format!("{} didnt panic", assert_message)); match result { None => {} Some(x) => { let actual_message = x.downcast_ref::<String>().unwrap().as_str(); self.internal_assert( actual_message.should_eq::<Epsilon>(message.as_ref()), format!( "{} paniced with output {}, {} was expected", assert_message, actual_message, message.as_ref() ), ) } }; self } }