liquid_core/
macros.rs

1/// A value::Value literal.
2///
3/// # Example
4///
5/// ```rust
6/// # use liquid_core::model::ValueView;
7/// #
8/// # fn main() {
9/// liquid_core::value!(5)
10///     .as_scalar().unwrap()
11///     .to_integer().unwrap();
12/// liquid_core::value!("foo")
13///     .as_scalar().unwrap()
14///     .to_kstr();
15/// liquid_core::value!([1, "2", 3])
16///     .as_array().unwrap();
17/// liquid_core::value!({"foo": 5})
18///     .as_object().unwrap();
19/// # }
20/// ```
21#[macro_export(local_inner_macros)]
22macro_rules! value {
23    ($($value:tt)+) => {
24        value_internal!($($value)+)
25    };
26}
27
28#[macro_export(local_inner_macros)]
29#[doc(hidden)]
30macro_rules! value_internal {
31    //////////////////////////////////////////////////////////////////////////
32    // The main implementation.
33    //
34    // Must be invoked as: value_internal!($($value)+)
35    //////////////////////////////////////////////////////////////////////////
36
37    (nil) => {
38        $crate::model::Value::Nil
39    };
40
41    (true) => {
42        $crate::model::Value::scalar(true)
43    };
44
45    (false) => {
46        $crate::model::Value::scalar(false)
47    };
48
49    ([]) => {
50        $crate::model::Value::Array(::std::default::Default::default())
51    };
52
53    ([ $($tt:tt)+ ]) => {
54        $crate::model::Value::Array(array_internal!(@array [] $($tt)+))
55    };
56
57    ({}) => {
58        $crate::model::Value::Object(::std::default::Default::default())
59    };
60
61    ({ $($tt:tt)+ }) => {
62        $crate::model::Value::Object({
63            let mut object = $crate::model::Object::new();
64            object_internal!(@object object () ($($tt)+) ($($tt)+));
65            object
66        })
67    };
68
69    ($other:ident) => {
70        $other
71    };
72
73    // Any Serialize type: numbers, strings, struct literals, variables etc.
74    // Must be below every other rule.
75    ($other:expr) => {
76        $crate::model::to_value(&$other).unwrap()
77    };
78}
79
80#[macro_export]
81#[doc(hidden)]
82macro_rules! value_unexpected {
83    () => {};
84}
85
86/// A value::Object literal.
87///
88/// # Example
89///
90/// ```rust
91/// # fn main() {
92/// liquid_core::object!({"foo": 5});
93/// # }
94/// ```
95#[macro_export(local_inner_macros)]
96macro_rules! object {
97    ($($value:tt)+) => {
98        object_internal!($($value)+)
99    };
100}
101
102#[macro_export(local_inner_macros)]
103#[doc(hidden)]
104macro_rules! object_internal {
105    //////////////////////////////////////////////////////////////////////////
106    // TT muncher for parsing the inside of an object {...}. Each entry is
107    // inserted into the given map variable.
108    //
109    // Must be invoked as: object_internal!(@object $object () ($($tt)*) ($($tt)*))
110    //
111    // We require two copies of the input tokens so that we can match on one
112    // copy and trigger errors on the other copy.
113    //////////////////////////////////////////////////////////////////////////
114
115    // Done.
116    (@object $object:ident () () ()) => {};
117
118    // Insert the current entry followed by trailing comma.
119    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
120        let _ = $object.insert(($($key)+).into(), $value);
121        object_internal!(@object $object () ($($rest)*) ($($rest)*));
122    };
123
124    // Current entry followed by unexpected token.
125    (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
126        object_unexpected!($unexpected);
127    };
128
129    // Insert the last entry without trailing comma.
130    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
131        let _ = $object.insert(($($key)+).into(), $value);
132    };
133
134    // Next value is `nil`.
135    (@object $object:ident ($($key:tt)+) (: nil $($rest:tt)*) $copy:tt) => {
136        object_internal!(@object $object [$($key)+] (value_internal!(nil)) $($rest)*);
137    };
138
139    // Next value is `true`.
140    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
141        object_internal!(@object $object [$($key)+] (value_internal!(true)) $($rest)*);
142    };
143
144    // Next value is `false`.
145    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
146        object_internal!(@object $object [$($key)+] (value_internal!(false)) $($rest)*);
147    };
148
149    // Next value is an array.
150    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
151        object_internal!(@object $object [$($key)+] (value_internal!([$($array)*])) $($rest)*);
152    };
153
154    // Next value is a map.
155    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
156        object_internal!(@object $object [$($key)+] (value_internal!({$($map)*})) $($rest)*);
157    };
158
159    // Next value is an expression followed by comma.
160    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
161        object_internal!(@object $object [$($key)+] (value_internal!($value)) , $($rest)*);
162    };
163
164    // Last value is an expression with no trailing comma.
165    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
166        object_internal!(@object $object [$($key)+] (value_internal!($value)));
167    };
168
169    // Missing value for last entry. Trigger a reasonable error message.
170    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
171        // "unexpected end of macro invocation"
172        object_internal!();
173    };
174
175    // Missing colon and value for last entry. Trigger a reasonable error
176    // message.
177    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
178        // "unexpected end of macro invocation"
179        object_internal!();
180    };
181
182    // Misplaced colon. Trigger a reasonable error message.
183    (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
184        // Takes no arguments so "no rules expected the token `:`".
185        object_unexpected!($colon);
186    };
187
188    // Found a comma inside a key. Trigger a reasonable error message.
189    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
190        // Takes no arguments so "no rules expected the token `,`".
191        object_unexpected!($comma);
192    };
193
194    // Key is fully parenthesized. This avoids clippy double_parens false
195    // positives because the parenthesization may be necessary here.
196    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
197        object_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
198    };
199
200    // Munch a token into the current key.
201    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
202        object_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
203    };
204
205    //////////////////////////////////////////////////////////////////////////
206    // The main implementation.
207    //
208    // Must be invoked as: object_internal!($($value)+)
209    //////////////////////////////////////////////////////////////////////////
210
211    ({}) => {
212        $crate::model::Object::new()
213    };
214
215    ({ $($tt:tt)+ }) => {
216        {
217            let mut object = $crate::model::Object::new();
218            object_internal!(@object object () ($($tt)+) ($($tt)+));
219            object
220        }
221    };
222
223    ($other:ident) => {
224        $other
225    };
226
227    // Any Serialize type: numbers, strings, struct literals, variables etc.
228    // Must be below every other rule.
229    ($other:expr) => {
230        $crate::model::to_object(&$other).unwrap()
231    };
232}
233
234#[macro_export]
235#[doc(hidden)]
236macro_rules! object_unexpected {
237    () => {};
238}
239
240/// A value::Array literal.
241///
242/// # Example
243///
244/// ```rust
245/// # use liquid_core::model::ValueView;
246/// #
247/// # fn main() {
248/// liquid_core::array!([1, "2", 3]);
249/// # }
250/// ```
251#[macro_export(local_inner_macros)]
252macro_rules! array {
253    ($($value:tt)+) => {
254        array_internal!($($value)+)
255    };
256}
257
258#[macro_export(local_inner_macros)]
259#[doc(hidden)]
260macro_rules! array_internal {
261    // Done with trailing comma.
262    (@array [$($elems:expr,)*]) => {
263        array_internal_vec![$($elems,)*]
264    };
265
266    // Done without trailing comma.
267    (@array [$($elems:expr),*]) => {
268        array_internal_vec![$($elems),*]
269    };
270
271    // Next element is `nil`.
272    (@array [$($elems:expr,)*] nil $($rest:tt)*) => {
273        array_internal!(@array [$($elems,)* value_internal!(nil)] $($rest)*)
274    };
275
276    // Next element is `true`.
277    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
278        array_internal!(@array [$($elems,)* value_internal!(true)] $($rest)*)
279    };
280
281    // Next element is `false`.
282    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
283        array_internal!(@array [$($elems,)* value_internal!(false)] $($rest)*)
284    };
285
286    // Next element is an array.
287    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
288        array_internal!(@array [$($elems,)* value_internal!([$($array)*])] $($rest)*)
289    };
290
291    // Next element is a map.
292    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
293        array_internal!(@array [$($elems,)* value_internal!({$($map)*})] $($rest)*)
294    };
295
296    // Next element is an expression followed by comma.
297    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
298        array_internal!(@array [$($elems,)* value_internal!($next),] $($rest)*)
299    };
300
301    // Last element is an expression with no trailing comma.
302    (@array [$($elems:expr,)*] $last:expr) => {
303        array_internal!(@array [$($elems,)* value_internal!($last)])
304    };
305
306    // Comma after the most recent element.
307    (@array [$($elems:expr),*] , $($rest:tt)*) => {
308        array_internal!(@array [$($elems,)*] $($rest)*)
309    };
310
311    // Unexpected token after most recent element.
312    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
313        array_unexpected!($unexpected)
314    };
315
316    //////////////////////////////////////////////////////////////////////////
317    // The main implementation.
318    //
319    // Must be invoked as: value_internal!($($value)+)
320    //////////////////////////////////////////////////////////////////////////
321
322    ([]) => {
323        $crate::model::Array::default()
324    };
325
326    ([ $($tt:tt)+ ]) => {
327        array_internal!(@array [] $($tt)+)
328    };
329
330    ($other:ident) => {
331        $other
332    };
333}
334
335#[macro_export]
336#[doc(hidden)]
337macro_rules! array_internal_vec {
338    ($($content:tt)*) => {
339        vec![$($content)*]
340    };
341}
342
343#[macro_export]
344#[doc(hidden)]
345macro_rules! array_unexpected {
346    () => {};
347}
348
349/// A value::Scalar literal.
350///
351/// # Example
352///
353/// ```rust
354/// # use liquid_core::model::ValueView;
355/// #
356/// # fn main() {
357/// liquid_core::scalar!(5)
358///     .to_integer().unwrap();
359/// liquid_core::scalar!("foo")
360///     .to_kstr();
361/// # }
362/// ```
363#[macro_export]
364macro_rules! scalar {
365    ($value:literal) => {
366        $crate::model::Scalar::new($value)
367    };
368
369    ($other:ident) => {
370        $other
371    };
372
373    // Any Serialize type: numbers, strings, struct literals, variables etc.
374    // Must be below every other rule.
375    ($other:expr) => {
376        $crate::model::to_scalar(&$other).unwrap()
377    };
378}
379
380#[allow(unused_macros)]
381#[macro_export]
382macro_rules! call_filter {
383    ($filter:expr, $input:expr) => {{
384        $crate::call_filter!($filter, $input, )
385    }};
386    ($filter:expr, $input:expr, $($args:expr),*) => {{
387        let positional = Box::new(vec![$($crate::Expression::Literal($crate::value!($args))),*].into_iter());
388        let keyword = Box::new(Vec::new().into_iter());
389        let args = $crate::parser::FilterArguments { positional, keyword };
390
391        let runtime = $crate::runtime::RuntimeBuilder::new().build();
392
393        let input = $crate::value!($input);
394
395        $crate::ParseFilter::parse(&$filter, args)
396            .and_then(|filter| $crate::Filter::evaluate(&*filter, &input, &runtime))
397    }};
398}