use std::any::Any;
use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::scenario::{Scenario, ScenarioContext};
use crate::types::StepResult;
pub struct ScenarioStep {
step_text: String,
func: Box<dyn Fn(&ScenarioContext, bool) -> StepResult>,
reg: Box<dyn Fn(&Scenario)>,
}
impl ScenarioStep {
pub fn new<F, R>(step_text: String, func: F, reg: R) -> Self
where
F: Fn(&ScenarioContext, bool) -> StepResult + 'static,
R: Fn(&Scenario) + 'static,
{
Self {
step_text,
func: Box::new(func),
reg: Box::new(reg),
}
}
fn render_panic(name: &str, err: Box<dyn Any + Send>) -> String {
if let Some(msg) = err.downcast_ref::<&str>() {
format!("step {} panic'd: {}", name, msg)
} else if let Some(msg) = err.downcast_ref::<String>() {
format!("step {} panic'd: {}", name, msg)
} else {
format!("step {} panic'd", name)
}
}
pub fn call(&self, context: &ScenarioContext, defuse_poison: bool) -> StepResult {
let func = AssertUnwindSafe(|| (*self.func)(context, defuse_poison));
catch_unwind(func).map_err(|e| Self::render_panic(self.step_text(), e))?
}
pub fn step_text(&self) -> &str {
&self.step_text
}
pub(crate) fn register_contexts(&self, scenario: &Scenario) {
(*self.reg)(scenario);
}
}