handlebars/
macros.rs

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/// # use handlebars::{handlebars_helper, Handlebars};
17/// # use serde_json::json;
18/// handlebars_helper!(is_above_10: |x: u64| x > 10);
19/// handlebars_helper!(is_above: |x: u64, { compare: u64 = 10 }| x > compare);
20///
21/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
22/// let mut handlebars = Handlebars::new();
23/// handlebars.register_helper("is-above-10", Box::new(is_above_10));
24/// handlebars.register_helper("is-above", Box::new(is_above));
25///
26/// let result = handlebars
27///     .render_template("{{#if (is-above-10 12)}}great!{{else}}okay{{/if}}", &json!({}))?;
28///  assert_eq!(&result, "great!");
29///
30/// let result2 = handlebars
31///     .render_template("{{#if (is-above 12 compare=10)}}great!{{else}}okay{{/if}}", &json!({}))?;
32///  assert_eq!(&result2, "great!");
33/// # Ok(()) }
34/// ```
35#[macro_export]
36macro_rules! handlebars_helper {
37    ($struct_name:ident: |$($name:ident: $tpe:tt$(<$($gen:ty),+>)?),*
38     $($(,)?{$($hash_name:ident: $hash_tpe:tt=$dft_val:literal),*})?
39     $($(,)?*$args:ident)?
40     $($(,)?**$kwargs:ident)?|
41     $body:expr ) => {
42        #[allow(non_camel_case_types)]
43        pub struct $struct_name;
44
45        impl $crate::HelperDef for $struct_name {
46            #[allow(unused_assignments)]
47            fn call_inner<'reg: 'rc, 'rc>(
48                &self,
49                h: &$crate::Helper<'rc>,
50                r: &'reg $crate::Handlebars<'reg>,
51                _: &'rc $crate::Context,
52                _: &mut $crate::RenderContext<'reg, 'rc>,
53            ) -> std::result::Result<$crate::ScopedJson<'rc>, $crate::RenderError> {
54                let mut param_idx = 0;
55
56                $(
57                    let $name = h.param(param_idx)
58                        .and_then(|x| {
59                            if r.strict_mode() && x.is_value_missing() {
60                                None
61                            } else {
62                                Some(x.value())
63                            }
64                        })
65                        .ok_or_else(|| $crate::RenderErrorReason::ParamNotFoundForName(stringify!($struct_name), stringify!($name).to_string()))
66                        .and_then(|x|
67                                  $crate::handlebars_helper!(@as_json_value x, $tpe$(<$($gen),+>)?)
68                                  .ok_or_else(|| $crate::RenderErrorReason::ParamTypeMismatchForName(stringify!($struct_name), stringify!($name).to_string(), stringify!($tpe$(<$($gen),+>)?).to_string()).into())
69                        )?;
70                    param_idx += 1;
71                )*
72
73                    $(
74                        $(
75                            let $hash_name = h.hash_get(stringify!($hash_name))
76                                .map(|x| x.value())
77                                .map(|x|
78                                     $crate::handlebars_helper!(@as_json_value x, $hash_tpe)
79                                     .ok_or_else(|| $crate::RenderErrorReason::HashTypeMismatchForName(
80                                         stringify!($struct_name), stringify!($hash_name).to_string(), stringify!($hash_tpe).to_string()
81                                     ))
82                                )
83                                .unwrap_or_else(|| Ok($dft_val))?;
84                        )*
85                    )?
86
87                    $(let $args = h.params().iter().map(|x| x.value()).collect::<Vec<&serde_json::Value>>();)?
88                    $(let $kwargs = h.hash().iter().map(|(k, v)| (k.to_owned(), v.value())).collect::<std::collections::BTreeMap<&str, &serde_json::Value>>();)?
89
90                let result = $body;
91                Ok($crate::ScopedJson::Derived($crate::JsonValue::from(result)))
92            }
93        }
94    };
95
96    (@as_json_value $x:ident, object) => { $x.as_object() };
97    (@as_json_value $x:ident, array) => { $x.as_array() };
98    (@as_json_value $x:ident, str) => { $x.as_str() };
99    (@as_json_value $x:ident, i64) => { $x.as_i64() };
100    (@as_json_value $x:ident, u64) => { $x.as_u64() };
101    (@as_json_value $x:ident, f64) => { $x.as_f64() };
102    (@as_json_value $x:ident, bool) => { $x.as_bool() };
103    (@as_json_value $x:ident, null) => { $x.as_null() };
104    (@as_json_value $x:ident, Json) => { Some($x) };
105    (@as_json_value $x:ident, $tpe:tt$(<$($gen:ty),+>)?) => { serde_json::from_value::<$tpe$(<$($gen),+>)?>($x.clone()).ok() };
106}
107
108#[cfg(feature = "no_logging")]
109#[macro_use]
110#[doc(hidden)]
111pub mod logging {
112    /// This macro is defined if the `logging` feature is set.
113    ///
114    /// It ignores all logging calls inside the library.
115    #[doc(hidden)]
116    #[macro_export]
117    macro_rules! debug {
118        (target: $target:expr, $($arg:tt)*) => {};
119        ($($arg:tt)*) => {};
120    }
121
122    /// This macro is defined if the `logging` feature is not set.
123    ///
124    /// It ignores all logging calls inside the library.
125    #[doc(hidden)]
126    #[macro_export]
127    macro_rules! error {
128        (target: $target:expr, $($arg:tt)*) => {};
129        ($($arg:tt)*) => {};
130    }
131
132    /// This macro is defined if the `logging` feature is not set.
133    ///
134    /// It ignores all logging calls inside the library.
135    #[doc(hidden)]
136    #[macro_export]
137    macro_rules! info {
138        (target: $target:expr, $($arg:tt)*) => {};
139        ($($arg:tt)*) => {};
140    }
141
142    /// This macro is defined if the `logging` feature is not set.
143    ///
144    /// It ignores all logging calls inside the library.
145    #[doc(hidden)]
146    #[macro_export]
147    macro_rules! log {
148        (target: $target:expr, $($arg:tt)*) => {};
149        ($($arg:tt)*) => {};
150    }
151
152    /// This macro is defined if the `logging` feature is not set.
153    ///
154    /// It ignores all logging calls inside the library.
155    #[doc(hidden)]
156    #[macro_export]
157    macro_rules! trace {
158        (target: $target:expr, $($arg:tt)*) => {};
159        ($($arg:tt)*) => {};
160    }
161
162    /// This macro is defined if the `logging` feature is not set.
163    ///
164    /// It ignores all logging calls inside the library.
165    #[doc(hidden)]
166    #[macro_export]
167    macro_rules! warn {
168        (target: $target:expr, $($arg:tt)*) => {};
169        ($($arg:tt)*) => {};
170    }
171}