From 6b9064c90468c0fb69175abd41cb1f70712849d1 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 17 Feb 2021 01:39:22 +0800 Subject: [PATCH 1/6] (feat) allow template source be used without dev_mode --- src/partial.rs | 33 ++++++++++++++++++++++++++++----- src/registry.rs | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/partial.rs b/src/partial.rs index 207e8f520..0eff0708d 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::HashMap; use serde_json::value::Value as Json; @@ -9,9 +10,35 @@ use crate::json::path::Path; use crate::output::Output; use crate::registry::Registry; use crate::render::{Decorator, Evaluable, RenderContext, Renderable}; +use crate::template::Template; pub(crate) const PARTIAL_BLOCK: &str = "@partial-block"; +#[inline] +fn resolve_partial<'reg: 'rc, 'rc: 'p, 'p>( + tname: &str, + d: &Decorator<'reg, 'rc>, + r: &'reg Registry<'reg>, + rc: &'p RenderContext<'reg, 'rc>, +) -> Result>, RenderError> { + let mut partial = rc.get_partial(tname).map(Cow::Borrowed); + + // try fetch from registry with the same name + if partial.is_none() { + if let Some(tpl) = r.get_template(tname) { + let tpl = tpl.map_err(RenderError::from)?; + partial = Some(tpl); + } + } + + // fallback to decorator's content + if partial.is_none() { + partial = d.template().map(Cow::Borrowed); + } + + Ok(partial) +} + pub fn expand_partial<'reg: 'rc, 'rc>( d: &Decorator<'reg, 'rc>, r: &'reg Registry<'reg>, @@ -29,11 +56,7 @@ pub fn expand_partial<'reg: 'rc, 'rc>( return Err(RenderError::new("Cannot include self in >")); } - // if tname == PARTIAL_BLOCK - let partial = rc - .get_partial(tname) - .or_else(|| r.get_template(tname)) - .or_else(|| d.template()); + let partial = resolve_partial(tname, d, r, rc)?; if let Some(t) = partial { // clone to avoid lifetime issue diff --git a/src/registry.rs b/src/registry.rs index cbf884a0c..29649be63 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -241,6 +241,8 @@ impl<'reg> Registry<'reg> { .map_err(|err| TemplateError::from((err, name.to_owned())))?; self.register_template_string(name, template_string)?; + // Register as template source only with dev_mode. + // The default FileSource has no cache so it doesn't perform well if self.dev_mode { self.template_sources .insert(name.to_owned(), Arc::new(source)); @@ -307,6 +309,7 @@ impl<'reg> Registry<'reg> { /// Remove a template from the registry pub fn unregister_template(&mut self, name: &str) { self.templates.remove(name); + self.template_sources.remove(name); } /// Register a helper @@ -393,17 +396,30 @@ impl<'reg> Registry<'reg> { /// Return `true` if a template is registered for the given name pub fn has_template(&self, name: &str) -> bool { - self.get_template(name).is_some() + self.templates.contains_key(name) || self.template_sources.contains_key(name) } /// Return a registered template, - pub fn get_template(&self, name: &str) -> Option<&Template> { - self.templates.get(name) + pub fn get_template( + &'reg self, + name: &str, + ) -> Option, TemplateError>> { + self.templates + .get(name) + .map(|t| Ok(Cow::Borrowed(t))) + .or_else(|| { + self.template_sources.get(name).map(|src| { + src.load() + .map_err(|e| TemplateError::from((e, name.to_owned()))) + .and_then(|tpl_str| Template::compile_with_name(tpl_str, name.to_owned())) + .map(Cow::Owned) + }) + }) } #[inline] fn get_or_load_template(&'reg self, name: &str) -> Result, RenderError> { - if let (true, Some(source)) = (self.dev_mode, self.template_sources.get(name)) { + if let Some(source) = self.template_sources.get(name) { source .load() .map_err(|e| TemplateError::from((e, name.to_owned()))) @@ -424,7 +440,7 @@ impl<'reg> Registry<'reg> { name: &str, ) -> Result>, RenderError> { #[cfg(feature = "script_helper")] - if let (true, Some(source)) = (self.dev_mode, self.script_sources.get(name)) { + if let Some(source) = self.script_sources.get(name) { return source .load() .map_err(ScriptError::from) @@ -453,14 +469,19 @@ impl<'reg> Registry<'reg> { self.decorators.get(name).map(|v| v.as_ref()) } - /// Return all templates registered - pub fn get_templates(&self) -> &HashMap { - &self.templates + /// Return all registered template names + pub fn get_template_names(&self) -> Vec { + self.templates + .keys() + .chain(self.template_sources.keys()) + .cloned() + .collect::>() } /// Unregister all templates pub fn clear_templates(&mut self) { self.templates.clear(); + self.template_sources.clear(); } #[inline] From cd01eb62427a7cfcfbbcf1b26c18f8a6d64c80e5 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 17 Feb 2021 01:47:52 +0800 Subject: [PATCH 2/6] (feat) add register_template_source api and make source public --- src/lib.rs | 1 + src/registry.rs | 9 +++++++++ src/sources.rs | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 526032a07..5754c3fcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -391,6 +391,7 @@ pub use self::json::value::{to_json, JsonRender, PathAndJson, ScopedJson}; pub use self::output::Output; pub use self::registry::{html_escape, no_escape, EscapeFn, Registry as Handlebars}; pub use self::render::{Decorator, Evaluable, Helper, RenderContext, Renderable}; +pub use self::sources::Source; pub use self::template::Template; #[doc(hidden)] diff --git a/src/registry.rs b/src/registry.rs index 29649be63..f2e83c357 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -215,6 +215,15 @@ impl<'reg> Registry<'reg> { Ok(()) } + /// Register a template source + pub fn register_template_source(&mut self, name: &str, tpl_src: S) + where + S: Source + Send + Sync + 'reg, + { + self.template_sources + .insert(name.to_owned(), Arc::new(tpl_src)); + } + /// Register a partial string /// /// A named partial will be added to the registry. It will overwrite template with diff --git a/src/sources.rs b/src/sources.rs index 8c8b2ba57..285685e74 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::{BufReader, Error as IOError, Read}; use std::path::PathBuf; -pub(crate) trait Source { +pub trait Source { type Item; type Error; From 73dc4a26ffabdfb323b3673cb47092e0f69538f5 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Thu, 18 Feb 2021 22:38:38 +0800 Subject: [PATCH 3/6] (fix) dedup for template names --- src/registry.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/registry.rs b/src/registry.rs index f2e83c357..a9f2b983e 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -480,11 +480,14 @@ impl<'reg> Registry<'reg> { /// Return all registered template names pub fn get_template_names(&self) -> Vec { - self.templates + let mut names = self + .templates .keys() .chain(self.template_sources.keys()) .cloned() - .collect::>() + .collect::>(); + names.dedup(); + names } /// Unregister all templates From 814a537e9b6b54c6b621ab5f54fa63d7ae60957c Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Thu, 18 Feb 2021 22:39:53 +0800 Subject: [PATCH 4/6] (fix) sort before dedup --- src/registry.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registry.rs b/src/registry.rs index a9f2b983e..e69598bfe 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -486,6 +486,7 @@ impl<'reg> Registry<'reg> { .chain(self.template_sources.keys()) .cloned() .collect::>(); + name.sort(); names.dedup(); names } From a38fb213caed56a85c06bd7ad1100bad6be58129 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Thu, 18 Feb 2021 23:13:03 +0800 Subject: [PATCH 5/6] (fix) typo --- src/registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registry.rs b/src/registry.rs index e69598bfe..f0792b7b0 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -486,7 +486,7 @@ impl<'reg> Registry<'reg> { .chain(self.template_sources.keys()) .cloned() .collect::>(); - name.sort(); + names.sort(); names.dedup(); names } From 46b2cf0293fca17e3ac98018d7fa511c59665e67 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Sun, 6 Jun 2021 17:16:21 -0700 Subject: [PATCH 6/6] (test) add a test for register_template_source --- src/registry.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/registry.rs b/src/registry.rs index d31bfec3d..f03e74ce3 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -617,8 +617,9 @@ mod test { use crate::render::{Helper, RenderContext, Renderable}; use crate::support::str::StringWriter; use crate::template::Template; + use crate::Source; use std::fs::File; - use std::io::Write; + use std::io::{Error as IoError, ErrorKind as IoErrorKind, Write}; use tempfile::tempdir; #[derive(Clone, Copy)] @@ -668,6 +669,31 @@ mod test { ); } + #[test] + fn test_register_template_source() { + let mut r = Registry::new(); + + impl Source for &'static str { + type Item = String; + type Error = IoError; + fn load(&self) -> Result { + Ok(String::from(*self)) + } + } + r.register_template_source("test", "

Hello world!

"); + assert_eq!("

Hello world!

", r.render("test", &()).unwrap()); + + impl Source for () { + type Item = String; + type Error = IoError; + fn load(&self) -> Result { + Err(IoError::from(IoErrorKind::Other)) + } + } + r.register_template_source("test2", ()); + assert!(r.render("test2", &()).is_err()); + } + #[test] #[cfg(feature = "dir_source")] fn test_register_templates_directory() {