summaryrefslogtreecommitdiffstats
path: root/vendor/handlebars/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/handlebars/src')
-rw-r--r--vendor/handlebars/src/decorators/mod.rs12
-rw-r--r--vendor/handlebars/src/error.rs15
-rw-r--r--vendor/handlebars/src/helpers/mod.rs16
-rw-r--r--vendor/handlebars/src/lib.rs4
-rw-r--r--vendor/handlebars/src/macros.rs2
-rw-r--r--vendor/handlebars/src/output.rs21
-rw-r--r--vendor/handlebars/src/partial.rs67
-rw-r--r--vendor/handlebars/src/registry.rs48
-rw-r--r--vendor/handlebars/src/render.rs598
-rw-r--r--vendor/handlebars/src/template.rs50
10 files changed, 470 insertions, 363 deletions
diff --git a/vendor/handlebars/src/decorators/mod.rs b/vendor/handlebars/src/decorators/mod.rs
index bd2d23458..8633c1d5f 100644
--- a/vendor/handlebars/src/decorators/mod.rs
+++ b/vendor/handlebars/src/decorators/mod.rs
@@ -230,14 +230,14 @@ mod test {
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output|
-> Result<(), RenderError> {
- let s = format!(
+ write!(
+ out,
"{}m",
h.param(0)
.as_ref()
.map(|v| v.value())
.unwrap_or(&to_json(0))
- );
- out.write(s.as_ref())?;
+ )?;
Ok(())
},
),
@@ -262,15 +262,15 @@ mod test {
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output|
-> Result<(), RenderError> {
- let s = format!(
+ write!(
+ out,
"{}{}",
h.param(0)
.as_ref()
.map(|v| v.value())
.unwrap_or(&to_json(0)),
new_unit
- );
- out.write(s.as_ref())?;
+ )?;
Ok(())
};
diff --git a/vendor/handlebars/src/error.rs b/vendor/handlebars/src/error.rs
index 618d68e4f..160986350 100644
--- a/vendor/handlebars/src/error.rs
+++ b/vendor/handlebars/src/error.rs
@@ -1,4 +1,3 @@
-// use std::backtrace::Backtrace;
use std::error::Error as StdError;
use std::fmt::{self, Write};
use std::io::Error as IOError;
@@ -152,6 +151,7 @@ pub enum TemplateErrorReason {
/// Error on parsing template.
#[derive(Debug, Error)]
pub struct TemplateError {
+ #[deprecated(note = "public access to reason to be removed soon, use .reason() instead.")]
pub reason: TemplateErrorReason,
pub template_name: Option<String>,
pub line_no: Option<usize>,
@@ -160,6 +160,7 @@ pub struct TemplateError {
}
impl TemplateError {
+ #[allow(deprecated)]
pub fn of(e: TemplateErrorReason) -> TemplateError {
TemplateError {
reason: e,
@@ -181,6 +182,12 @@ impl TemplateError {
self.template_name = Some(name);
self
}
+
+ /// Get underlying reason for the error
+ #[allow(deprecated)]
+ pub fn reason(&self) -> &TemplateErrorReason {
+ &self.reason
+ }
}
impl From<(IOError, String)> for TemplateError {
@@ -229,16 +236,16 @@ impl fmt::Display for TemplateError {
(Some(line), Some(col), &Some(ref seg)) => writeln!(
f,
"Template error: {}\n --> Template error in \"{}\":{}:{}\n |\n{} |\n = reason: {}",
- self.reason,
+ self.reason(),
self.template_name
.as_ref()
.unwrap_or(&"Unnamed template".to_owned()),
line,
col,
seg,
- self.reason
+ self.reason()
),
- _ => write!(f, "{}", self.reason),
+ _ => write!(f, "{}", self.reason()),
}
}
}
diff --git a/vendor/handlebars/src/helpers/mod.rs b/vendor/handlebars/src/helpers/mod.rs
index ff5fa2495..8019f46f9 100644
--- a/vendor/handlebars/src/helpers/mod.rs
+++ b/vendor/handlebars/src/helpers/mod.rs
@@ -208,15 +208,11 @@ mod test {
) -> 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())?;
+ write!(out, "{}:{}", h.name(), v.value().render())?;
+ if h.is_block() {
out.write("->")?;
h.template().unwrap().render(r, ctx, rc, out)?;
- };
+ }
Ok(())
}
}
@@ -258,8 +254,7 @@ mod test {
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output|
-> Result<(), RenderError> {
- let output = format!("{}{}", h.name(), h.param(0).unwrap().value());
- out.write(output.as_ref())?;
+ write!(out, "{}{}", h.name(), h.param(0).unwrap().value())?;
Ok(())
},
),
@@ -273,8 +268,7 @@ mod test {
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output|
-> Result<(), RenderError> {
- let output = format!("{}", h.hash_get("value").unwrap().value().render());
- out.write(output.as_ref())?;
+ write!(out, "{}", h.hash_get("value").unwrap().value().render())?;
Ok(())
},
),
diff --git a/vendor/handlebars/src/lib.rs b/vendor/handlebars/src/lib.rs
index 1f9ab1ed3..1cb9e79ee 100644
--- a/vendor/handlebars/src/lib.rs
+++ b/vendor/handlebars/src/lib.rs
@@ -1,5 +1,7 @@
-#![doc(html_root_url = "https://docs.rs/handlebars/4.3.3")]
+#![doc(html_root_url = "https://docs.rs/handlebars/4.3.7")]
#![cfg_attr(docsrs, feature(doc_cfg))]
+#![allow(unknown_lints)]
+#![allow(clippy::result_large_err)]
//! # Handlebars
//!
//! [Handlebars](http://handlebarsjs.com/) is a modern and extensible templating solution originally created in the JavaScript world. It's used by many popular frameworks like [Ember.js](http://emberjs.com) and Chaplin. It's also ported to some other platforms such as [Java](https://github.com/jknack/handlebars.java).
diff --git a/vendor/handlebars/src/macros.rs b/vendor/handlebars/src/macros.rs
index bd1eab0bb..2d893acab 100644
--- a/vendor/handlebars/src/macros.rs
+++ b/vendor/handlebars/src/macros.rs
@@ -54,7 +54,7 @@ macro_rules! handlebars_helper {
r: &'reg $crate::Handlebars<'reg>,
_: &'rc $crate::Context,
_: &mut $crate::RenderContext<'reg, 'rc>,
- ) -> Result<$crate::ScopedJson<'reg, 'rc>, $crate::RenderError> {
+ ) -> std::result::Result<$crate::ScopedJson<'reg, 'rc>, $crate::RenderError> {
let mut param_idx = 0;
$(
diff --git a/vendor/handlebars/src/output.rs b/vendor/handlebars/src/output.rs
index 67e62b849..12075b365 100644
--- a/vendor/handlebars/src/output.rs
+++ b/vendor/handlebars/src/output.rs
@@ -6,6 +6,19 @@ use std::string::FromUtf8Error;
/// Handlebars uses this trait to define rendered output.
pub trait Output {
fn write(&mut self, seg: &str) -> Result<(), IOError>;
+
+ /// Designed to be used with `write!` macro.
+ /// for backward compatibility and to avoid breakage the default implementation
+ /// uses `format!` this may be not what you want.
+ fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<(), IOError> {
+ // Check if there is nothing to format to avoid allocation on case like
+ // write!(out, "hey")?;
+ if let Some(content) = args.as_str() {
+ self.write(content)
+ } else {
+ self.write(&std::fmt::format(args))
+ }
+ }
}
pub struct WriteOutput<W: Write> {
@@ -16,6 +29,10 @@ impl<W: Write> Output for WriteOutput<W> {
fn write(&mut self, seg: &str) -> Result<(), IOError> {
self.write.write_all(seg.as_bytes())
}
+
+ fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<(), IOError> {
+ self.write.write_fmt(args)
+ }
}
impl<W: Write> WriteOutput<W> {
@@ -33,6 +50,10 @@ impl Output for StringOutput {
self.buf.extend_from_slice(seg.as_bytes());
Ok(())
}
+
+ fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<(), IOError> {
+ self.buf.write_fmt(args)
+ }
}
impl StringOutput {
diff --git a/vendor/handlebars/src/partial.rs b/vendor/handlebars/src/partial.rs
index bcf9803fd..6c41091f4 100644
--- a/vendor/handlebars/src/partial.rs
+++ b/vendor/handlebars/src/partial.rs
@@ -72,7 +72,6 @@ pub fn expand_partial<'reg: 'rc, 'rc>(
local_rc.dec_partial_block_depth();
}
- let mut block = None;
let mut block_created = false;
// create context if param given
@@ -80,7 +79,15 @@ pub fn expand_partial<'reg: 'rc, 'rc>(
// path given, update base_path
let mut block_inner = BlockContext::new();
*block_inner.base_path_mut() = base_path.to_vec();
- block = Some(block_inner);
+
+ // because block is moved here, we need another bool variable to track
+ // its status for later cleanup
+ block_created = true;
+ // clear blocks to prevent block params from parent
+ // template to be leaked into partials
+ // see `test_partial_context_issue_495` for the case.
+ local_rc.clear_blocks();
+ local_rc.push_block(block_inner);
}
if !d.hash().is_empty() {
@@ -91,31 +98,34 @@ pub fn expand_partial<'reg: 'rc, 'rc>(
.map(|(k, v)| (*k, v.value()))
.collect::<HashMap<&str, &Json>>();
+ // create block if we didn't (no param provided for partial expression)
+ if !block_created {
+ let block_inner = if let Some(block) = local_rc.block() {
+ // reuse current block information, including base_path and
+ // base_value if any
+ block.clone()
+ } else {
+ BlockContext::new()
+ };
+
+ local_rc.clear_blocks();
+ local_rc.push_block(block_inner);
+ }
+
+ // evaluate context within current block, this includes block
+ // context provided by partial expression parameter
let merged_context = merge_json(
local_rc.evaluate2(ctx, &Path::current())?.as_json(),
&hash_ctx,
);
- if let Some(ref mut block_inner) = block {
- block_inner.set_base_value(merged_context);
- } else {
- let mut block_inner = BlockContext::new();
- block_inner.set_base_value(merged_context);
- block = Some(block_inner);
+ // update the base value, there must be a block for this so it's
+ // also safe to unwrap.
+ if let Some(block) = local_rc.block_mut() {
+ block.set_base_value(merged_context);
}
}
- if let Some(block_inner) = block {
- // because block is moved here, we need another bool variable to track
- // its status for later cleanup
- block_created = true;
- // clear blocks to prevent block params from parent
- // template to be leaked into partials
- // see `test_partial_context_issue_495` for the case.
- local_rc.clear_blocks();
- local_rc.push_block(block_inner);
- }
-
// @partial-block
if let Some(pb) = d.template() {
local_rc.push_partial_block(pb);
@@ -654,3 +664,22 @@ outer third line"#,
)
}
}
+
+#[test]
+fn test_issue_534() {
+ let t1 = "{{title}}";
+ let t2 = "{{#each modules}}{{> (lookup this \"module\") content name=0}}{{/each}}";
+
+ let data = json!({
+ "modules": [
+ {"module": "t1", "content": {"title": "foo"}},
+ {"module": "t1", "content": {"title": "bar"}},
+ ]
+ });
+
+ let mut hbs = Registry::new();
+ hbs.register_template_string("t1", t1).unwrap();
+ hbs.register_template_string("t2", t2).unwrap();
+
+ assert_eq!("foobar", hbs.render("t2", &data).unwrap());
+}
diff --git a/vendor/handlebars/src/registry.rs b/vendor/handlebars/src/registry.rs
index 438f8573c..d84dba715 100644
--- a/vendor/handlebars/src/registry.rs
+++ b/vendor/handlebars/src/registry.rs
@@ -298,22 +298,13 @@ impl<'reg> Registry<'reg> {
{
let dir_path = dir_path.as_ref();
- // Allowing dots at the beginning as to not break old
- // applications.
- let tpl_extension = tpl_extension.strip_prefix('.').unwrap_or(tpl_extension);
-
let walker = WalkDir::new(dir_path);
let dir_iter = walker
.min_depth(1)
.into_iter()
.filter_map(|e| e.ok().map(|e| e.into_path()))
// Checks if extension matches
- .filter(|tpl_path| {
- tpl_path
- .extension()
- .map(|extension| extension == tpl_extension)
- .unwrap_or(false)
- })
+ .filter(|tpl_path| tpl_path.to_string_lossy().ends_with(tpl_extension))
// Rejects any hidden or temporary files.
.filter(|tpl_path| {
tpl_path
@@ -327,12 +318,16 @@ impl<'reg> Registry<'reg> {
.strip_prefix(dir_path)
.ok()
.map(|tpl_canonical_name| {
- tpl_canonical_name
- .with_extension("")
+ let tpl_name = tpl_canonical_name
.components()
.map(|component| component.as_os_str().to_string_lossy())
.collect::<Vec<_>>()
- .join("/")
+ .join("/");
+
+ tpl_name
+ .strip_suffix(tpl_extension)
+ .map(|s| s.to_owned())
+ .unwrap_or(tpl_name)
})
.map(|tpl_canonical_name| (tpl_canonical_name, tpl_path))
});
@@ -609,7 +604,7 @@ impl<'reg> Registry<'reg> {
T: Serialize,
{
let mut output = StringOutput::new();
- let ctx = Context::wraps(&data)?;
+ let ctx = Context::wraps(data)?;
self.render_to_output(name, &ctx, &mut output)?;
output.into_string().map_err(RenderError::from)
}
@@ -908,6 +903,31 @@ mod test {
drop(file1);
dir.close().unwrap();
}
+
+ {
+ let dir = tempdir().unwrap();
+ let mut r = Registry::new();
+
+ let file1_path = dir.path().join("t11.hbs.html");
+ let mut file1: File = File::create(&file1_path).unwrap();
+ writeln!(file1, "<h1>Bonjour {{world}}!</h1>").unwrap();
+
+ let mut dir_path = dir
+ .path()
+ .to_string_lossy()
+ .replace(std::path::MAIN_SEPARATOR, "/");
+ if !dir_path.ends_with("/") {
+ dir_path.push('/');
+ }
+ r.register_templates_directory(".hbs.html", dir_path)
+ .unwrap();
+
+ assert_eq!(r.templates.len(), 1);
+ assert_eq!(r.templates.contains_key("t11"), true);
+
+ drop(file1);
+ dir.close().unwrap();
+ }
}
#[test]
diff --git a/vendor/handlebars/src/render.rs b/vendor/handlebars/src/render.rs
index 036352b3a..6d7418e12 100644
--- a/vendor/handlebars/src/render.rs
+++ b/vendor/handlebars/src/render.rs
@@ -874,315 +874,333 @@ impl Evaluable for TemplateElement {
}
}
-#[test]
-fn test_raw_string() {
- let r = Registry::new();
- let raw_string = RawString("<h1>hello world</h1>".to_string());
-
- let mut out = StringOutput::new();
- let ctx = Context::null();
- {
- let mut rc = RenderContext::new(None);
- raw_string.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
- }
- assert_eq!(
- out.into_string().unwrap(),
- "<h1>hello world</h1>".to_string()
- );
-}
-
-#[test]
-fn test_expression() {
- let r = Registry::new();
- let element = Expression(Box::new(HelperTemplate::with_path(Path::with_named_paths(
- &["hello"],
- ))));
-
- let mut out = StringOutput::new();
- let mut m: BTreeMap<String, String> = BTreeMap::new();
- let value = "<p></p>".to_string();
- m.insert("hello".to_string(), value);
- let ctx = Context::wraps(&m).unwrap();
- {
- let mut rc = RenderContext::new(None);
- element.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
- }
-
- assert_eq!(
- out.into_string().unwrap(),
- "&lt;p&gt;&lt;/p&gt;".to_string()
- );
-}
-
-#[test]
-fn test_html_expression() {
- let r = Registry::new();
- let element = HtmlExpression(Box::new(HelperTemplate::with_path(Path::with_named_paths(
- &["hello"],
- ))));
-
- let mut out = StringOutput::new();
- let mut m: BTreeMap<String, String> = BTreeMap::new();
- let value = "world";
- m.insert("hello".to_string(), value.to_string());
- let ctx = Context::wraps(&m).unwrap();
- {
- let mut rc = RenderContext::new(None);
- element.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
+#[cfg(test)]
+mod test {
+ use std::collections::BTreeMap;
+
+ use super::{Helper, RenderContext, Renderable};
+ use crate::block::BlockContext;
+ use crate::context::Context;
+ use crate::error::RenderError;
+ use crate::json::path::Path;
+ use crate::json::value::JsonRender;
+ use crate::output::{Output, StringOutput};
+ use crate::registry::Registry;
+ use crate::template::TemplateElement::*;
+ use crate::template::{HelperTemplate, Template, TemplateElement};
+
+ #[test]
+ fn test_raw_string() {
+ let r = Registry::new();
+ let raw_string = RawString("<h1>hello world</h1>".to_string());
+
+ let mut out = StringOutput::new();
+ let ctx = Context::null();
+ {
+ let mut rc = RenderContext::new(None);
+ raw_string.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
+ }
+ assert_eq!(
+ out.into_string().unwrap(),
+ "<h1>hello world</h1>".to_string()
+ );
}
- assert_eq!(out.into_string().unwrap(), value.to_string());
-}
-
-#[test]
-fn test_template() {
- let r = Registry::new();
- let mut out = StringOutput::new();
- let mut m: BTreeMap<String, String> = BTreeMap::new();
- let value = "world".to_string();
- m.insert("hello".to_string(), value);
- let ctx = Context::wraps(&m).unwrap();
-
- let elements: Vec<TemplateElement> = vec![
- RawString("<h1>".to_string()),
- Expression(Box::new(HelperTemplate::with_path(Path::with_named_paths(
+ #[test]
+ fn test_expression() {
+ let r = Registry::new();
+ let element = Expression(Box::new(HelperTemplate::with_path(Path::with_named_paths(
&["hello"],
- )))),
- RawString("</h1>".to_string()),
- Comment("".to_string()),
- ];
+ ))));
- let template = Template {
- elements,
- name: None,
- mapping: Vec::new(),
- };
+ let mut out = StringOutput::new();
+ let mut m: BTreeMap<String, String> = BTreeMap::new();
+ let value = "<p></p>".to_string();
+ m.insert("hello".to_string(), value);
+ let ctx = Context::wraps(&m).unwrap();
+ {
+ let mut rc = RenderContext::new(None);
+ element.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
+ }
- {
- let mut rc = RenderContext::new(None);
- template.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
+ assert_eq!(
+ out.into_string().unwrap(),
+ "&lt;p&gt;&lt;/p&gt;".to_string()
+ );
}
- assert_eq!(out.into_string().unwrap(), "<h1>world</h1>".to_string());
-}
-
-#[test]
-fn test_render_context_promotion_and_demotion() {
- use crate::json::value::to_json;
- let mut render_context = RenderContext::new(None);
- let mut block = BlockContext::new();
-
- block.set_local_var("index", to_json(0));
- render_context.push_block(block);
-
- render_context.push_block(BlockContext::new());
- assert_eq!(
- render_context.get_local_var(1, "index").unwrap(),
- &to_json(0)
- );
-
- render_context.pop_block();
-
- assert_eq!(
- render_context.get_local_var(0, "index").unwrap(),
- &to_json(0)
- );
-}
+ #[test]
+ fn test_html_expression() {
+ let r = Registry::new();
+ let element = HtmlExpression(Box::new(HelperTemplate::with_path(Path::with_named_paths(
+ &["hello"],
+ ))));
-#[test]
-fn test_render_subexpression_issue_115() {
- use crate::support::str::StringWriter;
-
- let mut r = Registry::new();
- r.register_helper(
- "format",
- Box::new(
- |h: &Helper<'_, '_>,
- _: &Registry<'_>,
- _: &Context,
- _: &mut RenderContext<'_, '_>,
- out: &mut dyn Output|
- -> Result<(), RenderError> {
- out.write(format!("{}", h.param(0).unwrap().value().render()).as_ref())
- .map(|_| ())
- .map_err(RenderError::from)
- },
- ),
- );
+ let mut out = StringOutput::new();
+ let mut m: BTreeMap<String, String> = BTreeMap::new();
+ let value = "world";
+ m.insert("hello".to_string(), value.to_string());
+ let ctx = Context::wraps(&m).unwrap();
+ {
+ let mut rc = RenderContext::new(None);
+ element.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
+ }
- let mut sw = StringWriter::new();
- let mut m: BTreeMap<String, String> = BTreeMap::new();
- m.insert("a".to_string(), "123".to_string());
+ assert_eq!(out.into_string().unwrap(), value.to_string());
+ }
+
+ #[test]
+ fn test_template() {
+ let r = Registry::new();
+ let mut out = StringOutput::new();
+ let mut m: BTreeMap<String, String> = BTreeMap::new();
+ let value = "world".to_string();
+ m.insert("hello".to_string(), value);
+ let ctx = Context::wraps(&m).unwrap();
+
+ let elements: Vec<TemplateElement> = vec![
+ RawString("<h1>".to_string()),
+ Expression(Box::new(HelperTemplate::with_path(Path::with_named_paths(
+ &["hello"],
+ )))),
+ RawString("</h1>".to_string()),
+ Comment("".to_string()),
+ ];
+
+ let template = Template {
+ elements,
+ name: None,
+ mapping: Vec::new(),
+ };
- {
- if let Err(e) = r.render_template_to_write("{{format (format a)}}", &m, &mut sw) {
- panic!("{}", e);
+ {
+ let mut rc = RenderContext::new(None);
+ template.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
}
- }
-
- assert_eq!(sw.into_string(), "123".to_string());
-}
-#[test]
-fn test_render_error_line_no() {
- let mut r = Registry::new();
- let m: BTreeMap<String, String> = BTreeMap::new();
+ assert_eq!(out.into_string().unwrap(), "<h1>world</h1>".to_string());
+ }
+
+ #[test]
+ fn test_render_context_promotion_and_demotion() {
+ use crate::json::value::to_json;
+ let mut render_context = RenderContext::new(None);
+ let mut block = BlockContext::new();
+
+ block.set_local_var("index", to_json(0));
+ render_context.push_block(block);
+
+ render_context.push_block(BlockContext::new());
+ assert_eq!(
+ render_context.get_local_var(1, "index").unwrap(),
+ &to_json(0)
+ );
+
+ render_context.pop_block();
+
+ assert_eq!(
+ render_context.get_local_var(0, "index").unwrap(),
+ &to_json(0)
+ );
+ }
+
+ #[test]
+ fn test_render_subexpression_issue_115() {
+ use crate::support::str::StringWriter;
+
+ let mut r = Registry::new();
+ r.register_helper(
+ "format",
+ Box::new(
+ |h: &Helper<'_, '_>,
+ _: &Registry<'_>,
+ _: &Context,
+ _: &mut RenderContext<'_, '_>,
+ out: &mut dyn Output|
+ -> Result<(), RenderError> {
+ out.write(&h.param(0).unwrap().value().render())
+ .map(|_| ())
+ .map_err(RenderError::from)
+ },
+ ),
+ );
+
+ let mut sw = StringWriter::new();
+ let mut m: BTreeMap<String, String> = BTreeMap::new();
+ m.insert("a".to_string(), "123".to_string());
- let name = "invalid_template";
- assert!(r
- .register_template_string(name, "<h1>\n{{#if true}}\n {{#each}}{{/each}}\n{{/if}}")
- .is_ok());
+ {
+ if let Err(e) = r.render_template_to_write("{{format (format a)}}", &m, &mut sw) {
+ panic!("{}", e);
+ }
+ }
- if let Err(e) = r.render(name, &m) {
- assert_eq!(e.line_no.unwrap(), 3);
- assert_eq!(e.column_no.unwrap(), 3);
- assert_eq!(e.template_name, Some(name.to_owned()));
- } else {
- panic!("Error expected");
+ assert_eq!(sw.into_string(), "123".to_string());
}
-}
-#[test]
-fn test_partial_failback_render() {
- let mut r = Registry::new();
+ #[test]
+ fn test_render_error_line_no() {
+ let mut r = Registry::new();
+ let m: BTreeMap<String, String> = BTreeMap::new();
- assert!(r
- .register_template_string("parent", "<html>{{> layout}}</html>")
- .is_ok());
- assert!(r
- .register_template_string(
- "child",
- "{{#*inline \"layout\"}}content{{/inline}}{{#> parent}}{{> seg}}{{/parent}}"
- )
- .is_ok());
- assert!(r.register_template_string("seg", "1234").is_ok());
-
- let r = r.render("child", &true).expect("should work");
- assert_eq!(r, "<html>content</html>");
-}
-
-#[test]
-fn test_key_with_slash() {
- let mut r = Registry::new();
-
- assert!(r
- .register_template_string("t", "{{#each this}}{{@key}}: {{this}}\n{{/each}}")
- .is_ok());
-
- let r = r.render("t", &json!({"/foo": "bar"})).unwrap();
-
- assert_eq!(r, "/foo: bar\n");
-}
-
-#[test]
-fn test_comment() {
- let r = Registry::new();
-
- assert_eq!(
- r.render_template("Hello {{this}} {{! test me }}", &0)
- .unwrap(),
- "Hello 0 "
- );
-}
-
-#[test]
-fn test_zero_args_heler() {
- let mut r = Registry::new();
-
- r.register_helper(
- "name",
- Box::new(
- |_: &Helper<'_, '_>,
- _: &Registry<'_>,
- _: &Context,
- _: &mut RenderContext<'_, '_>,
- out: &mut dyn Output|
- -> Result<(), RenderError> { out.write("N/A").map_err(Into::into) },
- ),
- );
-
- r.register_template_string("t0", "Output name: {{name}}")
- .unwrap();
- r.register_template_string("t1", "Output name: {{first_name}}")
- .unwrap();
- r.register_template_string("t2", "Output name: {{./name}}")
- .unwrap();
-
- // when "name" is available in context, use context first
- assert_eq!(
- r.render("t0", &json!({"name": "Alex"})).unwrap(),
- "Output name: N/A"
- );
+ let name = "invalid_template";
+ assert!(r
+ .register_template_string(name, "<h1>\n{{#if true}}\n {{#each}}{{/each}}\n{{/if}}")
+ .is_ok());
- // when "name" is unavailable, call helper with same name
- assert_eq!(
- r.render("t2", &json!({"name": "Alex"})).unwrap(),
- "Output name: Alex"
- );
-
- // output nothing when neither context nor helper available
- assert_eq!(
- r.render("t1", &json!({"name": "Alex"})).unwrap(),
- "Output name: "
- );
-
- // generate error in strict mode for above case
- r.set_strict_mode(true);
- assert!(r.render("t1", &json!({"name": "Alex"})).is_err());
-
- // output nothing when helperMissing was defined
- r.set_strict_mode(false);
- r.register_helper(
- "helperMissing",
- Box::new(
- |h: &Helper<'_, '_>,
- _: &Registry<'_>,
- _: &Context,
- _: &mut RenderContext<'_, '_>,
- out: &mut dyn Output|
- -> Result<(), RenderError> {
- let name = h.name();
- out.write(&format!("{} not resolved", name))?;
- Ok(())
- },
- ),
- );
- assert_eq!(
- r.render("t1", &json!({"name": "Alex"})).unwrap(),
- "Output name: first_name not resolved"
- );
-}
-
-#[test]
-fn test_identifiers_starting_with_numbers() {
- let mut r = Registry::new();
-
- assert!(r
- .register_template_string("r1", "{{#if 0a}}true{{/if}}")
- .is_ok());
- let r1 = r.render("r1", &json!({"0a": true})).unwrap();
- assert_eq!(r1, "true");
-
- assert!(r.register_template_string("r2", "{{eq 1a 1}}").is_ok());
- let r2 = r.render("r2", &json!({"1a": 2, "a": 1})).unwrap();
- assert_eq!(r2, "false");
+ if let Err(e) = r.render(name, &m) {
+ assert_eq!(e.line_no.unwrap(), 3);
+ assert_eq!(e.column_no.unwrap(), 3);
+ assert_eq!(e.template_name, Some(name.to_owned()));
+ } else {
+ panic!("Error expected");
+ }
+ }
- assert!(r
+ #[test]
+ fn test_partial_failback_render() {
+ let mut r = Registry::new();
+
+ assert!(r
+ .register_template_string("parent", "<html>{{> layout}}</html>")
+ .is_ok());
+ assert!(r
+ .register_template_string(
+ "child",
+ "{{#*inline \"layout\"}}content{{/inline}}{{#> parent}}{{> seg}}{{/parent}}"
+ )
+ .is_ok());
+ assert!(r.register_template_string("seg", "1234").is_ok());
+
+ let r = r.render("child", &true).expect("should work");
+ assert_eq!(r, "<html>content</html>");
+ }
+
+ #[test]
+ fn test_key_with_slash() {
+ let mut r = Registry::new();
+
+ assert!(r
+ .register_template_string("t", "{{#each this}}{{@key}}: {{this}}\n{{/each}}")
+ .is_ok());
+
+ let r = r.render("t", &json!({"/foo": "bar"})).unwrap();
+
+ assert_eq!(r, "/foo: bar\n");
+ }
+
+ #[test]
+ fn test_comment() {
+ let r = Registry::new();
+
+ assert_eq!(
+ r.render_template("Hello {{this}} {{! test me }}", &0)
+ .unwrap(),
+ "Hello 0 "
+ );
+ }
+
+ #[test]
+ fn test_zero_args_heler() {
+ let mut r = Registry::new();
+
+ r.register_helper(
+ "name",
+ Box::new(
+ |_: &Helper<'_, '_>,
+ _: &Registry<'_>,
+ _: &Context,
+ _: &mut RenderContext<'_, '_>,
+ out: &mut dyn Output|
+ -> Result<(), RenderError> {
+ out.write("N/A").map_err(Into::into)
+ },
+ ),
+ );
+
+ r.register_template_string("t0", "Output name: {{name}}")
+ .unwrap();
+ r.register_template_string("t1", "Output name: {{first_name}}")
+ .unwrap();
+ r.register_template_string("t2", "Output name: {{./name}}")
+ .unwrap();
+
+ // when "name" is available in context, use context first
+ assert_eq!(
+ r.render("t0", &json!({"name": "Alex"})).unwrap(),
+ "Output name: N/A"
+ );
+
+ // when "name" is unavailable, call helper with same name
+ assert_eq!(
+ r.render("t2", &json!({"name": "Alex"})).unwrap(),
+ "Output name: Alex"
+ );
+
+ // output nothing when neither context nor helper available
+ assert_eq!(
+ r.render("t1", &json!({"name": "Alex"})).unwrap(),
+ "Output name: "
+ );
+
+ // generate error in strict mode for above case
+ r.set_strict_mode(true);
+ assert!(r.render("t1", &json!({"name": "Alex"})).is_err());
+
+ // output nothing when helperMissing was defined
+ r.set_strict_mode(false);
+ r.register_helper(
+ "helperMissing",
+ Box::new(
+ |h: &Helper<'_, '_>,
+ _: &Registry<'_>,
+ _: &Context,
+ _: &mut RenderContext<'_, '_>,
+ out: &mut dyn Output|
+ -> Result<(), RenderError> {
+ let name = h.name();
+ write!(out, "{} not resolved", name)?;
+ Ok(())
+ },
+ ),
+ );
+ assert_eq!(
+ r.render("t1", &json!({"name": "Alex"})).unwrap(),
+ "Output name: first_name not resolved"
+ );
+ }
+
+ #[test]
+ fn test_identifiers_starting_with_numbers() {
+ let mut r = Registry::new();
+
+ assert!(r
+ .register_template_string("r1", "{{#if 0a}}true{{/if}}")
+ .is_ok());
+ let r1 = r.render("r1", &json!({"0a": true})).unwrap();
+ assert_eq!(r1, "true");
+
+ assert!(r.register_template_string("r2", "{{eq 1a 1}}").is_ok());
+ let r2 = r.render("r2", &json!({"1a": 2, "a": 1})).unwrap();
+ assert_eq!(r2, "false");
+
+ assert!(r
.register_template_string("r3", "0: {{0}} {{#if (eq 0 true)}}resolved from context{{/if}}\n1a: {{1a}} {{#if (eq 1a true)}}resolved from context{{/if}}\n2_2: {{2_2}} {{#if (eq 2_2 true)}}resolved from context{{/if}}") // YUP it is just eq that barfs! is if handled specially? maybe this test should go nearer to specific helpers that fail?
.is_ok());
- let r3 = r
- .render("r3", &json!({"0": true, "1a": true, "2_2": true}))
- .unwrap();
- assert_eq!(
- r3,
- "0: true \n1a: true resolved from context\n2_2: true resolved from context"
- );
-
- // these should all be errors:
- assert!(r.register_template_string("r4", "{{eq 1}}").is_ok());
- assert!(r.register_template_string("r5", "{{eq a1}}").is_ok());
- assert!(r.register_template_string("r6", "{{eq 1a}}").is_ok());
- assert!(r.render("r4", &()).is_err());
- assert!(r.render("r5", &()).is_err());
- assert!(r.render("r6", &()).is_err());
+ let r3 = r
+ .render("r3", &json!({"0": true, "1a": true, "2_2": true}))
+ .unwrap();
+ assert_eq!(
+ r3,
+ "0: true \n1a: true resolved from context\n2_2: true resolved from context"
+ );
+
+ // these should all be errors:
+ assert!(r.register_template_string("r4", "{{eq 1}}").is_ok());
+ assert!(r.register_template_string("r5", "{{eq a1}}").is_ok());
+ assert!(r.register_template_string("r6", "{{eq 1a}}").is_ok());
+ assert!(r.render("r4", &()).is_err());
+ assert!(r.render("r5", &()).is_err());
+ assert!(r.render("r6", &()).is_err());
+ }
}
diff --git a/vendor/handlebars/src/template.rs b/vendor/handlebars/src/template.rs
index 617f47711..4bde14859 100644
--- a/vendor/handlebars/src/template.rs
+++ b/vendor/handlebars/src/template.rs
@@ -288,11 +288,33 @@ impl Template {
Parameter::Path(Path::new(param_span.as_str(), path_segs))
}
Rule::literal => {
- let s = param_span.as_str();
- if let Ok(json) = Json::from_str(s) {
+ // Parse the parameter as a JSON literal
+ let param_literal = it.next().unwrap();
+ let json_result = match param_literal.as_rule() {
+ Rule::string_literal
+ if it.peek().unwrap().as_rule() == Rule::string_inner_single_quote =>
+ {
+ // ...unless the parameter is a single-quoted string.
+ // In that case, transform it to a double-quoted string
+ // and then parse it as a JSON literal.
+ let string_inner_single_quote = it.next().unwrap();
+ let double_quoted = format!(
+ "\"{}\"",
+ string_inner_single_quote
+ .as_str()
+ .replace("\\'", "'")
+ .replace('"', "\\\"")
+ );
+ Json::from_str(&double_quoted)
+ }
+ _ => Json::from_str(param_span.as_str()),
+ };
+ if let Ok(json) = json_result {
Parameter::Literal(json)
} else {
- Parameter::Name(s.to_owned())
+ return Err(TemplateError::of(TemplateErrorReason::InvalidParam(
+ param_span.as_str().to_owned(),
+ )));
}
}
Rule::subexpression => {
@@ -508,8 +530,8 @@ impl Template {
}
}
- pub(crate) fn compile2<'a>(
- source: &'a str,
+ pub(crate) fn compile2(
+ source: &str,
options: TemplateOptions,
) -> Result<Template, TemplateError> {
let mut helper_stack: VecDeque<HelperTemplate> = VecDeque::new();
@@ -1010,7 +1032,7 @@ mod test {
let terr = Template::compile(source).unwrap_err();
- assert!(matches!(terr.reason, TemplateErrorReason::InvalidSyntax));
+ assert!(matches!(terr.reason(), TemplateErrorReason::InvalidSyntax));
assert_eq!(terr.line_no.unwrap(), 4);
assert_eq!(terr.column_no.unwrap(), 5);
}
@@ -1126,16 +1148,10 @@ mod test {
let sources = ["{{invalid", "{{{invalid", "{{invalid}", "{{!hello"];
for s in sources.iter() {
let result = Template::compile(s.to_owned());
- if let Err(e) = result {
- match e.reason {
- TemplateErrorReason::InvalidSyntax => {}
- _ => {
- panic!("Unexpected error type {}", e);
- }
- }
- } else {
- panic!("Undetected error");
- }
+ assert!(matches!(
+ *result.unwrap_err().reason(),
+ TemplateErrorReason::InvalidSyntax
+ ));
}
}
@@ -1316,6 +1332,6 @@ mod test {
let s = "{{#>(X)}}{{/X}}";
let result = Template::compile(s);
assert!(result.is_err());
- assert_eq!("decorator \"Subexpression(Subexpression { element: Expression(HelperTemplate { name: Path(Relative(([Named(\\\"X\\\")], \\\"X\\\"))), params: [], hash: {}, block_param: None, template: None, inverse: None, block: false }) })\" was opened, but \"X\" is closing", format!("{}", result.unwrap_err().reason));
+ assert_eq!("decorator \"Subexpression(Subexpression { element: Expression(HelperTemplate { name: Path(Relative(([Named(\\\"X\\\")], \\\"X\\\"))), params: [], hash: {}, block_param: None, template: None, inverse: None, block: false }) })\" was opened, but \"X\" is closing", format!("{}", result.unwrap_err().reason()));
}
}