use crate::core::eval;
use crate::core::exec::ExecOutcome;
use crate::core::Element;
use crate::core::ElementKind;
use crate::error::{Result, TemplateError};
use crate::span::Span;
pub struct IfBlock {
pub cond: crate::core::Expr,
pub then: Vec<Element>,
pub else_: Option<Vec<Element>>,
pub span: Span,
}
impl IfBlock {
pub fn new(
cond: crate::core::Expr,
then: Vec<Element>,
else_: Option<Vec<Element>>,
span: Span,
) -> Self {
IfBlock {
cond,
then,
else_,
span,
}
}
pub(crate) fn exec(&self, env: &mut crate::core::Environment) -> Result<ExecOutcome> {
exec_if(env, self.span, &self.cond, &self.then, &self.else_)
}
}
fn blame_if_condition(
e: TemplateError,
env: &mut crate::core::Environment,
cond: &crate::core::Expr,
) -> TemplateError {
if let TemplateError::TypeMismatch { ctx, .. } = &e {
if ctx.blamer.is_none() {
return e.with_blame_at(
"#if",
"condition",
&crate::core::environment::expr_desc(cond),
&env.current_template_name,
cond.span,
);
}
}
crate::core::environment::attach_location(e, &env.current_template_name, cond.span)
}
fn exec_if(
env: &mut crate::core::Environment,
span: Span,
cond: &crate::core::Expr,
then: &[Element],
else_: &Option<Vec<Element>>,
) -> Result<ExecOutcome> {
let mut cur_span = span;
let mut cond = cond;
let mut then = then;
let mut else_ = else_;
loop {
let cm = eval::eval(env, cond).map_err(|e| {
crate::core::environment::attach_location(e, &env.current_template_name, cur_span)
})?;
let b = eval::model_to_boolean(env, &cm).map_err(|e| blame_if_condition(e, env, cond))?;
if b {
return Ok(ExecOutcome::Next(then.to_vec()));
}
match else_ {
Some(v) if v.len() == 1 => {
if let ElementKind::If {
cond: c2,
then: t2,
else_: e2,
} = &v[0].kind
{
cur_span = v[0].span;
cond = c2;
then = t2;
else_ = e2;
continue;
}
return Ok(ExecOutcome::Next(v.clone()));
}
Some(v) => return Ok(ExecOutcome::Next(v.clone())),
None => return Ok(ExecOutcome::Done),
}
}
}