use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn a_list_runs_the_first_plan_that_serves_and_nothing_after() {
let after = Routine::serving("after", |_| true);
let list = InOrder::new(vec![
Routine::serving("declines", |_| false).boxed(),
Routine::serving("serves", |_| true).boxed(),
after.clone().boxed(),
]);
assert_eq!(list.launch(&1), Ok("serves".to_string()));
assert_eq!(after.launches(), 0);
}
#[test]
fn a_decline_at_launch_hands_the_list_on() {
let list = InOrder::new(vec![
Routine::serving("odd inputs only", |inputs| inputs % 2 == 1).boxed(),
Routine::serving("anything", |_| true).boxed(),
]);
assert_eq!(list.launch(&1), Ok("odd inputs only".to_string()));
assert_eq!(list.launch(&2), Ok("anything".to_string()));
}
#[test]
fn a_pin_declines_with_its_plans_reason() {
let pin = InOrder::new(vec![Routine::serving("pinned", |_| false).boxed()]);
assert_eq!(
pin.launch(&1),
Err(Declined {
plan: "pinned".to_string(),
why: "declines 1".to_string(),
})
);
}
#[test]
fn a_step_hands_its_plan_what_it_made_and_its_decline_stops_it() {
let after = Routine::serving("even inputs only", |inputs| inputs % 2 == 0);
let doubled = Then::new(Doubled, after.clone().boxed());
assert_eq!(doubled.launch(&3), Ok("even inputs only".to_string()));
assert_eq!(
doubled.launch(&0),
Err(Declined {
plan: "doubled".to_string(),
why: "nothing to double".to_string(),
})
);
assert_eq!(after.launches(), 1);
}
#[test]
fn plans_that_try_the_same_routines_print_alike() {
let nested = InOrder::new(vec![
Routine::serving("a", |_| true).boxed(),
Box::new(InOrder::new(vec![
Routine::serving("b", |_| true).boxed(),
Box::new(InOrder::new(vec![Routine::serving("c", |_| true).boxed()])),
])),
Box::new(Then::new(
Doubled,
Box::new(InOrder::new(vec![Box::new(Fastest::new(
"table",
|_: &u32| "fastest".to_string(),
))])),
)),
]);
assert_eq!(
nested.tree().to_string(),
"in-order[a, b, c, doubled(fastest(table))]"
);
}
#[derive(Clone)]
struct Routine {
name: &'static str,
serves: fn(&u32) -> bool,
launched: Arc<AtomicUsize>,
}
impl Routine {
fn serving(name: &'static str, serves: fn(&u32) -> bool) -> Self {
Self {
name,
serves,
launched: Arc::default(),
}
}
fn boxed(self) -> BoxedPlan<u32, String> {
Box::new(self)
}
fn launches(&self) -> usize {
self.launched.load(Ordering::Relaxed)
}
}
impl Plan<u32> for Routine {
type Output = String;
fn launch(&self, inputs: &u32) -> Result<String, Declined> {
if !(self.serves)(inputs) {
return Err(Declined::by(self, format!("declines {inputs}")));
}
self.launched.fetch_add(1, Ordering::Relaxed);
Ok(self.name.to_string())
}
fn tree(&self) -> PlanTree {
PlanTree::Routine(self.name.to_string())
}
}
struct Doubled;
impl Step<u32> for Doubled {
fn apply(&self, inputs: &u32) -> Result<u32, Declined> {
match inputs {
0 => Err(Declined {
plan: "doubled".to_string(),
why: "nothing to double".to_string(),
}),
n => Ok(n * 2),
}
}
fn name(&self) -> String {
"doubled".to_string()
}
}