Skip to content

Commit

Permalink
(feat) use rc to make registry clonable
Browse files Browse the repository at this point in the history
  • Loading branch information
sunng87 committed Nov 29, 2020
1 parent 6aa35a4 commit 202d9ad
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 49 deletions.
11 changes: 0 additions & 11 deletions src/error.rs
Expand Up @@ -101,17 +101,6 @@ impl RenderError {
RenderError::new(&msg)
}

#[deprecated]
pub fn with<E>(cause: E) -> RenderError
where
E: Error + Send + Sync + 'static,
{
let mut e = RenderError::new(cause.to_string());
e.cause = Some(Box::new(cause));

e
}

pub fn from_error<E>(error_kind: &str, cause: E) -> RenderError
where
E: Error + Send + Sync + 'static,
Expand Down
54 changes: 26 additions & 28 deletions src/registry.rs
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::io::{Error as IoError, Write};
use std::path::Path;
use std::rc::Rc;

use serde::Serialize;

Expand Down Expand Up @@ -34,7 +35,7 @@ use crate::helpers::scripting::ScriptHelper;
///
/// An *escape fn* is represented as a `Box` to avoid unnecessary type
/// parameters (and because traits cannot be aliased using `type`).
pub type EscapeFn = Box<dyn Fn(&str) -> String + Send + Sync>;
pub type EscapeFn = Rc<dyn Fn(&str) -> String + Send + Sync>;

/// The default *escape fn* replaces the characters `&"<>`
/// with the equivalent html / xml entities.
Expand All @@ -51,12 +52,13 @@ pub fn no_escape(data: &str) -> String {
/// The single entry point of your Handlebars templates
///
/// It maintains compiled templates and registered helpers.
#[derive(Clone)]
pub struct Registry<'reg> {
templates: HashMap<String, Template>,
template_sources: HashMap<String, Box<dyn Source<Item = String, Error = IoError> + 'reg>>,
template_sources: HashMap<String, Rc<dyn Source<Item = String, Error = IoError> + 'reg>>,

helpers: HashMap<String, Box<dyn HelperDef + Send + Sync + 'reg>>,
decorators: HashMap<String, Box<dyn DecoratorDef + Send + Sync + 'reg>>,
helpers: HashMap<String, Rc<dyn HelperDef + 'reg>>,
decorators: HashMap<String, Rc<dyn DecoratorDef + 'reg>>,
escape_fn: EscapeFn,
strict_mode: bool,
dev_mode: bool,
Expand Down Expand Up @@ -109,7 +111,7 @@ impl<'reg> Registry<'reg> {
template_sources: HashMap::new(),
helpers: HashMap::new(),
decorators: HashMap::new(),
escape_fn: Box::new(html_escape),
escape_fn: Rc::new(html_escape),
strict_mode: false,
dev_mode: false,
#[cfg(feature = "script_helper")]
Expand Down Expand Up @@ -233,7 +235,7 @@ impl<'reg> Registry<'reg> {
self.register_template_string(name, template_string)?;
if self.dev_mode {
self.template_sources
.insert(name.to_owned(), Box::new(source));
.insert(name.to_owned(), Rc::new(source));
}

Ok(())
Expand Down Expand Up @@ -295,12 +297,8 @@ impl<'reg> Registry<'reg> {
}

/// Register a helper
pub fn register_helper(
&mut self,
name: &str,
def: Box<dyn HelperDef + Send + Sync + 'reg>,
) -> Option<Box<dyn HelperDef + Send + Sync + 'reg>> {
self.helpers.insert(name.to_string(), def)
pub fn register_helper(&mut self, name: &str, def: Box<dyn HelperDef + 'reg>) {
self.helpers.insert(name.to_string(), def.into());
}

/// Register a [rhai](https://docs.rs/rhai/) script as handlebars helper
Expand Down Expand Up @@ -329,12 +327,12 @@ impl<'reg> Registry<'reg> {
&mut self,
name: &str,
script: String,
) -> Result<Option<Box<dyn HelperDef + Send + Sync + 'reg>>, ScriptError> {
) -> Result<(), ScriptError> {
let compiled = self.engine.compile(&script)?;
let script_helper = ScriptHelper { script: compiled };
Ok(self
.helpers
.insert(name.to_string(), Box::new(script_helper)))
self.helpers
.insert(name.to_string(), Rc::new(script_helper));
Ok(())
}

/// Register a [rhai](https://docs.rs/rhai/) script from file
Expand All @@ -343,7 +341,7 @@ impl<'reg> Registry<'reg> {
&mut self,
name: &str,
script_path: P,
) -> Result<Option<Box<dyn HelperDef + Send + Sync + 'reg>>, ScriptError>
) -> Result<(), ScriptError>
where
P: AsRef<Path>,
{
Expand All @@ -354,25 +352,21 @@ impl<'reg> Registry<'reg> {
}

/// Register a decorator
pub fn register_decorator(
&mut self,
name: &str,
def: Box<dyn DecoratorDef + Send + Sync + 'reg>,
) -> Option<Box<dyn DecoratorDef + Send + Sync + 'reg>> {
self.decorators.insert(name.to_string(), def)
pub fn register_decorator(&mut self, name: &str, def: Box<dyn DecoratorDef + 'reg>) {
self.decorators.insert(name.to_string(), def.into());
}

/// Register a new *escape fn* to be used from now on by this registry.
pub fn register_escape_fn<F: 'static + Fn(&str) -> String + Send + Sync>(
&mut self,
escape_fn: F,
) {
self.escape_fn = Box::new(escape_fn);
self.escape_fn = Rc::new(escape_fn);
}

/// Restore the default *escape fn*.
pub fn unregister_escape_fn(&mut self) {
self.escape_fn = Box::new(html_escape);
self.escape_fn = Rc::new(html_escape);
}

/// Get a reference to the current *escape fn*.
Expand Down Expand Up @@ -408,8 +402,12 @@ impl<'reg> Registry<'reg> {
}

/// Return a registered helper
pub fn get_helper(&self, name: &str) -> Option<&(dyn HelperDef + Send + Sync + 'reg)> {
self.helpers.get(name).map(|v| v.as_ref())
/// TODO: attempt if we can make script helper reloadable
pub(crate) fn get_or_load_helper(
&'reg self,
name: &str,
) -> Option<Cow<'reg, Rc<dyn HelperDef + 'reg>>> {
self.helpers.get(name).map(|v| Cow::Borrowed(v))
}

#[inline]
Expand All @@ -418,7 +416,7 @@ impl<'reg> Registry<'reg> {
}

/// Return a registered decorator
pub fn get_decorator(&self, name: &str) -> Option<&(dyn DecoratorDef + Send + Sync + 'reg)> {
pub fn get_decorator(&self, name: &str) -> Option<&(dyn DecoratorDef + 'reg)> {
self.decorators.get(name).map(|v| v.as_ref())
}

Expand Down
18 changes: 8 additions & 10 deletions src/render.rs
@@ -1,7 +1,6 @@
use std::borrow::{Borrow, Cow};
use std::collections::{BTreeMap, VecDeque};
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;

use serde_json::value::Value as Json;
Expand Down Expand Up @@ -505,7 +504,7 @@ pub trait Evaluable {
}

fn call_helper_for_value<'reg: 'rc, 'rc>(
hd: &dyn HelperDef,
hd: &Rc<dyn HelperDef + 'rc>,
ht: &Helper<'reg, 'rc>,
r: &'reg Registry<'reg>,
ctx: &'rc Context,
Expand Down Expand Up @@ -584,13 +583,12 @@ impl Parameter {

let h = Helper::try_from_template(ht, registry, ctx, rc)?;
if let Some(ref d) = rc.get_local_helper(&name) {
let helper_def = d.deref();
call_helper_for_value(helper_def, &h, registry, ctx, rc)
call_helper_for_value(d, &h, registry, ctx, rc)
} else {
registry
.get_helper(&name)
.get_or_load_helper(&name)
.or_else(|| {
registry.get_helper(if ht.block {
registry.get_or_load_helper(if ht.block {
BLOCK_HELPER_MISSING
} else {
HELPER_MISSING
Expand All @@ -599,7 +597,7 @@ impl Parameter {
.ok_or_else(|| {
RenderError::new(format!("Helper not defined: {:?}", ht.name))
})
.and_then(move |d| call_helper_for_value(d, &h, registry, ctx, rc))
.and_then(move |d| call_helper_for_value(&d, &h, registry, ctx, rc))
}
}
}
Expand Down Expand Up @@ -693,9 +691,9 @@ fn render_helper<'reg: 'rc, 'rc>(
d.call(&h, registry, ctx, rc, out)
} else {
registry
.get_helper(h.name())
.get_or_load_helper(h.name())
.or_else(|| {
registry.get_helper(if ht.block {
registry.get_or_load_helper(if ht.block {
BLOCK_HELPER_MISSING
} else {
HELPER_MISSING
Expand Down Expand Up @@ -746,7 +744,7 @@ impl Renderable for TemplateElement {
Err(RenderError::strict_error(context_json.relative_path()))
} else {
// helper missing
if let Some(hook) = registry.get_helper(HELPER_MISSING) {
if let Some(hook) = registry.get_or_load_helper(HELPER_MISSING) {
let h = Helper::try_from_template(ht, registry, ctx, rc)?;
hook.call(&h, registry, ctx, rc, out)
} else {
Expand Down

0 comments on commit 202d9ad

Please sign in to comment.