use core::fmt::{self, Display};
pub trait Plan<I> {
type Output;
fn launch(&self, inputs: &I) -> Result<Self::Output, Declined>;
fn tree(&self) -> PlanTree;
}
pub type BoxedPlan<I, O> = Box<dyn Plan<I, Output = O> + Send + Sync>;
impl<I, O> Plan<I> for BoxedPlan<I, O> {
type Output = O;
fn launch(&self, inputs: &I) -> Result<O, Declined> {
self.as_ref().launch(inputs)
}
fn tree(&self) -> PlanTree {
self.as_ref().tree()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Declined {
pub plan: String,
pub why: String,
}
impl Declined {
pub fn by<I>(plan: &impl Plan<I>, why: impl Display) -> Self {
Self {
plan: plan.tree().to_string(),
why: why.to_string(),
}
}
}
impl Display for Declined {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} declines: {}", self.plan, self.why)
}
}
impl std::error::Error for Declined {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PlanTree {
Routine(String),
Then { step: String, plan: Box<PlanTree> },
InOrder(Vec<PlanTree>),
Fastest(String),
}
impl PlanTree {
fn in_order(&self) -> Vec<&PlanTree> {
match self {
Self::InOrder(plans) => plans.iter().flat_map(Self::in_order).collect(),
other => vec![other],
}
}
}
impl Display for PlanTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Routine(name) => f.write_str(name),
Self::Then { step, plan } => write!(f, "{step}({plan})"),
Self::Fastest(table) => write!(f, "fastest({table})"),
Self::InOrder(_) => write_in_order(f, &self.in_order()),
}
}
}
fn write_in_order(f: &mut fmt::Formatter<'_>, plans: &[&PlanTree]) -> fmt::Result {
if let [one] = plans {
return write!(f, "{one}");
}
f.write_str("in-order[")?;
for (i, plan) in plans.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{plan}")?;
}
f.write_str("]")
}