minijinja/
macros.rs

1// `ok!` and `some!` are less bloaty alternatives to the standard library's try operator (`?`).
2// Since we do not need type conversions in this crate we can fall back to much easier match
3// patterns that compile faster and produce less bloaty code.
4
5macro_rules! ok {
6    ($expr:expr) => {
7        match $expr {
8            Ok(val) => val,
9            Err(err) => return Err(err),
10        }
11    };
12}
13
14macro_rules! some {
15    ($expr:expr) => {
16        match $expr {
17            Some(val) => val,
18            None => return None,
19        }
20    };
21}
22
23/// Hidden utility module for the [`context!`](crate::context!) macro.
24#[doc(hidden)]
25pub mod __context {
26    use crate::value::{Value, ValueMap};
27    use crate::Environment;
28    use std::rc::Rc;
29
30    #[inline(always)]
31    pub fn make() -> ValueMap {
32        ValueMap::default()
33    }
34
35    #[inline(always)]
36    pub fn add(ctx: &mut ValueMap, key: &'static str, value: Value) {
37        ctx.insert(key.into(), value);
38    }
39
40    #[inline(always)]
41    pub fn build(ctx: ValueMap) -> Value {
42        Value::from_object(ctx)
43    }
44
45    pub fn thread_local_env() -> Rc<Environment<'static>> {
46        thread_local! {
47            static ENV: Rc<Environment<'static>> = Rc::new(Environment::new());
48        }
49        ENV.with(|x| x.clone())
50    }
51}
52
53/// Creates a template context from keys and values or merging in another value.
54///
55/// ```rust
56/// # use minijinja::context;
57/// let ctx = context!{
58///     name => "Peter",
59///     location => "World",
60/// };
61/// ```
62///
63/// Alternatively if the variable name matches the key name it can
64/// be omitted:
65///
66/// ```rust
67/// # use minijinja::context;
68/// let name = "Peter";
69/// let ctx = context!{ name };
70/// ```
71///
72/// The return value is a [`Value`](crate::value::Value).
73///
74/// Note that [`context!`](crate::context!) can also be used recursively if you need to
75/// create nested objects:
76///
77/// ```rust
78/// # use minijinja::context;
79/// let ctx = context! {
80///     nav => vec![
81///         context!(path => "/", title => "Index"),
82///         context!(path => "/downloads", title => "Downloads"),
83///         context!(path => "/faq", title => "FAQ"),
84///     ]
85/// };
86/// ```
87///
88/// Additionally the macro supports a second syntax that can merge other
89/// contexts or values.  In that case one or more values need to be
90/// passed with a leading `..` operator.  This is useful to supply extra
91/// values into render in a common place.  The order of precedence is
92/// left to right:
93///
94/// ```rust
95/// # use minijinja::context;
96/// let ctx = context! { a => "A" };
97/// let ctx = context! { ..ctx, ..context! {
98///     b => "B"
99/// }};
100///
101/// // or
102///
103/// let ctx = context! {
104///     a => "A",
105///     ..context! {
106///         b => "B"
107///     }
108/// };
109/// ```
110///
111/// The merge works with an value, not just values created by the `context!`
112/// macro and is performed lazy.  This means it also works with dynamic
113/// [`Object`](crate::value::Object)s.  The merge uses the underlying
114/// [`merge_maps`](crate::value::merge_maps) function.
115///
116/// # Note on Conversions
117///
118/// This macro uses [`Value::from_serialize`](crate::Value::from_serialize)
119/// for conversions.
120///
121/// This macro currently does not move passed values.  Future versions of
122/// MiniJinja are going to change the move behavior and it's recommended to not
123/// depend on this implicit reference behavior.  You should thus pass values
124/// with `&value` if you intend on still being able to reference them
125/// after the macro invocation.
126#[macro_export]
127macro_rules! context {
128    () => {
129        $crate::__context::build($crate::__context::make())
130    };
131    (
132        $($key:ident $(=> $value:expr)?),*
133        $(, .. $ctx:expr),* $(,)?
134    ) => {{
135        let mut ctx = $crate::__context::make();
136        $(
137            $crate::__context_pair!(ctx, $key $(=> $value)?);
138        )*
139        let ctx = $crate::__context::build(ctx);
140        let merge_ctx = [
141            $(
142                $crate::value::Value::from($ctx),
143            )*
144        ];
145        if merge_ctx.is_empty() {
146            ctx
147        } else {
148            $crate::value::merge_maps(
149                merge_ctx.into_iter().rev().chain(::std::iter::once(ctx)))
150        }
151    }};
152    (
153        $(.. $ctx:expr),* $(,)?
154    ) => {{
155        $crate::value::merge_maps([
156            $(
157                $crate::value::Value::from($ctx),
158            )*
159        ].into_iter().rev())
160    }};
161}
162
163#[macro_export]
164#[doc(hidden)]
165macro_rules! __context_pair {
166    ($ctx:ident, $key:ident) => {{
167        $crate::__context_pair!($ctx, $key => $key);
168    }};
169    ($ctx:ident, $key:ident => $value:expr) => {
170        $crate::__context::add(
171            &mut $ctx,
172            stringify!($key),
173            $crate::value::Value::from_serialize(&$value),
174        );
175    };
176}
177
178/// An utility macro to create arguments for function calls.
179///
180/// This creates a slice of values on the stack which can be
181/// passed to [`call`](crate::value::Value::call),
182/// [`call_method`](crate::value::Value::call_method),
183/// [`apply_filter`](crate::State::apply_filter),
184/// [`perform_test`](crate::State::perform_test) or similar
185/// APIs that take slices of values.
186///
187/// It supports both positional and keyword arguments.
188/// To mark a parameter as keyword argument define it as
189/// `name => value`, otherwise just use `value`.
190///
191/// ```
192/// # use minijinja::{value::Value, args, Environment};
193/// # let env = Environment::default();
194/// # let state = &env.empty_state();
195/// # let value = Value::from(());
196/// value.call(state, args!(1, 2, foo => "bar"));
197/// ```
198///
199/// Note that this like [`context!`](crate::context) goes through
200/// [`Value::from_serialize`](crate::value::Value::from_serialize).
201#[macro_export]
202macro_rules! args {
203    () => { &[][..] as &[$crate::value::Value] };
204    ($($arg:tt)*) => { $crate::__args_helper!(branch [[$($arg)*]], [$($arg)*]) };
205}
206
207/// Utility macro for `args!`
208#[macro_export]
209#[doc(hidden)]
210macro_rules! __args_helper {
211    // branch helper between `args` and `kwargs`.
212    //
213    // It analyzes the first bracket enclosed tt bundle to see if kwargs are
214    // used.  If yes, it uses `kwargs` to handle the second tt
215    // bundle, otherwise it uses `args`.
216    (branch [[]], $args:tt) => { $crate::__args_helper!(args $args) };
217    (branch [[$n:ident => $e:expr]], $args:tt) => { $crate::__args_helper!(kwargs $args) };
218    (branch [[$n:ident => $e:expr, $($r:tt)*]], $args:tt) => { $crate::__args_helper!(kwargs $args) };
219    (branch [[$e:expr]], $args:tt) => { $crate::__args_helper!(args $args) };
220    (branch [[$e:expr, $($rest:tt)*]], $args:tt) => { $crate::__args_helper!(branch [[$($rest)*]], $args) };
221
222    // creates args on the stack
223    (args [$($arg:tt)*]) => {&{
224        let mut args = Vec::<$crate::value::Value>::new();
225        $crate::__args_helper!(peel args, args, false, [$($arg)*]);
226        args
227    }[..]};
228
229    // creates args with kwargs on the stack
230    (kwargs [$($arg:tt)*]) => {&{
231        let mut args = Vec::<$crate::value::Value>::new();
232        let mut kwargs = Vec::<(&str, $crate::value::Value)>::new();
233        $crate::__args_helper!(peel args, kwargs, false, [$($arg)*]);
234        args.push($crate::value::Kwargs::from_iter(kwargs.into_iter()).into());
235        args
236    }[..]};
237
238    // Peels a single argument from the arguments and stuffs them into
239    // `$args` or `$kwargs` depending on type.
240    (peel $args:ident, $kwargs:ident, $has_kwargs:ident, []) => {};
241    (peel $args:ident, $kwargs:ident, $has_kwargs:ident, [$name:ident => $expr:expr]) => {
242        $kwargs.push((stringify!($name), $crate::value::Value::from_serialize(&$expr)));
243    };
244    (peel $args:ident, $kwargs:ident, $has_kwargs:ident, [$name:ident => $expr:expr, $($rest:tt)*]) => {
245        $kwargs.push((stringify!($name), $crate::value::Value::from_serialize(&$expr)));
246        $crate::__args_helper!(peel $args, $kwargs, true, [$($rest)*]);
247    };
248    (peel $args:ident, $kwargs:ident, false, [$expr:expr]) => {
249        $args.push($crate::value::Value::from_serialize(&$expr));
250    };
251    (peel $args:ident, $kwargs:ident, false, [$expr:expr, $($rest:tt)*]) => {
252        $args.push($crate::value::Value::from_serialize(&$expr));
253        $crate::__args_helper!(peel $args, $kwargs, false, [$($rest)*]);
254    };
255}
256
257/// A macro similar to [`format!`] but that uses MiniJinja for rendering.
258///
259/// This can be used to quickly render a MiniJinja template into a string
260/// without having to create an environment first which can be useful in
261/// some situations.  Note however that the template is re-parsed every
262/// time the [`render!`](crate::render) macro is called which is potentially
263/// slow.
264///
265/// There are two forms for this macro.  The default form takes template
266/// source and context variables, the extended form also lets you provide
267/// a custom environment that should be used rather than a default one.
268/// The context variables are passed the same way as with the
269/// [`context!`](crate::context) macro.
270///
271/// # Example
272///
273/// Passing context explicitly:
274///
275/// ```
276/// # use minijinja::render;
277/// println!("{}", render!("Hello {{ name }}!", name => "World"));
278/// ```
279///
280/// Passing variables with the default name:
281///
282/// ```
283/// # use minijinja::render;
284/// let name = "World";
285/// println!("{}", render!("Hello {{ name }}!", name));
286/// ```
287///
288/// Passing an explicit environment:
289///
290/// ```
291/// # use minijinja::{Environment, render};
292/// let env = Environment::new();
293/// println!("{}", render!(in env, "Hello {{ name }}!", name => "World"));
294/// ```
295///
296/// # Panics
297///
298/// This macro panics if the format string is an invalid template or the
299/// template evaluation failed.
300#[macro_export]
301macro_rules! render {
302    (
303        in $env:expr,
304        $tmpl:expr
305        $(, $key:ident $(=> $value:expr)?)* $(,)?
306    ) => {
307        ($env).render_str($tmpl, $crate::context! { $($key $(=> $value)? ,)* })
308            .expect("failed to render expression")
309    };
310    (
311        $tmpl:expr
312        $(, $key:ident $(=> $value:expr)?)* $(,)?
313    ) => {
314        $crate::render!(in $crate::__context::thread_local_env(), $tmpl, $($key $(=> $value)? ,)*)
315    }
316}