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}