use crate::*;
pub struct Stepper<O, S, T> {
name: &'static str,
init_func: InitFn<O, S, T>,
step_funcs: Arc<IndexMap<&'static str, StepFn<S, T>>>,
}
impl<O, S, T> Stepper<O, S, T> {
pub fn builder(name: &'static str) -> StepperBuilder<O, S, T> {
StepperBuilder::new(name)
}
pub fn run(&self, testable: Testable, options: O) -> T {
let namepath = testable.namepath().to_string();
log::debug!("Stepper '{}' {namepath}: init", self.name);
let init_fn = self.init_func;
let StepState(mut state, mut subject) = init_fn(&testable, options);
for (step_name, step_fn) in &*self.step_funcs {
log::debug!("Stepper '{}' {namepath}: {step_name}", self.name);
StepState(state, subject) = step_fn(&testable, state, subject);
}
subject
}
pub fn run_thru(&mut self, step: &'static str, testable: Testable, options: O) -> T {
let namepath = testable.namepath().to_string();
log::debug!("Stepper '{}' {namepath}: init", self.name);
let init_fn = self.init_func;
let StepState(mut state, mut subject) = init_fn(&testable, options);
if step == INIT {
return subject;
}
for (step_name, step_fn) in &*self.step_funcs {
log::debug!("Stepper '{}' {namepath}: {step_name}", self.name);
StepState(state, subject) = step_fn(&testable, state, subject);
if *step_name == step {
break;
}
}
subject
}
}
pub type InitFn<O, S, T> = fn(&Testable<'_, '_,'_>, O) -> StepState<S, T>;
pub type StepFn<S, T> = fn(&Testable<'_, '_,'_>, S, T) -> StepState<S, T>;
pub struct StepState<S, T>(pub S, pub T);
impl<S, T> StepState<S, T> {
pub fn state(self) -> S {
self.0
}
pub fn subject(self) -> T {
self.1
}
}
pub const INIT: &'static str = "init";
pub struct StepperBuilder<O, S, T> {
name: &'static str,
init_func: Option<InitFn<O, S, T>>,
steps_builder: Option<IndexMap<&'static str, StepFn<S, T>>>,
steps: Option<Arc<IndexMap<&'static str, StepFn<S, T>>>>,
}
impl<O, S, T> StepperBuilder<O, S, T> {
pub fn new(name: &'static str) -> Self {
Self {
name,
init_func: None,
steps_builder: Some(IndexMap::new()),
steps: None,
}
}
pub fn init(mut self, step: InitFn<O, S, T>) -> Self {
self.init_func = Some(step);
self
}
pub fn step(mut self, name: &'static str, step: StepFn<S, T>) -> Self {
debug_assert!(name != INIT, "Cannot use 'init' as a step name");
self.steps_builder.as_mut().expect("builder phase").insert(name, step);
self
}
pub fn finalize(mut self) -> Self {
if self.init_func.is_none() {
panic!("Stepper must have an initialization function defined");
} else if self.steps_builder.is_none() {
panic!("Stepper finalize already called");
}
self.steps = Some(Arc::new(self.steps_builder.take().expect("finalized")));
self
}
pub fn build(&self) -> Stepper<O, S, T> {
let init_func = self.init_func
.expect("Stepper must have an initialization function defined");
let step_funcs = Arc::clone(self.steps.as_ref().expect("Stepper must be finalized"));
Stepper {
name: self.name,
init_func,
step_funcs,
}
}
pub fn run(&self, testable: Testable, options: O) -> T {
self.build()
.run(testable, options)
}
pub fn run_thru(&mut self, step: &'static str, testable: Testable, options: O) -> T {
self.build()
.run_thru(step, testable, options)
}
}