use std::fmt::Display;
use nom::{
bytes::complete::tag,
character::complete::{char, multispace0},
error::context,
sequence::tuple,
};
use crate::xpath::{
grammar::{
recipes::Res,
whitespace_recipes::{sep, ws},
},
xpath_item_set::XpathItemSet,
ExpressionApplyError, XpathExpressionContext,
};
use super::{expr, expr_single, Expr, ExprSingle};
pub fn if_expr(input: &str) -> Res<&str, IfExpr> {
context(
"if_expr",
tuple((
ws((tag("if"), char('('), expr, char(')'))),
multispace0,
sep((tag("then"), expr_single, tag("else"), expr_single)),
)),
)(input)
.map(|(next_input, res)| {
(
next_input,
IfExpr {
condition: res.0 .2,
then: res.2 .1,
else_expr: res.2 .3,
},
)
})
}
#[derive(PartialEq, Debug, Clone)]
pub struct IfExpr {
pub condition: Expr,
pub then: ExprSingle,
pub else_expr: ExprSingle,
}
impl IfExpr {
pub(crate) fn eval<'tree>(
&self,
context: &XpathExpressionContext<'tree>,
) -> Result<XpathItemSet<'tree>, ExpressionApplyError> {
let condition_result = self.condition.eval(context)?;
if condition_result.boolean()? {
self.then.eval(context)
} else {
self.else_expr.eval(context)
}
}
}
impl Display for IfExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "if ({})", self.condition)?;
write!(f, " then {}", self.then)?;
write!(f, " else {}", self.else_expr)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn if_expr_should_parse() {
let input = r#"if($widget1/unit-cost<$widget2/unit-cost)then $widget1 else $widget2"#;
let (next_input, res) = if_expr(input).unwrap();
assert_eq!(next_input, "");
assert_eq!(
res.to_string(),
"if ($widget1/unit-cost<$widget2/unit-cost) then $widget1 else $widget2"
);
}
#[test]
fn if_expr_should_parse_whitespace() {
let input = r#"if ($widget1/unit-cost < $widget2/unit-cost)
then $widget1
else $widget2"#;
let (next_input, res) = if_expr(input).unwrap();
assert_eq!(next_input, "");
assert_eq!(
res.to_string(),
"if ($widget1/unit-cost<$widget2/unit-cost) then $widget1 else $widget2"
);
}
}