blob: a19a1ee0f1ace1aed1ee64f3fd6370c9681fc157 [file] [log] [blame]
use render::{Directive, RenderContext};
use registry::Registry;
use error::RenderError;
pub use self::inline::INLINE_DIRECTIVE;
/// Decorator Definition
///
/// Implement this trait to define your own decorators or directives. Currently
/// decorator shares same definition with helper.
///
/// In handlebars, it is recommanded to use decorator to change context data and update helper
/// definition.
/// ## Updating context data
///
/// In decorator, you can change some context data your are about to render.
///
/// ```
/// use handlebars::*;
///
/// fn update_data(_: &Decorator, _: &Handlebars, rc: &mut RenderContext)
/// -> Result<(), RenderError> {
/// // modify json object
/// let mut data = rc.context_mut().data_mut();
/// if let Some(ref mut m) = data.as_object_mut() {
/// m.insert("hello".to_string(), to_json(&"world".to_owned()));
/// }
/// Ok(())
/// }
///
/// ```
///
/// ## Define local helper
///
/// You can override behavior of a helper from position of decorator to the end of template.
///
/// ```
/// use handlebars::*;
///
/// fn override_helper(_: &Decorator, _: &Handlebars, rc: &mut RenderContext)
/// -> Result<(), RenderError> {
/// let new_helper = |h: &Helper, _: &Handlebars, rc: &mut RenderContext|
/// -> Result<(), RenderError> {
/// // your helper logic
/// Ok(())
/// };
/// rc.register_local_helper("distance", Box::new(new_helper));
/// Ok(())
/// }
/// ```
///
pub trait DirectiveDef: Send + Sync {
fn call(&self, d: &Directive, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>;
}
/// implement DirectiveDef for bare function so we can use function as directive
impl<
F: Send
+ Sync
+ for<'b, 'c, 'd, 'e> Fn(&'b Directive, &'c Registry, &'d mut RenderContext)
-> Result<(), RenderError>,
> DirectiveDef for F
{
fn call(&self, d: &Directive, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
(*self)(d, r, rc)
}
}
mod inline;
#[cfg(test)]
mod test {
use registry::Registry;
use context::{self, as_string, Context};
use render::{Directive, Helper, RenderContext};
use error::RenderError;
#[test]
fn test_register_decorator() {
let mut handlebars = Registry::new();
handlebars
.register_template_string("t0", "{{*foo}}".to_string())
.unwrap();
let data = btreemap! {
"hello".to_string() => "world".to_string()
};
assert!(handlebars.render("t0", &data).is_err());
handlebars.register_decorator(
"foo",
Box::new(
|_: &Directive, _: &Registry, _: &mut RenderContext| -> Result<(), RenderError> {
Ok(())
},
),
);
assert_eq!(handlebars.render("t0", &data).ok().unwrap(), "".to_string());
}
#[test]
fn test_update_data_with_decorator() {
let mut handlebars = Registry::new();
handlebars
.register_template_string("t0", "{{hello}}{{*foo}}{{hello}}".to_string())
.unwrap();
let data = btreemap! {
"hello".to_string() => "world".to_string()
};
handlebars.register_decorator(
"foo",
Box::new(
|_: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
// modify json object
let ctx_ref = rc.context_mut();
let data = ctx_ref.data_mut();
if let Some(ref mut m) = data.as_object_mut().as_mut() {
m.insert("hello".to_string(), context::to_json(&"war".to_owned()));
}
Ok(())
},
),
);
assert_eq!(
handlebars.render("t0", &data).ok().unwrap(),
"worldwar".to_string()
);
let data2 = 0;
handlebars.register_decorator(
"bar",
Box::new(
|d: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
// modify value
let v = d.param(0)
.and_then(|v| Context::wraps(v.value()).ok())
.unwrap_or(Context::null());
*rc.context_mut() = v;
Ok(())
},
),
);
handlebars
.register_template_string("t1", "{{this}}{{*bar 1}}{{this}}".to_string())
.unwrap();
assert_eq!(
handlebars.render("t1", &data2).ok().unwrap(),
"01".to_string()
);
handlebars
.register_template_string(
"t2",
"{{this}}{{*bar \"string_literal\"}}{{this}}".to_string(),
)
.unwrap();
assert_eq!(
handlebars.render("t2", &data2).ok().unwrap(),
"0string_literal".to_string()
);
handlebars
.register_template_string("t3", "{{this}}{{*bar}}{{this}}".to_string())
.unwrap();
assert_eq!(
handlebars.render("t3", &data2).ok().unwrap(),
"0".to_string()
);
}
#[test]
fn test_local_helper_with_decorator() {
let mut handlebars = Registry::new();
handlebars
.register_template_string(
"t0",
"{{distance 4.5}},{{*foo \"miles\"}}{{distance 10.1}},{{*bar}}{{distance 3.4}}"
.to_string(),
)
.unwrap();
handlebars.register_helper(
"distance",
Box::new(
|h: &Helper, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
let s = format!(
"{}m",
h.param(0,)
.map(|v| v.value(),)
.unwrap_or(&context::to_json(&0),)
);
try!(rc.writer().write(s.into_bytes().as_ref()));
Ok(())
},
),
);
handlebars.register_decorator(
"foo",
Box::new(
|d: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
let new_unit = d.param(0)
.and_then(|v| as_string(v.value()))
.unwrap_or("")
.to_owned();
let new_helper = move |h: &Helper,
_: &Registry,
rc: &mut RenderContext|
-> Result<(), RenderError> {
let s = format!(
"{}{}",
h.param(0,)
.map(|v| v.value(),)
.unwrap_or(&context::to_json(&0),),
new_unit
);
try!(rc.writer().write(s.into_bytes().as_ref()));
Ok(())
};
rc.register_local_helper("distance", Box::new(new_helper));
Ok(())
},
),
);
handlebars.register_decorator(
"bar",
Box::new(
|_: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
rc.unregister_local_helper("distance");
Ok(())
},
),
);
assert_eq!(
handlebars.render("t0", &0).ok().unwrap(),
"4.5m,10.1miles,3.4m".to_owned()
);
}
}