Skip to content

Instantly share code, notes, and snippets.

@alexsavio
Created October 10, 2023 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexsavio/c2e40d87ab58c653336d0316db2b3f02 to your computer and use it in GitHub Desktop.
Save alexsavio/c2e40d87ab58c653336d0316db2b3f02 to your computer and use it in GitHub Desktop.
handlebars helpers
use handlebars::{Handlebars, RenderError, HelperDef, RenderContext, Helper, Context, handlebars_helper};
use serde_json::Value as Json;
// #[allow(non_camel_case_types)]
// pub struct is_defined;
// impl HelperDef for is_defined {
// fn call_inner<'reg: 'rc, 'rc>(
// &self,
// h: &Helper<'reg, 'rc>,
// hbs: &'reg Handlebars,
// ctx: &'rc Context,
// _: &mut RenderContext<'reg, 'rc>,
// ) -> Result<bool, RenderError> {
// let params = h.params();
// if params.len() != 1 {
// return Err(RenderError::new(
// "is_defined: requires one parameter".to_owned(),
// ));
// }
// let param = &params[0].as_str().unwrap();
// ctx.data().get(param).map_or(Ok(false), |_| Ok(true))
// }
// }
pub fn register_helpers(handlebars: &mut Handlebars) {
{
handlebars_helper!(to_lower_case: |v: str| v.to_lowercase());
handlebars.register_helper("to_lower_case", Box::new(to_lower_case))
}
{
handlebars_helper!(to_upper_case: |v: str| v.to_uppercase());
handlebars.register_helper("to_upper_case", Box::new(to_upper_case))
}
{
handlebars_helper!(trim: |v: str| v.trim());
handlebars.register_helper("trim", Box::new(trim))
}
{
handlebars_helper!(trim_start: |v: str| v.trim_start());
handlebars.register_helper("trim_start", Box::new(trim_start))
}
{
handlebars_helper!(trim_end: |v: str| v.trim_end());
handlebars.register_helper("trim_end", Box::new(trim_end))
}
{
handlebars_helper!(contains: |v: str, s: str| v.contains(s));
handlebars.register_helper("contains", Box::new(contains))
}
{
handlebars_helper!(starts_with: |v: str, s: str| v.starts_with(s));
handlebars.register_helper("starts_with", Box::new(starts_with))
}
{
handlebars_helper!(ends_with: |v: str, s: str| v.ends_with(s));
handlebars.register_helper("ends_with", Box::new(ends_with))
}
{
handlebars_helper!(isdefined: |v: Json| !v.is_null());
handlebars.register_helper("isdefined", Box::new(isdefined));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
use serde_json::json;
use handlebars::{no_escape, Handlebars};
pub fn new_handlebars<'reg>() -> Handlebars<'reg> {
let mut handlebars = Handlebars::new();
handlebars.set_strict_mode(true);
handlebars.register_escape_fn(no_escape); //html escaping is the default and cause issue
register_helpers(&mut handlebars);
handlebars
}
#[macro_export]
macro_rules! assert_renders {
($($arg:expr),+$(,)?) => {{
use std::collections::HashMap;
use pretty_assertions::assert_eq;
let vs: HashMap<String, String> = HashMap::new();
let mut handlebars = new_handlebars();
$({
let sample: (&str, &str) = $arg;
handlebars.register_template_string(&sample.0, &sample.0).expect("register_template_string");
assert_eq!(handlebars.render(&sample.0, &vs).expect("render"), sample.1.to_owned());
})*
Ok(())
}}
}
fn test_condition(condition: &str, expected: bool) {
let handlebars = new_handlebars();
let result = handlebars
.render_template(
&format!(
"{{{{#if {condition}}}}}lorem{{{{else}}}}ipsum{{{{/if}}}}",
condition = condition
),
&json!({}),
)
.unwrap();
assert_eq!(&result, if expected { "lorem" } else { "ipsum" }, "testing condition: {}", condition);
}
// fn test_condition_given_data(condition: &str, data: Json, expected: bool) {
// println!("result: {}", result.unwrap());
// let result = handlebars
// .render_template(
// &format!(
// "{{{{#if {condition}}}}}lorem{{{{else}}}}ipsum{{{{/if}}}}",
// condition = condition
// ),
// &data,
// )
// .unwrap();
// assert_eq!(&result, if expected { "lorem" } else { "ipsum" }, "testing condition: {}", condition);
// }
#[test]
fn test_register_string_helpers() -> Result<(), Box<dyn Error>> {
assert_renders![
(r##"{{ to_lower_case "Hello foo-bars" }}"##, r##"hello foo-bars"##),
(r##"{{ to_upper_case "Hello foo-bars" }}"##, r##"HELLO FOO-BARS"##)
]
}
#[test]
fn test_helper_trim() -> Result<(), Box<dyn Error>> {
assert_renders![
(r##"{{ trim "foo" }}"##, r##"foo"##),
(r##"{{ trim " foo" }}"##, r##"foo"##),
(r##"{{ trim "foo " }}"##, r##"foo"##),
(r##"{{ trim " foo " }}"##, r##"foo"##)
]
}
#[test]
fn test_helper_trim_start() -> Result<(), Box<dyn Error>> {
assert_renders![
(r##"{{ trim_start "foo" }}"##, r##"foo"##),
(r##"{{ trim_start " foo" }}"##, r##"foo"##),
(r##"{{ trim_start "foo " }}"##, r##"foo "##),
(r##"{{ trim_start " foo " }}"##, r##"foo "##)
]
}
#[test]
fn test_helper_contains() -> Result<(), Box<dyn Error>> {
test_condition(r#"( contains "foo" "bar" )"#, false);
test_condition(r#"( contains "foo" "foo" )"#, true);
test_condition(r#"( contains "barfoobar" "foo" )"#, true);
test_condition(r#"( contains "foo" "barfoobar" )"#, false);
Ok(())
}
#[test]
fn test_helper_starts_with() -> Result<(), Box<dyn Error>> {
test_condition(r#"( starts_with "foo" "bar" )"#, false);
test_condition(r#"( starts_with "foobar" "foo" )"#, true);
test_condition(r#"( starts_with "foo" "foobar" )"#, false);
Ok(())
}
#[test]
fn test_helper_ends_with() -> Result<(), Box<dyn Error>> {
test_condition(r#"( ends_with "foo" "bar" )"#, false);
test_condition(r#"( ends_with "foobar" "bar" )"#, true);
test_condition(r#"( ends_with "foo" "foobar" )"#, false);
Ok(())
}
#[test]
fn test_isdefined() -> Result<(), Box<dyn Error>> {
let handlebars = new_handlebars();
let result = handlebars.render_template(
r#"{{isdefined a}} {{isdefined b}} {{#if (isdefined a)}}a{{/if}} {{#if (isdefined b)}}b{{/if}}"#,
&json!({"a": 1})
);
assert_eq!(result.unwrap(), "true false a ");
Ok(())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment