use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::{HelperDef, HelperResult};
use crate::json::value::JsonTruthy;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Helper, RenderContext, Renderable};
use crate::ScopedJson;
#[derive(Clone, Copy)]
pub struct IfHelper {
name: crate::HelperName,
positive: bool,
}
impl HelperDef for IfHelper {
fn call<'reg: 'rc, 'rc>(
&self,
h: &Helper<'rc>,
r: &'reg Registry<'reg>,
ctx: &'rc Context,
rc: &mut RenderContext<'reg, 'rc>,
out: &mut dyn Output,
) -> HelperResult {
let condition = h.param(0).ok_or_else(|| {
RenderError::new(format!(
"Param not found for helper \"{}\": condition",
self.name
))
})?;
let include_zero = h
.hash_get("include_zero")
.and_then(|v| v.value().as_bool())
.unwrap_or(false);
let mut condition = condition.value().is_truthy(include_zero);
if !self.positive {
condition = !condition;
}
let tmpl = if condition { h.template() } else { h.inverse() };
match tmpl {
Some(t) => t.render(r, ctx, rc, out),
None => Ok(()),
}
}
}
#[derive(Clone, Copy)]
pub struct IfTernaryHelper {
name: crate::HelperName,
positive: bool,
}
impl HelperDef for IfTernaryHelper {
fn call_inner<'reg: 'rc, 'rc>(
&self,
h: &Helper<'rc>,
_: &'reg Registry<'reg>,
_: &'rc Context,
_: &mut RenderContext<'reg, 'rc>,
) -> Result<ScopedJson<'rc>, RenderError> {
let condition = h.param(0).ok_or_else(|| {
RenderError::new(format!(
"Param not found for helper \"{}\": condition",
self.name
))
})?;
let true_value = h
.param(1)
.ok_or_else(|| {
RenderError::new(format!(
"Param not found for helper \"{}\": true_value",
self.name
))
})?
.value()
.clone();
let false_value = h
.param(2)
.ok_or_else(|| {
RenderError::new(format!(
"Param not found for helper \"{}\": false_value",
self.name
))
})?
.value()
.clone();
let include_zero = h
.hash_get("include_zero")
.and_then(|v| v.value().as_bool())
.unwrap_or(false);
let mut condition = condition.value().is_truthy(include_zero);
if !self.positive {
condition = !condition;
}
if condition {
return Ok(true_value.into());
}
return Ok(false_value.into());
}
}
pub static IF_HELPER: IfHelper = IfHelper {
name: crate::HelperName::If,
positive: true,
};
pub static IF_TERNARY_HELPER: IfTernaryHelper = IfTernaryHelper {
name: crate::HelperName::IfTernary,
positive: true,
};
pub static UNLESS_HELPER: IfHelper = IfHelper {
name: crate::HelperName::Unless,
positive: false,
};
pub static UNLESS_TERNARY_HELPER: IfTernaryHelper = IfTernaryHelper {
name: crate::HelperName::UnlessTernary,
positive: false,
};
#[cfg(test)]
mod test {
#[test]
fn test_if() {
assert_renders![
("{{#if true}}hello{{/if}}", "hello"),
("{{#unless true}}hello{{else}}world{{/unless}}", "world"),
];
}
#[test]
fn test_if_context() {
assert_renders![
("{{#if a.c.d}}hello {{a.b}}{{/if}}", "hello 99"),
("{{#with a}}{{#if c.d}}hello {{../a.b}}{{/if}}{{/with}}", "hello 99"),
@&json!({"a": {"b": 99, "c": {"d": true}}}),
];
}
#[test]
fn test_if_include_zero() {
assert_renders![
("{{#if 0}}1{{else}}0{{/if}}", "0"),
("{{#if 0 include_zero=true}}1{{else}}0{{/if}}", "1"),
("{{#if nan include_zero=true}}1{{else}}0{{/if}}", "0"),
@&json!({"nan": std::f64::NAN}),
];
}
#[test]
fn test_invisible_line_stripping() {
assert_renders![
("{{#if true}}\nyes\n{{/if}}\n", "yes\n"),
("{{#if true}}\r\nyes\r\n{{/if}}\r\n", "yes\r\n"),
("{{#if true}}x{{/if}}\ny", "x\ny"),
("{{#if a}}\nx\n{{^}}\ny\n{{/if}}\nz", "y\nz"),
];
}
#[test]
fn test_if_ternary() {
assert_renders![
(r#"{{if_ternary true "true" "false"}}"#, "true"),
(r#"{{if_ternary false "true" "false"}}"#, "false"),
(r#"{{if_ternary null "true" "false"}}"#, "false"),
(r#"{{if_ternary 0 "true" "false"}}"#, "false"),
(
r#"{{if_ternary include_zero=true 0 "true" "false"}}"#,
"true"
),
];
}
}