diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/handlebars/src/registry.rs | 272 |
1 files changed, 215 insertions, 57 deletions
diff --git a/vendor/handlebars/src/registry.rs b/vendor/handlebars/src/registry.rs index e7f5f1cfd..438f8573c 100644 --- a/vendor/handlebars/src/registry.rs +++ b/vendor/handlebars/src/registry.rs @@ -17,12 +17,10 @@ use crate::output::{Output, StringOutput, WriteOutput}; use crate::render::{RenderContext, Renderable}; use crate::sources::{FileSource, Source}; use crate::support::str::{self, StringWriter}; -use crate::template::Template; +use crate::template::{Template, TemplateOptions}; #[cfg(feature = "dir_source")] -use std::path; -#[cfg(feature = "dir_source")] -use walkdir::{DirEntry, WalkDir}; +use walkdir::WalkDir; #[cfg(feature = "script_helper")] use rhai::Engine; @@ -30,6 +28,9 @@ use rhai::Engine; #[cfg(feature = "script_helper")] use crate::helpers::scripting::ScriptHelper; +#[cfg(feature = "rust-embed")] +use rust_embed::RustEmbed; + /// This type represents an *escape fn*, that is a function whose purpose it is /// to escape potentially problematic characters in a string. /// @@ -62,6 +63,7 @@ pub struct Registry<'reg> { escape_fn: EscapeFn, strict_mode: bool, dev_mode: bool, + prevent_indent: bool, #[cfg(feature = "script_helper")] pub(crate) engine: Arc<Engine>, @@ -90,21 +92,6 @@ impl<'reg> Default for Registry<'reg> { } } -#[cfg(feature = "dir_source")] -fn filter_file(entry: &DirEntry, suffix: &str) -> bool { - let path = entry.path(); - - // ignore hidden files, emacs buffers and files with wrong suffix - !path.is_file() - || path - .file_name() - .map(|s| { - let ds = s.to_string_lossy(); - ds.starts_with('.') || ds.starts_with('#') || !ds.ends_with(suffix) - }) - .unwrap_or(true) -} - #[cfg(feature = "script_helper")] fn rhai_engine() -> Engine { Engine::new() @@ -120,6 +107,7 @@ impl<'reg> Registry<'reg> { escape_fn: Arc::new(html_escape), strict_mode: false, dev_mode: false, + prevent_indent: false, #[cfg(feature = "script_helper")] engine: Arc::new(rhai_engine()), #[cfg(feature = "script_helper")] @@ -178,7 +166,7 @@ impl<'reg> Registry<'reg> { /// Return dev mode state, default is false /// /// With dev mode turned on, handlebars enables a set of development - /// firendly features, that may affect its performance. + /// friendly features, that may affect its performance. pub fn dev_mode(&self) -> bool { self.dev_mode } @@ -186,9 +174,30 @@ impl<'reg> Registry<'reg> { /// Enable or disable dev mode /// /// With dev mode turned on, handlebars enables a set of development - /// firendly features, that may affect its performance. + /// friendly features, that may affect its performance. + /// + /// **Note that you have to enable dev mode before adding templates to + /// the registry**. Otherwise it won't take effect at all. pub fn set_dev_mode(&mut self, enabled: bool) { self.dev_mode = enabled; + + // clear template source when disabling dev mode + if !enabled { + self.template_sources.clear(); + } + } + + /// Enable or disable indent for partial include tag `{{>}}` + /// + /// By default handlebars keeps indent whitespaces for partial + /// include tag, to change this behaviour, set this toggle to `true`. + pub fn set_prevent_indent(&mut self, enable: bool) { + self.prevent_indent = enable; + } + + /// Return state for `prevent_indent` option, default to `false`. + pub fn prevent_indent(&self) -> bool { + self.prevent_indent } /// Register a `Template` @@ -196,6 +205,9 @@ impl<'reg> Registry<'reg> { /// This is infallible since the template has already been parsed and /// insert cannot fail. If there is an existing template with this name it /// will be overwritten. + /// + /// Dev mode doesn't apply for pre-compiled template because it's lifecycle + /// is not managed by the registry. pub fn register_template(&mut self, name: &str, tpl: Template) { self.templates.insert(name.to_string(), tpl); } @@ -211,7 +223,13 @@ impl<'reg> Registry<'reg> { where S: AsRef<str>, { - let template = Template::compile_with_name(tpl_str, name.to_owned())?; + let template = Template::compile2( + tpl_str.as_ref(), + TemplateOptions { + name: Some(name.to_owned()), + prevent_indent: self.prevent_indent, + }, + )?; self.register_template(name, template); Ok(()) } @@ -227,7 +245,10 @@ impl<'reg> Registry<'reg> { self.register_template_string(name, partial_str) } - /// Register a template from a path + /// Register a template from a path on file system + /// + /// If dev mode is enabled, the registry will keep reading the template file + /// from file system everytime it's visited. pub fn register_template_file<P>( &mut self, name: &str, @@ -262,11 +283,14 @@ impl<'reg> Registry<'reg> { /// /// This method is not available by default. /// You will need to enable the `dir_source` feature to use it. + /// + /// When dev_mode enabled, like `register_template_file`, templates is reloaded + /// from file system everytime it's visied. #[cfg(feature = "dir_source")] #[cfg_attr(docsrs, doc(cfg(feature = "dir_source")))] pub fn register_templates_directory<P>( &mut self, - tpl_extension: &'static str, + tpl_extension: &str, dir_path: P, ) -> Result<(), TemplateError> where @@ -274,40 +298,88 @@ impl<'reg> Registry<'reg> { { let dir_path = dir_path.as_ref(); - let prefix_len = if dir_path - .to_string_lossy() - .ends_with(|c| c == '\\' || c == '/') - // `/` will work on windows too so we still need to check - { - dir_path.to_string_lossy().len() - } else { - dir_path.to_string_lossy().len() + 1 - }; + // 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(|e| e.is_ok() && !filter_file(e.as_ref().unwrap(), tpl_extension)); + .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) + }) + // Rejects any hidden or temporary files. + .filter(|tpl_path| { + tpl_path + .file_stem() + .map(|stem| stem.to_string_lossy()) + .map(|stem| !(stem.starts_with('.') || stem.starts_with('#'))) + .unwrap_or(false) + }) + .filter_map(|tpl_path| { + tpl_path + .strip_prefix(dir_path) + .ok() + .map(|tpl_canonical_name| { + tpl_canonical_name + .with_extension("") + .components() + .map(|component| component.as_os_str().to_string_lossy()) + .collect::<Vec<_>>() + .join("/") + }) + .map(|tpl_canonical_name| (tpl_canonical_name, tpl_path)) + }); + + for (tpl_canonical_name, tpl_path) in dir_iter { + self.register_template_file(&tpl_canonical_name, &tpl_path)?; + } - for entry in dir_iter { - let entry = entry?; + Ok(()) + } - let tpl_path = entry.path(); - let tpl_file_path = entry.path().to_string_lossy(); + /// Register templates using a + /// [RustEmbed](https://github.com/pyros2097/rust-embed) type + /// + /// File names from embed struct are used as template name. + /// + /// ```skip + /// #[derive(RustEmbed)] + /// #[folder = "templates"] + /// struct Assets; + /// + /// let mut hbs = Handlebars::new(); + /// hbs.register_embed_templates::<Assets>(); + /// ``` + /// + #[cfg(feature = "rust-embed")] + #[cfg_attr(docsrs, doc(cfg(feature = "rust-embed")))] + pub fn register_embed_templates<E>(&mut self) -> Result<(), TemplateError> + where + E: RustEmbed, + { + for item in E::iter() { + let file_name = item.as_ref(); + if let Some(file) = E::get(file_name) { + let data = file.data; - let tpl_name = &tpl_file_path[prefix_len..tpl_file_path.len() - tpl_extension.len()]; - // replace platform path separator with our internal one - let tpl_canonical_name = tpl_name.replace(path::MAIN_SEPARATOR, "/"); - self.register_template_file(&tpl_canonical_name, &tpl_path)?; + let tpl_content = String::from_utf8_lossy(data.as_ref()); + self.register_template_string(file_name, tpl_content)?; + } } - Ok(()) } /// 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 @@ -335,7 +407,6 @@ impl<'reg> Registry<'reg> { /// (value * 100).to_string() + label /// ``` /// - /// #[cfg(feature = "script_helper")] #[cfg_attr(docsrs, doc(cfg(feature = "script_helper")))] pub fn register_script_helper(&mut self, name: &str, script: &str) -> Result<(), ScriptError> { @@ -347,6 +418,9 @@ impl<'reg> Registry<'reg> { } /// Register a [rhai](https://docs.rs/rhai/) script from file + /// + /// When dev mode is enable, script file is reloaded from original file + /// everytime it is called. #[cfg(feature = "script_helper")] #[cfg_attr(docsrs, doc(cfg(feature = "script_helper")))] pub fn register_script_helper_file<P>( @@ -365,6 +439,22 @@ impl<'reg> Registry<'reg> { self.register_script_helper(name, &script) } + /// Borrow a read-only reference to current rhai engine + #[cfg(feature = "script_helper")] + #[cfg_attr(docsrs, doc(cfg(feature = "script_helper")))] + pub fn engine(&self) -> &Engine { + self.engine.as_ref() + } + + /// Set a custom rhai engine for the registry. + /// + /// *Note that* you need to set custom engine before adding scripts. + #[cfg(feature = "script_helper")] + #[cfg_attr(docsrs, doc(cfg(feature = "script_helper")))] + pub fn set_engine(&mut self, engine: Engine) { + self.engine = Arc::new(engine); + } + /// Register a decorator pub fn register_decorator( &mut self, @@ -389,7 +479,7 @@ impl<'reg> Registry<'reg> { /// Get a reference to the current *escape fn*. pub fn get_escape_fn(&self) -> &dyn Fn(&str) -> String { - &*self.escape_fn + self.escape_fn.as_ref() } /// Return `true` if a template is registered for the given name @@ -411,7 +501,15 @@ impl<'reg> Registry<'reg> { let r = source .load() .map_err(|e| TemplateError::from((e, name.to_owned()))) - .and_then(|tpl_str| Template::compile_with_name(tpl_str, name.to_owned())) + .and_then(|tpl_str| { + Template::compile2( + tpl_str.as_ref(), + TemplateOptions { + name: Some(name.to_owned()), + prevent_indent: self.prevent_indent, + }, + ) + }) .map(Cow::Owned) .map_err(RenderError::from); Some(r) @@ -433,6 +531,7 @@ impl<'reg> Registry<'reg> { } /// Return a registered helper + #[inline] pub(crate) fn get_or_load_helper( &'reg self, name: &str, @@ -460,6 +559,7 @@ impl<'reg> Registry<'reg> { } /// Return a registered decorator + #[inline] pub(crate) fn get_decorator( &self, name: &str, @@ -468,6 +568,10 @@ impl<'reg> Registry<'reg> { } /// Return all templates registered + /// + /// **Note that** in dev mode, the template returned from this method may + /// not reflect its latest state. This method doesn't try to reload templates + /// from its source. pub fn get_templates(&self) -> &HashMap<String, Template> { &self.templates } @@ -475,6 +579,7 @@ impl<'reg> Registry<'reg> { /// Unregister all templates pub fn clear_templates(&mut self) { self.templates.clear(); + self.template_sources.clear(); } #[inline] @@ -489,7 +594,7 @@ impl<'reg> Registry<'reg> { { self.get_or_load_template(name).and_then(|t| { let mut render_context = RenderContext::new(t.name.as_ref()); - t.render(self, &ctx, &mut render_context, output) + t.render(self, ctx, &mut render_context, output) }) } @@ -516,7 +621,7 @@ impl<'reg> Registry<'reg> { output.into_string().map_err(RenderError::from) } - /// Render a registered template and write some data to the `std::io::Write` + /// Render a registered template and write data to the `std::io::Write` pub fn render_to_write<T, W>(&self, name: &str, data: &T, writer: W) -> Result<(), RenderError> where T: Serialize, @@ -527,6 +632,21 @@ impl<'reg> Registry<'reg> { self.render_to_output(name, &ctx, &mut output) } + /// Render a registered template using reusable `Context`, and write data to + /// the `std::io::Write` + pub fn render_with_context_to_write<W>( + &self, + name: &str, + ctx: &Context, + writer: W, + ) -> Result<(), RenderError> + where + W: Write, + { + let mut output = WriteOutput::new(writer); + self.render_to_output(name, ctx, &mut output) + } + /// Render a template string using current registry without registering it pub fn render_template<T>(&self, template_string: &str, data: &T) -> Result<String, RenderError> where @@ -537,23 +657,52 @@ impl<'reg> Registry<'reg> { Ok(writer.into_string()) } - /// Render a template string using reused context data + /// Render a template string using reusable context data pub fn render_template_with_context( &self, template_string: &str, ctx: &Context, ) -> Result<String, RenderError> { - let tpl = Template::compile(template_string)?; + let tpl = Template::compile2( + template_string, + TemplateOptions { + prevent_indent: self.prevent_indent, + ..Default::default() + }, + )?; let mut out = StringOutput::new(); { let mut render_context = RenderContext::new(None); - tpl.render(self, &ctx, &mut render_context, &mut out)?; + tpl.render(self, ctx, &mut render_context, &mut out)?; } out.into_string().map_err(RenderError::from) } + /// Render a template string using resuable context, and write data into + /// `std::io::Write` + pub fn render_template_with_context_to_write<W>( + &self, + template_string: &str, + ctx: &Context, + writer: W, + ) -> Result<(), RenderError> + where + W: Write, + { + let tpl = Template::compile2( + template_string, + TemplateOptions { + prevent_indent: self.prevent_indent, + ..Default::default() + }, + )?; + let mut render_context = RenderContext::new(None); + let mut out = WriteOutput::new(writer); + tpl.render(self, ctx, &mut render_context, &mut out) + } + /// Render a template string using current registry without registering it pub fn render_template_to_write<T, W>( &self, @@ -565,11 +714,8 @@ impl<'reg> Registry<'reg> { T: Serialize, W: Write, { - let tpl = Template::compile(template_string)?; let ctx = Context::wraps(data)?; - let mut render_context = RenderContext::new(None); - let mut out = WriteOutput::new(writer); - tpl.render(self, &ctx, &mut render_context, &mut out) + self.render_template_with_context_to_write(template_string, &ctx, writer) } } @@ -944,8 +1090,7 @@ mod test { .unwrap(); assert_eq!( "0123", - reg.render_with_context("t0", &Context::wraps(&data).unwrap()) - .unwrap() + reg.render_with_context("t0", &Context::from(data)).unwrap() ); } @@ -1089,4 +1234,17 @@ mod test { dir.close().unwrap(); } + + #[test] + #[cfg(feature = "script_helper")] + fn test_engine_access() { + use rhai::Engine; + + let mut registry = Registry::new(); + let mut eng = Engine::new(); + eng.set_max_string_size(1000); + registry.set_engine(eng); + + assert_eq!(1000, registry.engine().max_string_size()); + } } |