Skip to main content

expry/
macros.rs

1/// Construct a `expry::DecodedValue` from a JSON-like literal.
2///
3/// ```
4/// # use expry::value;
5/// #
6/// let value = value!({
7///     "code": 200,
8///     "success": true,
9///     "payload": {
10///         "features": [
11///             "expry",
12///             "json"
13///         ]
14///     }
15/// });
16/// ```
17///
18/// Variables or expressions can be interpolated into the JSON literal. Any type
19/// interpolated into an array element or object value must implement the From<_>
20/// trait, while any type interpolated into a object key must
21/// implement `Into<String>`. If the `Serialize` implementation of the
22/// interpolated type decides to fail, or if the interpolated type contains a
23/// map with non-string keys, the `json!` macro will panic.
24///
25/// ```
26/// # use expry::value;
27/// let code = 200;
28/// let features = vec!["expry", "json"];
29///
30/// let value = value!({
31///     "code": code,
32///     "success": code == 200,
33///     "payload": {
34///         features[0]: features[1]
35///     }
36/// });
37/// ```
38///
39/// Trailing commas are allowed inside both arrays and objects.
40///
41/// ```
42/// # use expry::value;
43/// let value = value!([
44///     "notice",
45///     "the",
46///     "trailing",
47///     "comma -->",
48/// ]);
49/// ```
50/// Adapted from <https://github.com/serde-rs/json/blob/master/src/macros.rs>.
51#[macro_export(local_inner_macros)]
52macro_rules! value {
53    // Hide distracting implementation details from the generated rustdoc.
54    ($($json:tt)+) => {
55        value_internal!($($json)+)
56    };
57}
58
59// Changes are fine as long as `value_internal!` does not call any new helper
60// macros and can still be invoked as `value_internal!($($json)+)`.
61#[macro_export(local_inner_macros)]
62#[doc(hidden)]
63macro_rules! value_internal {
64    //////////////////////////////////////////////////////////////////////////
65    // TT muncher for parsing the inside of an array [...]. Produces a vec![...]
66    // of the elements.
67    //
68    // Must be invoked as: value_internal!(@array [] $($tt)*)
69    //////////////////////////////////////////////////////////////////////////
70
71    // Done with trailing comma.
72    (@array [$($elems:expr,)*]) => {
73        value_internal_vec![$($elems,)*]
74    };
75
76    // Done without trailing comma.
77    (@array [$($elems:expr),*]) => {
78        value_internal_vec![$($elems),*]
79    };
80
81    // Next element is `null`.
82    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
83        value_internal!(@array [$($elems,)* value_internal!(null)] $($rest)*)
84    };
85
86    // Next element is `true`.
87    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
88        value_internal!(@array [$($elems,)* value_internal!(true)] $($rest)*)
89    };
90
91    // Next element is `false`.
92    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
93        value_internal!(@array [$($elems,)* value_internal!(false)] $($rest)*)
94    };
95
96    // Next element is an array.
97    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
98        value_internal!(@array [$($elems,)* value_internal!([$($array)*])] $($rest)*)
99    };
100
101    // Next element is a map.
102    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
103        value_internal!(@array [$($elems,)* value_internal!({$($map)*})] $($rest)*)
104    };
105
106    // Next element is an expression followed by comma.
107    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
108        value_internal!(@array [$($elems,)* value_internal!($next),] $($rest)*)
109    };
110
111    // Last element is an expression with no trailing comma.
112    (@array [$($elems:expr,)*] $last:expr) => {
113        value_internal!(@array [$($elems,)* value_internal!($last)])
114    };
115
116    // Comma after the most recent element.
117    (@array [$($elems:expr),*] , $($rest:tt)*) => {
118        value_internal!(@array [$($elems,)*] $($rest)*)
119    };
120
121    // Unexpected token after most recent element.
122    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
123        value_unexpected!($unexpected)
124    };
125
126    //////////////////////////////////////////////////////////////////////////
127    // TT muncher for parsing the inside of an object {...}. Each entry is
128    // inserted into the given map variable.
129    //
130    // Must be invoked as: value_internal!(@object $map () ($($tt)*) ($($tt)*))
131    //
132    // We require two copies of the input tokens so that we can match on one
133    // copy and trigger errors on the other copy.
134    //////////////////////////////////////////////////////////////////////////
135
136    // Done.
137    (@object $object:ident () () ()) => {};
138
139    // Insert the current entry followed by trailing comma.
140    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
141        let _ = $object.insert($crate::key_str($($key)+), $value);
142        value_internal!(@object $object () ($($rest)*) ($($rest)*));
143    };
144
145    // Current entry followed by unexpected token.
146    (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
147        value_unexpected!($unexpected);
148    };
149
150    // Insert the last entry without trailing comma.
151    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
152        $object.insert($crate::key_str($($key)+), $value);
153    };
154
155    // Next value is `null`.
156    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
157        value_internal!(@object $object [$($key)+] (value_internal!(null)) $($rest)*);
158    };
159
160    // Next value is `true`.
161    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
162        value_internal!(@object $object [$($key)+] (value_internal!(true)) $($rest)*);
163    };
164
165    // Next value is `false`.
166    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
167        value_internal!(@object $object [$($key)+] (value_internal!(false)) $($rest)*);
168    };
169
170    // Next value is an array.
171    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
172        value_internal!(@object $object [$($key)+] (value_internal!([$($array)*])) $($rest)*);
173    };
174
175    // Next value is a map.
176    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
177        value_internal!(@object $object [$($key)+] (value_internal!({$($map)*})) $($rest)*);
178    };
179
180    // Next value is an expression followed by comma.
181    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
182        value_internal!(@object $object [$($key)+] (value_internal!($value)) , $($rest)*);
183    };
184
185    // Last value is an expression with no trailing comma.
186    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
187        value_internal!(@object $object [$($key)+] (value_internal!($value)));
188    };
189
190    // Missing value for last entry. Trigger a reasonable error message.
191    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
192        // "unexpected end of macro invocation"
193        value_internal!();
194    };
195
196    // Missing colon and value for last entry. Trigger a reasonable error
197    // message.
198    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
199        // "unexpected end of macro invocation"
200        value_internal!();
201    };
202
203    // Misplaced colon. Trigger a reasonable error message.
204    (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
205        // Takes no arguments so "no rules expected the token `:`".
206        value_unexpected!($colon);
207    };
208
209    // Found a comma inside a key. Trigger a reasonable error message.
210    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
211        // Takes no arguments so "no rules expected the token `,`".
212        value_unexpected!($comma);
213    };
214
215    // Key is fully parenthesized. This avoids clippy double_parens false
216    // positives because the parenthesization may be necessary here.
217    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
218        value_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
219    };
220
221    // Refuse to absorb colon token into key expression.
222    (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
223        value_expect_expr_comma!($($unexpected)+);
224    };
225
226    // Munch a token into the current key.
227    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
228        value_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
229    };
230
231    //////////////////////////////////////////////////////////////////////////
232    // The main implementation.
233    //
234    // Must be invoked as: value_internal!($($json)+)
235    //////////////////////////////////////////////////////////////////////////
236
237    (null) => {
238        $crate::DecodedValue::Null
239    };
240
241    (true) => {
242        $crate::DecodedValue::Bool(true)
243    };
244
245    (false) => {
246        $crate::DecodedValue::Bool(false)
247    };
248
249    ([]) => {
250        $crate::DecodedValue::Array(value_internal_vec![])
251    };
252
253    ([ $($tt:tt)+ ]) => {
254        $crate::DecodedValue::Array(value_internal!(@array [] $($tt)+))
255    };
256
257    ({}) => {
258        $crate::DecodedValue::Object($crate::DecodedObject::new())
259    };
260
261    ({ $($tt:tt)+ }) => {
262        $crate::DecodedValue::Object({
263            let mut object = $crate::DecodedObject::new();
264            value_internal!(@object object () ($($tt)+) ($($tt)+));
265            object
266        })
267    };
268
269    // Any Serialize type: numbers, strings, struct literals, variables etc.
270    // Must be below every other rule.
271    // ($other:expr) => {
272    //     $crate::to_value(&$other).unwrap()
273    // };
274    ($other:expr) => {
275        //(&$other).into()
276        <_ as ::core::convert::Into<$crate::DecodedValue<'_>>>::into((&$other))
277    };
278}
279
280// The value_internal macro above cannot invoke vec directly because it uses
281// local_inner_macros. A vec invocation there would resolve to $crate::vec.
282// Instead invoke vec here outside of local_inner_macros.
283#[macro_export]
284#[doc(hidden)]
285macro_rules! value_internal_vec {
286    ($($content:tt)*) => {
287        vec![$($content)*]
288    };
289}
290
291#[macro_export]
292#[doc(hidden)]
293macro_rules! value_unexpected {
294    () => {};
295}
296
297#[macro_export]
298#[doc(hidden)]
299macro_rules! value_expect_expr_comma {
300    ($e:expr , $($tt:tt)*) => {};
301}
302
303#[macro_export]
304macro_rules! clear {
305    ( $name:ident in $x:expr ) => {
306        #[allow(unused_mut)]
307        let mut $name = &mut ($x);      // Create a new lifetime for the allocations handed out from this scope.
308        #[allow(unused_mut)]
309        let mut $name = $name._clear(); // Have a mutable object to distribute the allocations from,
310                                        // so that the lifetime of this object and the allocations
311                                        // are not coupled. Mutable so that new scopes can be
312                                        // created from this scope.
313    };
314    ( $name:ident ) => {
315        #[allow(unused_mut)]
316        let mut $name = &mut $name;
317        #[allow(unused_mut)]
318        let mut $name = $name._clear();
319    };
320}
321
322#[macro_export]
323macro_rules! rewind {
324    ( $name:ident in $x:expr ) => {
325        #[allow(unused_mut)]
326        let mut $name = &mut ($x);         // For explanation, see above.
327        #[allow(unused_mut)]
328        let mut $name = $name._rewind();
329    };
330    ( $name:ident ) => {
331        #[allow(unused_mut)]
332        let mut $name = &mut $name;
333        #[allow(unused_mut)]
334        let mut $name = $name._rewind();
335    }
336}
337
338#[macro_export]
339macro_rules! pool {
340    ( $name:ident ) => {
341        let mut $name = $crate::memorypool::MemoryPool::new();
342        $crate::clear!($name);
343    }
344}