blob: eb85133b6cc2077139f1ca9752e75553fd10f266 [file] [log] [blame]
use super::block_util::create_block;
use crate::block::BlockParams;
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::RenderErrorReason;
#[derive(Clone, Copy)]
pub struct WithHelper;
impl HelperDef for WithHelper {
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 param = h
.param(0)
.ok_or(RenderErrorReason::ParamNotFoundForIndex("with", 0))?;
if param.value().is_truthy(false) {
let mut block = create_block(param);
if let Some(block_param) = h.block_param() {
let mut params = BlockParams::new();
if param.context_path().is_some() {
params.add_path(block_param, Vec::with_capacity(0))?;
} else {
params.add_value(block_param, param.value().clone())?;
}
block.set_block_params(params);
}
rc.push_block(block);
if let Some(t) = h.template() {
t.render(r, ctx, rc, out)?;
};
rc.pop_block();
Ok(())
} else if let Some(t) = h.inverse() {
t.render(r, ctx, rc, out)
} else if r.strict_mode() {
Err(RenderError::strict_error(param.relative_path()))
} else {
Ok(())
}
}
}
pub static WITH_HELPER: WithHelper = WithHelper;
#[cfg(test)]
mod test {
use crate::registry::Registry;
#[derive(Serialize)]
struct Address {
city: String,
country: String,
}
#[derive(Serialize)]
struct Person {
name: String,
age: i16,
addr: Address,
titles: Vec<String>,
}
#[test]
fn test_with() {
let addr = Address {
city: "Beijing".to_string(),
country: "China".to_string(),
};
let person = Person {
name: "Ning Sun".to_string(),
age: 27,
addr,
titles: vec!["programmer".to_string(), "cartographier".to_string()],
};
let mut handlebars = Registry::new();
assert!(handlebars
.register_template_string("t0", "{{#with addr}}{{city}}{{/with}}")
.is_ok());
assert!(handlebars
.register_template_string("t1", "{{#with notfound}}hello{{else}}world{{/with}}")
.is_ok());
assert!(handlebars
.register_template_string("t2", "{{#with addr/country}}{{this}}{{/with}}")
.is_ok());
let r0 = handlebars.render("t0", &person);
assert_eq!(r0.ok().unwrap(), "Beijing".to_string());
let r1 = handlebars.render("t1", &person);
assert_eq!(r1.ok().unwrap(), "world".to_string());
let r2 = handlebars.render("t2", &person);
assert_eq!(r2.ok().unwrap(), "China".to_string());
}
#[test]
fn test_with_block_param() {
let addr = Address {
city: "Beijing".to_string(),
country: "China".to_string(),
};
let person = Person {
name: "Ning Sun".to_string(),
age: 27,
addr,
titles: vec!["programmer".to_string(), "cartographier".to_string()],
};
let mut handlebars = Registry::new();
assert!(handlebars
.register_template_string("t0", "{{#with addr as |a|}}{{a.city}}{{/with}}")
.is_ok());
assert!(handlebars
.register_template_string("t1", "{{#with notfound as |c|}}hello{{else}}world{{/with}}")
.is_ok());
assert!(handlebars
.register_template_string("t2", "{{#with addr/country as |t|}}{{t}}{{/with}}")
.is_ok());
let r0 = handlebars.render("t0", &person);
assert_eq!(r0.ok().unwrap(), "Beijing".to_string());
let r1 = handlebars.render("t1", &person);
assert_eq!(r1.ok().unwrap(), "world".to_string());
let r2 = handlebars.render("t2", &person);
assert_eq!(r2.ok().unwrap(), "China".to_string());
}
#[test]
fn test_with_in_each() {
let addr = Address {
city: "Beijing".to_string(),
country: "China".to_string(),
};
let person = Person {
name: "Ning Sun".to_string(),
age: 27,
addr,
titles: vec!["programmer".to_string(), "cartographier".to_string()],
};
let addr2 = Address {
city: "Beijing".to_string(),
country: "China".to_string(),
};
let person2 = Person {
name: "Ning Sun".to_string(),
age: 27,
addr: addr2,
titles: vec!["programmer".to_string(), "cartographier".to_string()],
};
let people = vec![person, person2];
let mut handlebars = Registry::new();
assert!(handlebars
.register_template_string(
"t0",
"{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}"
)
.is_ok());
assert!(handlebars
.register_template_string(
"t1",
"{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}"
)
.is_ok());
assert!(handlebars
.register_template_string(
"t2",
"{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}"
)
.is_ok());
let r0 = handlebars.render("t0", &people);
assert_eq!(r0.ok().unwrap(), "BeijingBeijing".to_string());
let r1 = handlebars.render("t1", &people);
assert_eq!(r1.ok().unwrap(), "2727".to_string());
let r2 = handlebars.render("t2", &people);
assert_eq!(r2.ok().unwrap(), "01".to_string());
}
#[test]
fn test_path_up() {
let mut handlebars = Registry::new();
assert!(handlebars
.register_template_string("t0", "{{#with a}}{{#with b}}{{../../d}}{{/with}}{{/with}}")
.is_ok());
let data = json!({
"a": {
"b": [{"c": [1]}]
},
"d": 1
});
let r0 = handlebars.render("t0", &data);
assert_eq!(r0.ok().unwrap(), "1".to_string());
}
#[test]
fn test_else_context() {
let reg = Registry::new();
let template = "{{#with list}}A{{else}}{{foo}}{{/with}}";
let input = json!({"list": [], "foo": "bar"});
let rendered = reg.render_template(template, &input).unwrap();
assert_eq!("bar", rendered);
}
#[test]
fn test_derived_value() {
let hb = Registry::new();
let data = json!({"a": {"b": {"c": "d"}}});
let template = "{{#with (lookup a.b \"c\")}}{{this}}{{/with}}";
assert_eq!("d", hb.render_template(template, &data).unwrap());
}
#[test]
fn test_nested_derived_value() {
let hb = Registry::new();
let data = json!({"a": {"b": {"c": "d"}}});
let template = "{{#with (lookup a \"b\")}}{{#with this}}{{c}}{{/with}}{{/with}}";
assert_eq!("d", hb.render_template(template, &data).unwrap());
}
#[test]
fn test_strict_with() {
let mut hb = Registry::new();
assert_eq!(
hb.render_template("{{#with name}}yes{{/with}}", &json!({}))
.unwrap(),
""
);
assert_eq!(
hb.render_template("{{#with name}}yes{{else}}no{{/with}}", &json!({}))
.unwrap(),
"no"
);
hb.set_strict_mode(true);
assert!(hb
.render_template("{{#with name}}yes{{/with}}", &json!({}))
.is_err());
assert_eq!(
hb.render_template("{{#with name}}yes{{else}}no{{/with}}", &json!({}))
.unwrap(),
"no"
);
}
}