summaryrefslogtreecommitdiffstats
path: root/vendor/handlebars/src/helpers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/handlebars/src/helpers
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--vendor/handlebars/src/helpers/block_util.rs17
-rw-r--r--vendor/handlebars/src/helpers/helper_each.rs593
-rw-r--r--vendor/handlebars/src/helpers/helper_extras.rs112
-rw-r--r--vendor/handlebars/src/helpers/helper_if.rs151
-rw-r--r--vendor/handlebars/src/helpers/helper_log.rs72
-rw-r--r--vendor/handlebars/src/helpers/helper_lookup.rs104
-rw-r--r--vendor/handlebars/src/helpers/helper_raw.rs44
-rw-r--r--vendor/handlebars/src/helpers/helper_with.rs276
-rw-r--r--vendor/handlebars/src/helpers/mod.rs291
-rw-r--r--vendor/handlebars/src/helpers/scripting.rs111
10 files changed, 1771 insertions, 0 deletions
diff --git a/vendor/handlebars/src/helpers/block_util.rs b/vendor/handlebars/src/helpers/block_util.rs
new file mode 100644
index 000000000..6971fdd8a
--- /dev/null
+++ b/vendor/handlebars/src/helpers/block_util.rs
@@ -0,0 +1,17 @@
+use crate::block::BlockContext;
+use crate::json::value::PathAndJson;
+
+pub(crate) fn create_block<'reg: 'rc, 'rc>(
+ param: &'rc PathAndJson<'reg, 'rc>,
+) -> BlockContext<'reg> {
+ let mut block = BlockContext::new();
+
+ if let Some(new_path) = param.context_path() {
+ *block.base_path_mut() = new_path.clone();
+ } else {
+ // use clone for now
+ block.set_base_value(param.value().clone());
+ }
+
+ block
+}
diff --git a/vendor/handlebars/src/helpers/helper_each.rs b/vendor/handlebars/src/helpers/helper_each.rs
new file mode 100644
index 000000000..4b76e7ce7
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_each.rs
@@ -0,0 +1,593 @@
+use serde_json::value::Value as Json;
+
+use super::block_util::create_block;
+use crate::block::{BlockContext, BlockParams};
+use crate::context::Context;
+use crate::error::RenderError;
+use crate::helpers::{HelperDef, HelperResult};
+use crate::json::value::to_json;
+use crate::output::Output;
+use crate::registry::Registry;
+use crate::render::{Helper, RenderContext, Renderable};
+use crate::util::copy_on_push_vec;
+
+fn update_block_context<'reg>(
+ block: &mut BlockContext<'reg>,
+ base_path: Option<&Vec<String>>,
+ relative_path: String,
+ is_first: bool,
+ value: &Json,
+) {
+ if let Some(ref p) = base_path {
+ if is_first {
+ *block.base_path_mut() = copy_on_push_vec(p, relative_path);
+ } else if let Some(ptr) = block.base_path_mut().last_mut() {
+ *ptr = relative_path;
+ }
+ } else {
+ block.set_base_value(value.clone());
+ }
+}
+
+fn set_block_param<'reg: 'rc, 'rc>(
+ block: &mut BlockContext<'reg>,
+ h: &Helper<'reg, 'rc>,
+ base_path: Option<&Vec<String>>,
+ k: &Json,
+ v: &Json,
+) -> Result<(), RenderError> {
+ if let Some(bp_val) = h.block_param() {
+ let mut params = BlockParams::new();
+ if base_path.is_some() {
+ params.add_path(bp_val, Vec::with_capacity(0))?;
+ } else {
+ params.add_value(bp_val, v.clone())?;
+ }
+
+ block.set_block_params(params);
+ } else if let Some((bp_val, bp_key)) = h.block_param_pair() {
+ let mut params = BlockParams::new();
+ if base_path.is_some() {
+ params.add_path(bp_val, Vec::with_capacity(0))?;
+ } else {
+ params.add_value(bp_val, v.clone())?;
+ }
+ params.add_value(bp_key, k.clone())?;
+
+ block.set_block_params(params);
+ }
+
+ Ok(())
+}
+
+#[derive(Clone, Copy)]
+pub struct EachHelper;
+
+impl HelperDef for EachHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ r: &'reg Registry<'reg>,
+ ctx: &'rc Context,
+ rc: &mut RenderContext<'reg, 'rc>,
+ out: &mut dyn Output,
+ ) -> HelperResult {
+ let value = h
+ .param(0)
+ .ok_or_else(|| RenderError::new("Param not found for helper \"each\""))?;
+
+ let template = h.template();
+
+ match template {
+ Some(t) => match *value.value() {
+ Json::Array(ref list)
+ if !list.is_empty() || (list.is_empty() && h.inverse().is_none()) =>
+ {
+ let block_context = create_block(&value);
+ rc.push_block(block_context);
+
+ let len = list.len();
+
+ let array_path = value.context_path();
+
+ for (i, v) in list.iter().enumerate().take(len) {
+ if let Some(ref mut block) = rc.block_mut() {
+ let is_first = i == 0usize;
+ let is_last = i == len - 1;
+
+ let index = to_json(i);
+ block.set_local_var("first", to_json(is_first));
+ block.set_local_var("last", to_json(is_last));
+ block.set_local_var("index", index.clone());
+
+ update_block_context(block, array_path, i.to_string(), is_first, &v);
+ set_block_param(block, h, array_path, &index, &v)?;
+ }
+
+ t.render(r, ctx, rc, out)?;
+ }
+
+ rc.pop_block();
+ Ok(())
+ }
+ Json::Object(ref obj)
+ if !obj.is_empty() || (obj.is_empty() && h.inverse().is_none()) =>
+ {
+ let block_context = create_block(&value);
+ rc.push_block(block_context);
+
+ let mut is_first = true;
+ let obj_path = value.context_path();
+
+ for (k, v) in obj.iter() {
+ if let Some(ref mut block) = rc.block_mut() {
+ let key = to_json(k);
+
+ block.set_local_var("first", to_json(is_first));
+ block.set_local_var("key", key.clone());
+
+ update_block_context(block, obj_path, k.to_string(), is_first, &v);
+ set_block_param(block, h, obj_path, &key, &v)?;
+ }
+
+ t.render(r, ctx, rc, out)?;
+
+ if is_first {
+ is_first = false;
+ }
+ }
+
+ rc.pop_block();
+ Ok(())
+ }
+ _ => {
+ if let Some(else_template) = h.inverse() {
+ else_template.render(r, ctx, rc, out)
+ } else if r.strict_mode() {
+ Err(RenderError::strict_error(value.relative_path()))
+ } else {
+ Ok(())
+ }
+ }
+ },
+ None => Ok(()),
+ }
+ }
+}
+
+pub static EACH_HELPER: EachHelper = EachHelper;
+
+#[cfg(test)]
+mod test {
+ use crate::json::value::to_json;
+ use crate::registry::Registry;
+ use serde_json::value::Value as Json;
+ use std::collections::BTreeMap;
+ use std::str::FromStr;
+
+ #[test]
+ fn test_empty_each() {
+ let mut hbs = Registry::new();
+ hbs.set_strict_mode(true);
+
+ let data = json!({
+ "a": [ ],
+ });
+
+ let template = "{{#each a}}each{{/each}}";
+ assert_eq!(hbs.render_template(template, &data).unwrap(), "");
+ }
+
+ #[test]
+ fn test_each() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string(
+ "t0",
+ "{{#each this}}{{@first}}|{{@last}}|{{@index}}:{{this}}|{{/each}}"
+ )
+ .is_ok());
+ assert!(handlebars
+ .register_template_string("t1", "{{#each this}}{{@first}}|{{@key}}:{{this}}|{{/each}}")
+ .is_ok());
+
+ let r0 = handlebars.render("t0", &vec![1u16, 2u16, 3u16]);
+ assert_eq!(
+ r0.ok().unwrap(),
+ "true|false|0:1|false|false|1:2|false|true|2:3|".to_string()
+ );
+
+ let mut m: BTreeMap<String, u16> = BTreeMap::new();
+ m.insert("ftp".to_string(), 21);
+ m.insert("http".to_string(), 80);
+ let r1 = handlebars.render("t1", &m);
+ assert_eq!(r1.ok().unwrap(), "true|ftp:21|false|http:80|".to_string());
+ }
+
+ #[test]
+ fn test_each_with_parent() {
+ let json_str = r#"{"a":{"a":99,"c":[{"d":100},{"d":200}]}}"#;
+
+ let data = Json::from_str(json_str).unwrap();
+ // println!("data: {}", data);
+ let mut handlebars = Registry::new();
+
+ // previously, to access the parent in an each block,
+ // a user would need to specify ../../b, as the path
+ // that is computed includes the array index: ./a.c.[0]
+ assert!(handlebars
+ .register_template_string("t0", "{{#each a.c}} d={{d}} b={{../a.a}} {{/each}}")
+ .is_ok());
+
+ let r1 = handlebars.render("t0", &data);
+ assert_eq!(r1.ok().unwrap(), " d=100 b=99 d=200 b=99 ".to_string());
+ }
+
+ #[test]
+ fn test_nested_each_with_parent() {
+ let json_str = r#"{"a": [{"b": [{"d": 100}], "c": 200}]}"#;
+
+ let data = Json::from_str(json_str).unwrap();
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string(
+ "t0",
+ "{{#each a}}{{#each b}}{{d}}:{{../c}}{{/each}}{{/each}}"
+ )
+ .is_ok());
+
+ let r1 = handlebars.render("t0", &data);
+ assert_eq!(r1.ok().unwrap(), "100:200".to_string());
+ }
+
+ #[test]
+ fn test_nested_each() {
+ let json_str = r#"{"a": [{"b": true}], "b": [[1, 2, 3],[4, 5]]}"#;
+
+ let data = Json::from_str(json_str).unwrap();
+
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string(
+ "t0",
+ "{{#each b}}{{#if ../a}}{{#each this}}{{this}}{{/each}}{{/if}}{{/each}}"
+ )
+ .is_ok());
+
+ let r1 = handlebars.render("t0", &data);
+ assert_eq!(r1.ok().unwrap(), "12345".to_string());
+ }
+
+ #[test]
+ fn test_nested_array() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each this.[0]}}{{this}}{{/each}}")
+ .is_ok());
+
+ let r0 = handlebars.render("t0", &(vec![vec![1, 2, 3]]));
+
+ assert_eq!(r0.ok().unwrap(), "123".to_string());
+ }
+
+ #[test]
+ fn test_empty_key() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each this}}{{@key}}-{{value}}\n{{/each}}")
+ .is_ok());
+
+ let r0 = handlebars
+ .render(
+ "t0",
+ &json!({
+ "foo": {
+ "value": "bar"
+ },
+ "": {
+ "value": "baz"
+ }
+ }),
+ )
+ .unwrap();
+
+ let mut r0_sp: Vec<_> = r0.split('\n').collect();
+ r0_sp.sort();
+
+ assert_eq!(r0_sp, vec!["", "-baz", "foo-bar"]);
+ }
+
+ #[test]
+ fn test_each_else() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each a}}1{{else}}empty{{/each}}")
+ .is_ok());
+ let m1 = btreemap! {
+ "a".to_string() => Vec::<String>::new(),
+ };
+ let r0 = handlebars.render("t0", &m1).unwrap();
+ assert_eq!(r0, "empty");
+
+ let m2 = btreemap! {
+ "b".to_string() => Vec::<String>::new()
+ };
+ let r1 = handlebars.render("t0", &m2).unwrap();
+ assert_eq!(r1, "empty");
+ }
+
+ #[test]
+ fn test_block_param() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each a as |i|}}{{i}}{{/each}}")
+ .is_ok());
+ let m1 = btreemap! {
+ "a".to_string() => vec![1,2,3,4,5]
+ };
+ let r0 = handlebars.render("t0", &m1).unwrap();
+ assert_eq!(r0, "12345");
+ }
+
+ #[test]
+ fn test_each_object_block_param() {
+ let mut handlebars = Registry::new();
+ let template = "{{#each this as |v k|}}\
+ {{#with k as |inner_k|}}{{inner_k}}{{/with}}:{{v}}|\
+ {{/each}}";
+ assert!(handlebars.register_template_string("t0", template).is_ok());
+
+ let m = btreemap! {
+ "ftp".to_string() => 21,
+ "http".to_string() => 80
+ };
+ let r0 = handlebars.render("t0", &m);
+ assert_eq!(r0.ok().unwrap(), "ftp:21|http:80|".to_string());
+ }
+
+ #[test]
+ fn test_each_object_block_param2() {
+ let mut handlebars = Registry::new();
+ let template = "{{#each this as |v k|}}\
+ {{#with v as |inner_v|}}{{k}}:{{inner_v}}{{/with}}|\
+ {{/each}}";
+
+ assert!(handlebars.register_template_string("t0", template).is_ok());
+
+ let m = btreemap! {
+ "ftp".to_string() => 21,
+ "http".to_string() => 80
+ };
+ let r0 = handlebars.render("t0", &m);
+ assert_eq!(r0.ok().unwrap(), "ftp:21|http:80|".to_string());
+ }
+
+ fn test_nested_each_with_path_ups() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string(
+ "t0",
+ "{{#each a.b}}{{#each c}}{{../../d}}{{/each}}{{/each}}"
+ )
+ .is_ok());
+
+ let data = btreemap! {
+ "a".to_string() => to_json(&btreemap! {
+ "b".to_string() => vec![btreemap!{"c".to_string() => vec![1]}]
+ }),
+ "d".to_string() => to_json(&1)
+ };
+
+ let r0 = handlebars.render("t0", &data);
+ assert_eq!(r0.ok().unwrap(), "1".to_string());
+ }
+
+ #[test]
+ fn test_nested_each_with_path_up_this() {
+ let mut handlebars = Registry::new();
+ let template = "{{#each variant}}{{#each ../typearg}}\
+ {{#if @first}}template<{{/if}}{{this}}{{#if @last}}>{{else}},{{/if}}\
+ {{/each}}{{/each}}";
+ assert!(handlebars.register_template_string("t0", template).is_ok());
+ let data = btreemap! {
+ "typearg".to_string() => vec!["T".to_string()],
+ "variant".to_string() => vec!["1".to_string(), "2".to_string()]
+ };
+ let r0 = handlebars.render("t0", &data);
+ assert_eq!(r0.ok().unwrap(), "template<T>template<T>".to_string());
+ }
+
+ #[test]
+ fn test_key_iteration_with_unicode() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each this}}{{@key}}: {{this}}\n{{/each}}")
+ .is_ok());
+ let data = json!({
+ "normal": 1,
+ "你好": 2,
+ "#special key": 3,
+ "😂": 4,
+ "me.dot.key": 5
+ });
+ let r0 = handlebars.render("t0", &data).ok().unwrap();
+ assert!(r0.contains("normal: 1"));
+ assert!(r0.contains("你好: 2"));
+ assert!(r0.contains("#special key: 3"));
+ assert!(r0.contains("😂: 4"));
+ assert!(r0.contains("me.dot.key: 5"));
+ }
+
+ #[test]
+ fn test_base_path_after_each() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each a}}{{this}}{{/each}} {{b}}")
+ .is_ok());
+ let data = json!({
+ "a": [1, 2, 3, 4],
+ "b": "good",
+ });
+
+ let r0 = handlebars.render("t0", &data).ok().unwrap();
+
+ assert_eq!("1234 good", r0);
+ }
+
+ #[test]
+ fn test_else_context() {
+ let reg = Registry::new();
+ let template = "{{#each list}}A{{else}}{{foo}}{{/each}}";
+ let input = json!({"list": [], "foo": "bar"});
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("bar", rendered);
+ }
+
+ #[test]
+ fn test_block_context_leak() {
+ let reg = Registry::new();
+ let template = "{{#each list}}{{#each inner}}{{this}}{{/each}}{{foo}}{{/each}}";
+ let input = json!({"list": [{"inner": [], "foo": 1}, {"inner": [], "foo": 2}]});
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("12", rendered);
+ }
+
+ #[test]
+ fn test_derived_array_as_block_params() {
+ handlebars_helper!(range: |x: u64| (0..x).collect::<Vec<u64>>());
+ let mut reg = Registry::new();
+ reg.register_helper("range", Box::new(range));
+ let template = "{{#each (range 3) as |i|}}{{i}}{{/each}}";
+ let input = json!(0);
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("012", rendered);
+ }
+
+ #[test]
+ fn test_derived_object_as_block_params() {
+ handlebars_helper!(point: |x: u64, y: u64| json!({"x":x, "y":y}));
+ let mut reg = Registry::new();
+ reg.register_helper("point", Box::new(point));
+ let template = "{{#each (point 0 1) as |i|}}{{i}}{{/each}}";
+ let input = json!(0);
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("01", rendered);
+ }
+
+ #[test]
+ fn test_derived_array_without_block_param() {
+ handlebars_helper!(range: |x: u64| (0..x).collect::<Vec<u64>>());
+ let mut reg = Registry::new();
+ reg.register_helper("range", Box::new(range));
+ let template = "{{#each (range 3)}}{{this}}{{/each}}";
+ let input = json!(0);
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("012", rendered);
+ }
+
+ #[test]
+ fn test_derived_object_without_block_params() {
+ handlebars_helper!(point: |x: u64, y: u64| json!({"x":x, "y":y}));
+ let mut reg = Registry::new();
+ reg.register_helper("point", Box::new(point));
+ let template = "{{#each (point 0 1)}}{{this}}{{/each}}";
+ let input = json!(0);
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("01", rendered);
+ }
+
+ #[test]
+ fn test_non_iterable() {
+ let reg = Registry::new();
+ let template = "{{#each this}}each block{{else}}else block{{/each}}";
+ let input = json!("strings aren't iterable");
+ let rendered = reg.render_template(template, &input).unwrap();
+ assert_eq!("else block", rendered);
+ }
+
+ #[test]
+ fn test_recursion() {
+ let mut reg = Registry::new();
+ assert!(reg
+ .register_template_string(
+ "walk",
+ "(\
+ {{#each this}}\
+ {{#if @key}}{{@key}}{{else}}{{@index}}{{/if}}: \
+ {{this}} \
+ {{> walk this}}, \
+ {{/each}}\
+ )",
+ )
+ .is_ok());
+
+ let input = json!({
+ "array": [42, {"wow": "cool"}, [[]]],
+ "object": { "a": { "b": "c", "d": ["e"] } },
+ "string": "hi"
+ });
+ let expected_output = "(\
+ array: [42, [object], [[], ], ] (\
+ 0: 42 (), \
+ 1: [object] (wow: cool (), ), \
+ 2: [[], ] (0: [] (), ), \
+ ), \
+ object: [object] (\
+ a: [object] (\
+ b: c (), \
+ d: [e, ] (0: e (), ), \
+ ), \
+ ), \
+ string: hi (), \
+ )";
+
+ let rendered = reg.render("walk", &input).unwrap();
+ assert_eq!(expected_output, rendered);
+ }
+
+ #[test]
+ fn test_strict_each() {
+ let mut reg = Registry::new();
+
+ assert!(reg
+ .render_template("{{#each data}}{{/each}}", &json!({}))
+ .is_ok());
+ assert!(reg
+ .render_template("{{#each data}}{{/each}}", &json!({"data": 24}))
+ .is_ok());
+
+ reg.set_strict_mode(true);
+
+ assert!(reg
+ .render_template("{{#each data}}{{/each}}", &json!({}))
+ .is_err());
+ assert!(reg
+ .render_template("{{#each data}}{{/each}}", &json!({"data": 24}))
+ .is_err());
+ assert!(reg
+ .render_template("{{#each data}}{{else}}food{{/each}}", &json!({}))
+ .is_ok());
+ assert!(reg
+ .render_template("{{#each data}}{{else}}food{{/each}}", &json!({"data": 24}))
+ .is_ok());
+ }
+
+ #[test]
+ fn newline_stripping_for_each() {
+ let reg = Registry::new();
+
+ let tpl = r#"<ul>
+ {{#each a}}
+ {{!-- comment --}}
+ <li>{{this}}</li>
+ {{/each}}
+</ul>"#;
+ assert_eq!(
+ r#"<ul>
+ <li>0</li>
+ <li>1</li>
+</ul>"#,
+ reg.render_template(tpl, &json!({"a": [0, 1]})).unwrap()
+ );
+ }
+}
diff --git a/vendor/handlebars/src/helpers/helper_extras.rs b/vendor/handlebars/src/helpers/helper_extras.rs
new file mode 100644
index 000000000..fe8f7a7d3
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_extras.rs
@@ -0,0 +1,112 @@
+//! Helpers for boolean operations
+use serde_json::Value as Json;
+
+use crate::json::value::JsonTruthy;
+
+handlebars_helper!(eq: |x: Json, y: Json| x == y);
+handlebars_helper!(ne: |x: Json, y: Json| x != y);
+handlebars_helper!(gt: |x: i64, y: i64| x > y);
+handlebars_helper!(gte: |x: i64, y: i64| x >= y);
+handlebars_helper!(lt: |x: i64, y: i64| x < y);
+handlebars_helper!(lte: |x: i64, y: i64| x <= y);
+handlebars_helper!(and: |x: Json, y: Json| x.is_truthy(false) && y.is_truthy(false));
+handlebars_helper!(or: |x: Json, y: Json| x.is_truthy(false) || y.is_truthy(false));
+handlebars_helper!(not: |x: Json| !x.is_truthy(false));
+handlebars_helper!(len: |x: Json| {
+ match x {
+ Json::Array(a) => a.len(),
+ Json::Object(m) => m.len(),
+ Json::String(s) => s.len(),
+ _ => 0
+ }
+});
+
+#[cfg(test)]
+mod test_conditions {
+ fn test_condition(condition: &str, expected: bool) {
+ let handlebars = crate::Handlebars::new();
+
+ let result = handlebars
+ .render_template(
+ &format!(
+ "{{{{#if {condition}}}}}lorem{{{{else}}}}ipsum{{{{/if}}}}",
+ condition = condition
+ ),
+ &json!({}),
+ )
+ .unwrap();
+ assert_eq!(&result, if expected { "lorem" } else { "ipsum" });
+ }
+
+ #[test]
+ fn foo() {
+ test_condition("(gt 5 3)", true);
+ test_condition("(gt 3 5)", false);
+ test_condition("(or (gt 3 5) (gt 5 3))", true);
+ test_condition("(not [])", true);
+ test_condition("(and null 4)", false);
+ }
+
+ #[test]
+ fn test_eq() {
+ test_condition("(eq 5 5)", true);
+ test_condition("(eq 5 6)", false);
+ test_condition(r#"(eq "foo" "foo")"#, true);
+ test_condition(r#"(eq "foo" "Foo")"#, false);
+ test_condition(r#"(eq [5] [5])"#, true);
+ test_condition(r#"(eq [5] [4])"#, false);
+ test_condition(r#"(eq 5 "5")"#, false);
+ test_condition(r#"(eq 5 [5])"#, false);
+ }
+
+ #[test]
+ fn test_ne() {
+ test_condition("(ne 5 6)", true);
+ test_condition("(ne 5 5)", false);
+ test_condition(r#"(ne "foo" "foo")"#, false);
+ test_condition(r#"(ne "foo" "Foo")"#, true);
+ }
+
+ #[test]
+ fn nested_conditions() {
+ let handlebars = crate::Handlebars::new();
+
+ let result = handlebars
+ .render_template("{{#if (gt 5 3)}}lorem{{else}}ipsum{{/if}}", &json!({}))
+ .unwrap();
+ assert_eq!(&result, "lorem");
+
+ let result = handlebars
+ .render_template(
+ "{{#if (not (gt 5 3))}}lorem{{else}}ipsum{{/if}}",
+ &json!({}),
+ )
+ .unwrap();
+ assert_eq!(&result, "ipsum");
+ }
+
+ #[test]
+ fn test_len() {
+ let handlebars = crate::Handlebars::new();
+
+ let result = handlebars
+ .render_template("{{len value}}", &json!({"value": [1,2,3]}))
+ .unwrap();
+ assert_eq!(&result, "3");
+
+ let result = handlebars
+ .render_template("{{len value}}", &json!({"value": {"a" :1, "b": 2}}))
+ .unwrap();
+ assert_eq!(&result, "2");
+
+ let result = handlebars
+ .render_template("{{len value}}", &json!({"value": "tomcat"}))
+ .unwrap();
+ assert_eq!(&result, "6");
+
+ let result = handlebars
+ .render_template("{{len value}}", &json!({"value": 3}))
+ .unwrap();
+ assert_eq!(&result, "0");
+ }
+}
diff --git a/vendor/handlebars/src/helpers/helper_if.rs b/vendor/handlebars/src/helpers/helper_if.rs
new file mode 100644
index 000000000..5a6e42fc0
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_if.rs
@@ -0,0 +1,151 @@
+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};
+
+#[derive(Clone, Copy)]
+pub struct IfHelper {
+ positive: bool,
+}
+
+impl HelperDef for IfHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, '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_else(|| RenderError::new("Param not found for helper \"if\""))?;
+ let include_zero = h
+ .hash_get("includeZero")
+ .and_then(|v| v.value().as_bool())
+ .unwrap_or(false);
+
+ let mut value = param.value().is_truthy(include_zero);
+
+ if !self.positive {
+ value = !value;
+ }
+
+ let tmpl = if value { h.template() } else { h.inverse() };
+ match tmpl {
+ Some(ref t) => t.render(r, ctx, rc, out),
+ None => Ok(()),
+ }
+ }
+}
+
+pub static IF_HELPER: IfHelper = IfHelper { positive: true };
+pub static UNLESS_HELPER: IfHelper = IfHelper { positive: false };
+
+#[cfg(test)]
+mod test {
+ use crate::helpers::WITH_HELPER;
+ use crate::registry::Registry;
+ use serde_json::value::Value as Json;
+ use std::str::FromStr;
+
+ #[test]
+ fn test_if() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#if this}}hello{{/if}}")
+ .is_ok());
+ assert!(handlebars
+ .register_template_string("t1", "{{#unless this}}hello{{else}}world{{/unless}}")
+ .is_ok());
+
+ let r0 = handlebars.render("t0", &true);
+ assert_eq!(r0.ok().unwrap(), "hello".to_string());
+
+ let r1 = handlebars.render("t1", &true);
+ assert_eq!(r1.ok().unwrap(), "world".to_string());
+
+ let r2 = handlebars.render("t0", &false);
+ assert_eq!(r2.ok().unwrap(), "".to_string());
+ }
+
+ #[test]
+ fn test_if_context() {
+ let json_str = r#"{"a":{"b":99,"c":{"d": true}}}"#;
+ let data = Json::from_str(json_str).unwrap();
+
+ let mut handlebars = Registry::new();
+ handlebars.register_helper("with", Box::new(WITH_HELPER));
+ assert!(handlebars
+ .register_template_string("t0", "{{#if a.c.d}}hello {{a.b}}{{/if}}")
+ .is_ok());
+ assert!(handlebars
+ .register_template_string(
+ "t1",
+ "{{#with a}}{{#if c.d}}hello {{../a.b}}{{/if}}{{/with}}"
+ )
+ .is_ok());
+
+ let r0 = handlebars.render("t0", &data);
+ assert_eq!(r0.unwrap(), "hello 99".to_string());
+
+ let r1 = handlebars.render("t1", &data);
+ assert_eq!(r1.unwrap(), "hello 99".to_string());
+ }
+
+ #[test]
+ fn test_if_include_zero() {
+ use std::f64;
+ let handlebars = Registry::new();
+
+ assert_eq!(
+ "0".to_owned(),
+ handlebars
+ .render_template("{{#if a}}1{{else}}0{{/if}}", &json!({"a": 0}))
+ .unwrap()
+ );
+ assert_eq!(
+ "1".to_owned(),
+ handlebars
+ .render_template(
+ "{{#if a includeZero=true}}1{{else}}0{{/if}}",
+ &json!({"a": 0})
+ )
+ .unwrap()
+ );
+ assert_eq!(
+ "0".to_owned(),
+ handlebars
+ .render_template(
+ "{{#if a includeZero=true}}1{{else}}0{{/if}}",
+ &json!({ "a": f64::NAN })
+ )
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn test_invisible_line_stripping() {
+ let hbs = Registry::new();
+ assert_eq!(
+ "yes\n",
+ hbs.render_template("{{#if a}}\nyes\n{{/if}}\n", &json!({"a": true}))
+ .unwrap()
+ );
+
+ assert_eq!(
+ "x\ny",
+ hbs.render_template("{{#if a}}x{{/if}}\ny", &json!({"a": true}))
+ .unwrap()
+ );
+
+ assert_eq!(
+ "y\nz",
+ hbs.render_template("{{#if a}}\nx\n{{^}}\ny\n{{/if}}\nz", &json!({"a": false}))
+ .unwrap()
+ );
+ }
+}
diff --git a/vendor/handlebars/src/helpers/helper_log.rs b/vendor/handlebars/src/helpers/helper_log.rs
new file mode 100644
index 000000000..5821efa35
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_log.rs
@@ -0,0 +1,72 @@
+use crate::context::Context;
+#[cfg(not(feature = "no_logging"))]
+use crate::error::RenderError;
+use crate::helpers::{HelperDef, HelperResult};
+#[cfg(not(feature = "no_logging"))]
+use crate::json::value::JsonRender;
+use crate::output::Output;
+use crate::registry::Registry;
+use crate::render::{Helper, RenderContext};
+#[cfg(not(feature = "no_logging"))]
+use log::Level;
+#[cfg(not(feature = "no_logging"))]
+use std::str::FromStr;
+
+#[derive(Clone, Copy)]
+pub struct LogHelper;
+
+#[cfg(not(feature = "no_logging"))]
+impl HelperDef for LogHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ _: &'reg Registry<'reg>,
+ _: &'rc Context,
+ _: &mut RenderContext<'reg, 'rc>,
+ _: &mut dyn Output,
+ ) -> HelperResult {
+ let param_to_log = h
+ .params()
+ .iter()
+ .map(|p| {
+ if let Some(ref relative_path) = p.relative_path() {
+ format!("{}: {}", relative_path, p.value().render())
+ } else {
+ p.value().render()
+ }
+ })
+ .collect::<Vec<String>>()
+ .join(", ");
+
+ let level = h
+ .hash_get("level")
+ .and_then(|v| v.value().as_str())
+ .unwrap_or("info");
+
+ if let Ok(log_level) = Level::from_str(level) {
+ log!(log_level, "{}", param_to_log)
+ } else {
+ return Err(RenderError::new(&format!(
+ "Unsupported logging level {}",
+ level
+ )));
+ }
+ Ok(())
+ }
+}
+
+#[cfg(feature = "no_logging")]
+impl HelperDef for LogHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ _: &Helper<'reg, 'rc>,
+ _: &Registry<'reg>,
+ _: &Context,
+ _: &mut RenderContext<'reg, 'rc>,
+ _: &mut dyn Output,
+ ) -> HelperResult {
+ Ok(())
+ }
+}
+
+pub static LOG_HELPER: LogHelper = LogHelper;
diff --git a/vendor/handlebars/src/helpers/helper_lookup.rs b/vendor/handlebars/src/helpers/helper_lookup.rs
new file mode 100644
index 000000000..bf887debe
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_lookup.rs
@@ -0,0 +1,104 @@
+use serde_json::value::Value as Json;
+
+use crate::context::Context;
+use crate::error::RenderError;
+use crate::helpers::HelperDef;
+use crate::json::value::ScopedJson;
+use crate::registry::Registry;
+use crate::render::{Helper, RenderContext};
+
+#[derive(Clone, Copy)]
+pub struct LookupHelper;
+
+impl HelperDef for LookupHelper {
+ fn call_inner<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ r: &'reg Registry<'reg>,
+ _: &'rc Context,
+ _: &mut RenderContext<'reg, 'rc>,
+ ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
+ let collection_value = h
+ .param(0)
+ .ok_or_else(|| RenderError::new("Param not found for helper \"lookup\""))?;
+ let index = h
+ .param(1)
+ .ok_or_else(|| RenderError::new("Insufficient params for helper \"lookup\""))?;
+
+ let value = match *collection_value.value() {
+ Json::Array(ref v) => index
+ .value()
+ .as_u64()
+ .and_then(|u| v.get(u as usize))
+ .unwrap_or(&Json::Null),
+ Json::Object(ref m) => index
+ .value()
+ .as_str()
+ .and_then(|k| m.get(k))
+ .unwrap_or(&Json::Null),
+ _ => &Json::Null,
+ };
+ if r.strict_mode() && value.is_null() {
+ Err(RenderError::strict_error(None))
+ } else {
+ Ok(value.clone().into())
+ }
+ }
+}
+
+pub static LOOKUP_HELPER: LookupHelper = LookupHelper;
+
+#[cfg(test)]
+mod test {
+ use crate::registry::Registry;
+
+ use std::collections::BTreeMap;
+
+ #[test]
+ fn test_lookup() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{#each v1}}{{lookup ../v2 @index}}{{/each}}")
+ .is_ok());
+ assert!(handlebars
+ .register_template_string("t1", "{{#each v1}}{{lookup ../v2 1}}{{/each}}")
+ .is_ok());
+ assert!(handlebars
+ .register_template_string("t2", "{{lookup kk \"a\"}}")
+ .is_ok());
+
+ let mut m: BTreeMap<String, Vec<u16>> = BTreeMap::new();
+ m.insert("v1".to_string(), vec![1u16, 2u16, 3u16]);
+ m.insert("v2".to_string(), vec![9u16, 8u16, 7u16]);
+
+ let m2 = btreemap! {
+ "kk".to_string() => btreemap!{"a".to_string() => "world".to_string()}
+ };
+
+ let r0 = handlebars.render("t0", &m);
+ assert_eq!(r0.ok().unwrap(), "987".to_string());
+
+ let r1 = handlebars.render("t1", &m);
+ assert_eq!(r1.ok().unwrap(), "888".to_string());
+
+ let r2 = handlebars.render("t2", &m2);
+ assert_eq!(r2.ok().unwrap(), "world".to_string());
+ }
+
+ #[test]
+ fn test_strict_lookup() {
+ let mut hbs = Registry::new();
+
+ assert_eq!(
+ hbs.render_template("{{lookup kk 1}}", &json!({"kk": []}))
+ .unwrap(),
+ ""
+ );
+
+ hbs.set_strict_mode(true);
+
+ assert!(hbs
+ .render_template("{{lookup kk 1}}", &json!({"kk": []}))
+ .is_err());
+ }
+}
diff --git a/vendor/handlebars/src/helpers/helper_raw.rs b/vendor/handlebars/src/helpers/helper_raw.rs
new file mode 100644
index 000000000..55aef6bfc
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_raw.rs
@@ -0,0 +1,44 @@
+use crate::context::Context;
+use crate::helpers::{HelperDef, HelperResult};
+use crate::output::Output;
+use crate::registry::Registry;
+use crate::render::{Helper, RenderContext, Renderable};
+
+#[derive(Clone, Copy)]
+pub struct RawHelper;
+
+impl HelperDef for RawHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ r: &'reg Registry<'reg>,
+ ctx: &'rc Context,
+ rc: &mut RenderContext<'reg, 'rc>,
+ out: &mut dyn Output,
+ ) -> HelperResult {
+ let tpl = h.template();
+ if let Some(t) = tpl {
+ t.render(r, ctx, rc, out)
+ } else {
+ Ok(())
+ }
+ }
+}
+
+pub static RAW_HELPER: RawHelper = RawHelper;
+
+#[cfg(test)]
+mod test {
+ use crate::registry::Registry;
+
+ #[test]
+ fn test_raw_helper() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "a{{{{raw}}}}{{content}}{{else}}hello{{{{/raw}}}}")
+ .is_ok());
+
+ let r = handlebars.render("t0", &());
+ assert_eq!(r.ok().unwrap(), "a{{content}}{{else}}hello");
+ }
+}
diff --git a/vendor/handlebars/src/helpers/helper_with.rs b/vendor/handlebars/src/helpers/helper_with.rs
new file mode 100644
index 000000000..c4d31cd0e
--- /dev/null
+++ b/vendor/handlebars/src/helpers/helper_with.rs
@@ -0,0 +1,276 @@
+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};
+
+#[derive(Clone, Copy)]
+pub struct WithHelper;
+
+impl HelperDef for WithHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, '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_else(|| RenderError::new("Param not found for helper \"with\""))?;
+
+ 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::json::value::to_json;
+ 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 = btreemap! {
+ "a".to_string() => to_json(&btreemap! {
+ "b".to_string() => vec![btreemap!{"c".to_string() => vec![1]}]
+ }),
+ "d".to_string() => to_json(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"
+ );
+ }
+}
diff --git a/vendor/handlebars/src/helpers/mod.rs b/vendor/handlebars/src/helpers/mod.rs
new file mode 100644
index 000000000..bfd50c1f4
--- /dev/null
+++ b/vendor/handlebars/src/helpers/mod.rs
@@ -0,0 +1,291 @@
+use crate::context::Context;
+use crate::error::RenderError;
+use crate::json::value::ScopedJson;
+use crate::output::Output;
+use crate::registry::Registry;
+use crate::render::{do_escape, Helper, RenderContext};
+
+pub use self::helper_each::EACH_HELPER;
+pub use self::helper_if::{IF_HELPER, UNLESS_HELPER};
+pub use self::helper_log::LOG_HELPER;
+pub use self::helper_lookup::LOOKUP_HELPER;
+pub use self::helper_raw::RAW_HELPER;
+pub use self::helper_with::WITH_HELPER;
+
+/// A type alias for `Result<(), RenderError>`
+pub type HelperResult = Result<(), RenderError>;
+
+/// Helper Definition
+///
+/// Implement `HelperDef` to create custom helpers. You can retrieve useful information from these arguments.
+///
+/// * `&Helper`: current helper template information, contains name, params, hashes and nested template
+/// * `&Registry`: the global registry, you can find templates by name from registry
+/// * `&Context`: the whole data to render, in most case you can use data from `Helper`
+/// * `&mut RenderContext`: you can access data or modify variables (starts with @)/partials in render context, for example, @index of #each. See its document for detail.
+/// * `&mut dyn Output`: where you write output to
+///
+/// By default, you can use a bare function as a helper definition because we have supported unboxed_closure. If you have stateful or configurable helper, you can create a struct to implement `HelperDef`.
+///
+/// ## Define an inline helper
+///
+/// ```
+/// use handlebars::*;
+///
+/// fn upper(h: &Helper<'_, '_>, _: &Handlebars<'_>, _: &Context, rc: &mut RenderContext<'_, '_>, out: &mut Output)
+/// -> HelperResult {
+/// // get parameter from helper or throw an error
+/// let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
+/// out.write(param.to_uppercase().as_ref())?;
+/// Ok(())
+/// }
+/// ```
+///
+/// ## Define block helper
+///
+/// Block helper is like `#if` or `#each` which has a inner template and an optional *inverse* template (the template in else branch). You can access the inner template by `helper.template()` and `helper.inverse()`. In most cases you will just call `render` on it.
+///
+/// ```
+/// use handlebars::*;
+///
+/// fn dummy_block<'reg, 'rc>(
+/// h: &Helper<'reg, 'rc>,
+/// r: &'reg Handlebars<'reg>,
+/// ctx: &'rc Context,
+/// rc: &mut RenderContext<'reg, 'rc>,
+/// out: &mut dyn Output,
+/// ) -> HelperResult {
+/// h.template()
+/// .map(|t| t.render(r, ctx, rc, out))
+/// .unwrap_or(Ok(()))
+/// }
+/// ```
+///
+/// ## Define helper function using macro
+///
+/// In most cases you just need some simple function to call from templates. We have a `handlebars_helper!` macro to simplify the job.
+///
+/// ```
+/// use handlebars::*;
+///
+/// handlebars_helper!(plus: |x: i64, y: i64| x + y);
+///
+/// let mut hbs = Handlebars::new();
+/// hbs.register_helper("plus", Box::new(plus));
+/// ```
+///
+pub trait HelperDef {
+ /// A simplified api to define helper
+ ///
+ /// To implement your own `call_inner`, you will return a new `ScopedJson`
+ /// which has a JSON value computed from current context.
+ ///
+ /// ### Calling from subexpression
+ ///
+ /// When calling the helper as a subexpression, the value and its type can
+ /// be received by upper level helpers.
+ ///
+ /// Note that the value can be `json!(null)` which is treated as `false` in
+ /// helpers like `if` and rendered as empty string.
+ fn call_inner<'reg: 'rc, 'rc>(
+ &self,
+ _: &Helper<'reg, 'rc>,
+ _: &'reg Registry<'reg>,
+ _: &'rc Context,
+ _: &mut RenderContext<'reg, 'rc>,
+ ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
+ Err(RenderError::unimplemented())
+ }
+
+ /// A complex version of helper interface.
+ ///
+ /// This function offers `Output`, which you can write custom string into
+ /// and render child template. Helpers like `if` and `each` are implemented
+ /// with this. Because the data written into `Output` are typically without
+ /// type information. So helpers defined by this function are not composable.
+ ///
+ /// ### Calling from subexpression
+ ///
+ /// Although helpers defined by this are not composable, when called from
+ /// subexpression, handlebars tries to parse the string output as JSON to
+ /// re-build its type. This can be buggy with numrical and other literal values.
+ /// So it is not recommended to use these helpers in subexpression.
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ r: &'reg Registry<'reg>,
+ ctx: &'rc Context,
+ rc: &mut RenderContext<'reg, 'rc>,
+ out: &mut dyn Output,
+ ) -> HelperResult {
+ match self.call_inner(h, r, ctx, rc) {
+ Ok(result) => {
+ if r.strict_mode() && result.is_missing() {
+ Err(RenderError::strict_error(None))
+ } else {
+ // auto escape according to settings
+ let output = do_escape(r, rc, result.render());
+ out.write(output.as_ref())?;
+ Ok(())
+ }
+ }
+ Err(e) => {
+ if e.is_unimplemented() {
+ // default implementation, do nothing
+ Ok(())
+ } else {
+ Err(e)
+ }
+ }
+ }
+ }
+}
+
+/// implement HelperDef for bare function so we can use function as helper
+impl<
+ F: for<'reg, 'rc> Fn(
+ &Helper<'reg, 'rc>,
+ &'reg Registry<'reg>,
+ &'rc Context,
+ &mut RenderContext<'reg, 'rc>,
+ &mut dyn Output,
+ ) -> HelperResult,
+ > HelperDef for F
+{
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ r: &'reg Registry<'reg>,
+ ctx: &'rc Context,
+ rc: &mut RenderContext<'reg, 'rc>,
+ out: &mut dyn Output,
+ ) -> HelperResult {
+ (*self)(h, r, ctx, rc, out)
+ }
+}
+
+mod block_util;
+mod helper_each;
+pub(crate) mod helper_extras;
+mod helper_if;
+mod helper_log;
+mod helper_lookup;
+mod helper_raw;
+mod helper_with;
+#[cfg(feature = "script_helper")]
+pub(crate) mod scripting;
+
+// pub type HelperDef = for <'a, 'b, 'c> Fn<(&'a Context, &'b Helper, &'b Registry, &'c mut RenderContext), Result<String, RenderError>>;
+//
+// pub fn helper_dummy (ctx: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<String, RenderError> {
+// h.template().unwrap().render(ctx, r, rc).unwrap()
+// }
+//
+#[cfg(test)]
+mod test {
+ use std::collections::BTreeMap;
+
+ use crate::context::Context;
+ use crate::error::RenderError;
+ use crate::helpers::HelperDef;
+ use crate::json::value::JsonRender;
+ use crate::output::Output;
+ use crate::registry::Registry;
+ use crate::render::{Helper, RenderContext, Renderable};
+
+ #[derive(Clone, Copy)]
+ struct MetaHelper;
+
+ impl HelperDef for MetaHelper {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ r: &'reg Registry<'reg>,
+ ctx: &'rc Context,
+ rc: &mut RenderContext<'reg, 'rc>,
+ out: &mut dyn Output,
+ ) -> Result<(), RenderError> {
+ let v = h.param(0).unwrap();
+
+ if !h.is_block() {
+ let output = format!("{}:{}", h.name(), v.value().render());
+ out.write(output.as_ref())?;
+ } else {
+ let output = format!("{}:{}", h.name(), v.value().render());
+ out.write(output.as_ref())?;
+ out.write("->")?;
+ h.template().unwrap().render(r, ctx, rc, out)?;
+ };
+ Ok(())
+ }
+ }
+
+ #[test]
+ fn test_meta_helper() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t0", "{{foo this}}")
+ .is_ok());
+ assert!(handlebars
+ .register_template_string("t1", "{{#bar this}}nice{{/bar}}")
+ .is_ok());
+
+ let meta_helper = MetaHelper;
+ handlebars.register_helper("helperMissing", Box::new(meta_helper));
+ handlebars.register_helper("blockHelperMissing", Box::new(meta_helper));
+
+ let r0 = handlebars.render("t0", &true);
+ assert_eq!(r0.ok().unwrap(), "foo:true".to_string());
+
+ let r1 = handlebars.render("t1", &true);
+ assert_eq!(r1.ok().unwrap(), "bar:true->nice".to_string());
+ }
+
+ #[test]
+ fn test_helper_for_subexpression() {
+ let mut handlebars = Registry::new();
+ assert!(handlebars
+ .register_template_string("t2", "{{foo value=(bar 0)}}")
+ .is_ok());
+
+ handlebars.register_helper(
+ "helperMissing",
+ Box::new(
+ |h: &Helper<'_, '_>,
+ _: &Registry<'_>,
+ _: &Context,
+ _: &mut RenderContext<'_, '_>,
+ out: &mut dyn Output|
+ -> Result<(), RenderError> {
+ let output = format!("{}{}", h.name(), h.param(0).unwrap().value());
+ out.write(output.as_ref())?;
+ Ok(())
+ },
+ ),
+ );
+ handlebars.register_helper(
+ "foo",
+ Box::new(
+ |h: &Helper<'_, '_>,
+ _: &Registry<'_>,
+ _: &Context,
+ _: &mut RenderContext<'_, '_>,
+ out: &mut dyn Output|
+ -> Result<(), RenderError> {
+ let output = format!("{}", h.hash_get("value").unwrap().value().render());
+ out.write(output.as_ref())?;
+ Ok(())
+ },
+ ),
+ );
+
+ let mut data = BTreeMap::new();
+ // handlebars should never try to lookup this value because
+ // subexpressions are now resolved as string literal
+ data.insert("bar0".to_string(), true);
+
+ let r2 = handlebars.render("t2", &data);
+
+ assert_eq!(r2.ok().unwrap(), "bar0".to_string());
+ }
+}
diff --git a/vendor/handlebars/src/helpers/scripting.rs b/vendor/handlebars/src/helpers/scripting.rs
new file mode 100644
index 000000000..cec3b9763
--- /dev/null
+++ b/vendor/handlebars/src/helpers/scripting.rs
@@ -0,0 +1,111 @@
+use std::collections::{BTreeMap, HashMap};
+
+use crate::context::Context;
+use crate::error::RenderError;
+use crate::helpers::HelperDef;
+use crate::json::value::{PathAndJson, ScopedJson};
+use crate::registry::Registry;
+use crate::render::{Helper, RenderContext};
+
+use rhai::serde::{from_dynamic, to_dynamic};
+use rhai::{Dynamic, Engine, Scope, AST};
+
+use serde_json::value::Value as Json;
+
+pub(crate) struct ScriptHelper {
+ pub(crate) script: AST,
+}
+
+#[inline]
+fn call_script_helper<'reg: 'rc, 'rc>(
+ params: &[PathAndJson<'reg, 'rc>],
+ hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
+ engine: &Engine,
+ script: &AST,
+) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
+ let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())?;
+
+ let hash: Dynamic = to_dynamic(
+ hash.iter()
+ .map(|(k, v)| ((*k).to_owned(), v.value()))
+ .collect::<HashMap<String, &Json>>(),
+ )?;
+
+ let mut scope = Scope::new();
+ scope.push_dynamic("params", params);
+ scope.push_dynamic("hash", hash);
+
+ let result = engine
+ .eval_ast_with_scope::<Dynamic>(&mut scope, script)
+ .map_err(RenderError::from)?;
+
+ let result_json: Json = from_dynamic(&result)?;
+
+ Ok(ScopedJson::Derived(result_json))
+}
+
+impl HelperDef for ScriptHelper {
+ fn call_inner<'reg: 'rc, 'rc>(
+ &self,
+ h: &Helper<'reg, 'rc>,
+ reg: &'reg Registry<'reg>,
+ _ctx: &'rc Context,
+ _rc: &mut RenderContext<'reg, 'rc>,
+ ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
+ call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::json::value::{PathAndJson, ScopedJson};
+ use rhai::Engine;
+
+ #[test]
+ fn test_dynamic_convert() {
+ let j0 = json! {
+ [{"name": "tomcat"}, {"name": "jetty"}]
+ };
+
+ let d0 = to_dynamic(&j0).unwrap();
+ assert_eq!("array", d0.type_name());
+
+ let j1 = json!({
+ "name": "tomcat",
+ "value": 4000,
+ });
+
+ let d1 = to_dynamic(&j1).unwrap();
+ assert_eq!("map", d1.type_name());
+ }
+
+ #[test]
+ fn test_to_json() {
+ let d0 = Dynamic::from("tomcat".to_owned());
+
+ assert_eq!(
+ Json::String("tomcat".to_owned()),
+ from_dynamic::<Json>(&d0).unwrap()
+ );
+ }
+
+ #[test]
+ fn test_script_helper_value_access() {
+ let engine = Engine::new();
+
+ let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
+ let ast = engine.compile(&script).unwrap();
+
+ let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
+ let hash = btreemap! {
+ "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
+ "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
+ };
+
+ let result = call_script_helper(&params, &hash, &engine, &ast)
+ .unwrap()
+ .render();
+ assert_eq!("1,true,2,no", &result);
+ }
+}