1/// Macro that allows you to quickly define a handlebars helper by passing a
2/// name and a closure.
3///
4/// There are several types of arguments available to closure:
5///
6/// * Parameters are mapped to closure arguments one by one. Any declared
7/// parameters are required
8/// * Hash are mapped as named arguments and declared in a bracket block.
9/// All named arguments are optional so default value is required.
10/// * An optional `*args` provides a vector of all helper parameters.
11/// * An optional `**kwargs` provides a map of all helper hash.
12///
13/// # Examples
14///
15/// ```rust
16/// #[macro_use] extern crate handlebars;
17/// #[macro_use] extern crate serde_json;
18///
19/// handlebars_helper!(is_above_10: |x: u64| x > 10);
20/// handlebars_helper!(is_above: |x: u64, { compare: u64 = 10 }| x > compare);
21///
22/// # fn main() {
23/// #
24/// let mut handlebars = handlebars::Handlebars::new();
25/// handlebars.register_helper("is-above-10", Box::new(is_above_10));
26/// handlebars.register_helper("is-above", Box::new(is_above));
27///
28/// let result = handlebars
29/// .render_template("{{#if (is-above-10 12)}}great!{{else}}okay{{/if}}", &json!({}))
30/// .unwrap();
31/// assert_eq!(&result, "great!");
32/// let result2 = handlebars
33/// .render_template("{{#if (is-above 12 compare=10)}}great!{{else}}okay{{/if}}", &json!({}))
34/// .unwrap();
35/// assert_eq!(&result2, "great!");
36/// # }
37/// ```
38
39#[macro_export]
40macro_rules! handlebars_helper {
41 ($struct_name:ident: |$($name:ident: $tpe:tt$(<$($gen:ty),+>)?),*
42 $($(,)?{$($hash_name:ident: $hash_tpe:tt=$dft_val:literal),*})?
43 $($(,)?*$args:ident)?
44 $($(,)?**$kwargs:ident)?|
45 $body:expr ) => {
46 #[allow(non_camel_case_types)]
47 pub struct $struct_name;
48
49 impl $crate::HelperDef for $struct_name {
50 #[allow(unused_assignments)]
51 fn call_inner<'reg: 'rc, 'rc>(
52 &self,
53 h: &$crate::Helper<'reg, 'rc>,
54 r: &'reg $crate::Handlebars<'reg>,
55 _: &'rc $crate::Context,
56 _: &mut $crate::RenderContext<'reg, 'rc>,
57 ) -> std::result::Result<$crate::ScopedJson<'reg, 'rc>, $crate::RenderError> {
58 let mut param_idx = 0;
59
60 $(
61 let $name = h.param(param_idx)
62 .and_then(|x| {
63 if r.strict_mode() && x.is_value_missing() {
64 None
65 } else {
66 Some(x.value())
67 }
68 })
69 .ok_or_else(|| $crate::RenderError::new(&format!(
70 "`{}` helper: Couldn't read parameter {}",
71 stringify!($struct_name), stringify!($name),
72 )))
73 .and_then(|x|
74 handlebars_helper!(@as_json_value x, $tpe$(<$($gen),+>)?)
75 .ok_or_else(|| $crate::RenderError::new(&format!(
76 "`{}` helper: Couldn't convert parameter {} to type `{}`. \
77 It's {:?} as JSON. Got these params: {:?}",
78 stringify!($struct_name), stringify!($name), stringify!($tpe$(<$($gen),+>)?),
79 x, h.params(),
80 )))
81 )?;
82 param_idx += 1;
83 )*
84
85 $(
86 $(
87 let $hash_name = h.hash_get(stringify!($hash_name))
88 .map(|x| x.value())
89 .map(|x|
90 handlebars_helper!(@as_json_value x, $hash_tpe)
91 .ok_or_else(|| $crate::RenderError::new(&format!(
92 "`{}` helper: Couldn't convert hash {} to type `{}`. \
93 It's {:?} as JSON. Got these hash: {:?}",
94 stringify!($struct_name), stringify!($hash_name), stringify!($hash_tpe),
95 x, h.hash(),
96 )))
97 )
98 .unwrap_or_else(|| Ok($dft_val))?;
99 )*
100 )?
101
102 $(let $args = h.params().iter().map(|x| x.value()).collect::<Vec<&serde_json::Value>>();)?
103 $(let $kwargs = h.hash().iter().map(|(k, v)| (k.to_owned(), v.value())).collect::<std::collections::BTreeMap<&str, &serde_json::Value>>();)?
104
105 let result = $body;
106 Ok($crate::ScopedJson::Derived($crate::JsonValue::from(result)))
107 }
108 }
109 };
110
111 (@as_json_value $x:ident, object) => { $x.as_object() };
112 (@as_json_value $x:ident, array) => { $x.as_array() };
113 (@as_json_value $x:ident, str) => { $x.as_str() };
114 (@as_json_value $x:ident, i64) => { $x.as_i64() };
115 (@as_json_value $x:ident, u64) => { $x.as_u64() };
116 (@as_json_value $x:ident, f64) => { $x.as_f64() };
117 (@as_json_value $x:ident, bool) => { $x.as_bool() };
118 (@as_json_value $x:ident, null) => { $x.as_null() };
119 (@as_json_value $x:ident, Json) => { Some($x) };
120 (@as_json_value $x:ident, $tpe:tt$(<$($gen:ty),+>)?) => { serde_json::from_value::<$tpe$(<$($gen),+>)?>($x.clone()).ok() };
121}
122
123#[cfg(feature = "no_logging")]
124#[macro_use]
125#[doc(hidden)]
126pub mod logging {
127 /// This macro is defined if the `logging` feature is set.
128 ///
129 /// It ignores all logging calls inside the library.
130 #[doc(hidden)]
131 #[macro_export]
132 macro_rules! debug {
133 (target: $target:expr, $($arg:tt)*) => {};
134 ($($arg:tt)*) => {};
135 }
136
137 /// This macro is defined if the `logging` feature is not set.
138 ///
139 /// It ignores all logging calls inside the library.
140 #[doc(hidden)]
141 #[macro_export]
142 macro_rules! error {
143 (target: $target:expr, $($arg:tt)*) => {};
144 ($($arg:tt)*) => {};
145 }
146
147 /// This macro is defined if the `logging` feature is not set.
148 ///
149 /// It ignores all logging calls inside the library.
150 #[doc(hidden)]
151 #[macro_export]
152 macro_rules! info {
153 (target: $target:expr, $($arg:tt)*) => {};
154 ($($arg:tt)*) => {};
155 }
156
157 /// This macro is defined if the `logging` feature is not set.
158 ///
159 /// It ignores all logging calls inside the library.
160 #[doc(hidden)]
161 #[macro_export]
162 macro_rules! log {
163 (target: $target:expr, $($arg:tt)*) => {};
164 ($($arg:tt)*) => {};
165 }
166
167 /// This macro is defined if the `logging` feature is not set.
168 ///
169 /// It ignores all logging calls inside the library.
170 #[doc(hidden)]
171 #[macro_export]
172 macro_rules! trace {
173 (target: $target:expr, $($arg:tt)*) => {};
174 ($($arg:tt)*) => {};
175 }
176
177 /// This macro is defined if the `logging` feature is not set.
178 ///
179 /// It ignores all logging calls inside the library.
180 #[doc(hidden)]
181 #[macro_export]
182 macro_rules! warn {
183 (target: $target:expr, $($arg:tt)*) => {};
184 ($($arg:tt)*) => {};
185 }
186}
187