typed_json/
macros.rs

1/// Construct an [`impl serde::Serialize`](serde::Serialize) type from a JSON literal.
2///
3/// ```
4/// # use typed_json::json;
5/// #
6/// let value = json!({
7///     "code": 200,
8///     "success": true,
9///     "payload": {
10///         "features": [
11///             "serde",
12///             "json"
13///         ],
14///         "homepage": null
15///     }
16/// });
17/// ```
18///
19/// Variables or expressions can be interpolated into the JSON literal. Any type
20/// interpolated into an array element or object value must implement Serde's
21/// `Serialize` trait, while any type interpolated into a object key must
22/// implement `Into<String>`. If the `Serialize` implementation of the
23/// interpolated type decides to fail, or if the interpolated type contains a
24/// map with non-string keys, the `json!` macro will panic.
25///
26/// ```
27/// # use typed_json::json;
28/// #
29/// let code = 200;
30/// let features = vec!["typed", "json"];
31///
32/// let value = json!({
33///     "code": code,
34///     "success": code == 200,
35///     "payload": {
36///         features[0]: features[1]
37///     }
38/// });
39/// ```
40///
41/// Trailing commas are allowed inside both arrays and objects.
42///
43/// ```
44/// # use typed_json::json;
45/// #
46/// let value = json!([
47///     "notice",
48///     "the",
49///     "trailing",
50///     "comma -->",
51/// ]);
52/// ```
53#[macro_export(local_inner_macros)]
54macro_rules! json {
55    // Hide distracting implementation details from the generated rustdoc.
56    ($($json:tt)+) => {
57        json_internal!($($json)+)
58    };
59}
60
61#[macro_export(local_inner_macros)]
62#[doc(hidden)]
63macro_rules! json_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: json_internal!(@array [] $($tt)*)
69    //////////////////////////////////////////////////////////////////////////
70
71    // Done with trailing comma.
72    (@array [$($elems:expr,)*]) => {
73        json_internal_vec![$($elems,)*]
74    };
75
76    // Done without trailing comma.
77    (@array [$($elems:expr),*]) => {
78        json_internal_vec![$($elems),*]
79    };
80
81    // Next element is `null`.
82    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
83        json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*)
84    };
85
86    // Next element is `true`.
87    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
88        json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*)
89    };
90
91    // Next element is `false`.
92    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
93        json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*)
94    };
95
96    // Next element is an array.
97    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
98        json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*)
99    };
100
101    // Next element is a map.
102    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
103        json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*)
104    };
105
106    // Next element is an expression followed by comma.
107    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
108        json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*)
109    };
110
111    // Last element is an expression with no trailing comma.
112    (@array [$($elems:expr,)*] $last:expr) => {
113        json_internal!(@array [$($elems,)* json_internal!($last)])
114    };
115
116    // Comma after the most recent element.
117    (@array [$($elems:expr),*] , $($rest:tt)*) => {
118        json_internal!(@array [$($elems,)*] $($rest)*)
119    };
120
121    // Unexpected token after most recent element.
122    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
123        json_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: json_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 [$($elems:expr),*] () () ()) => { json_internal_vec![$($elems),*] };
138
139    (@object [$($elems:expr),*] [$($key:tt)+] ($value:expr) $(,)?) => {
140        json_internal_vec![$($elems,)* $crate::__private::KV::Pair(json_internal!($($key)*), $value)]
141    };
142
143    // Insert the current entry followed by trailing comma.
144    (@object [$($elems:expr),*] [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
145        json_internal!(@object [$($elems,)* $crate::__private::KV::Pair(json_internal!($($key)*), $value)] () ($($rest)*) ($($rest)*))
146    };
147
148    // Current entry followed by unexpected token.
149    (@object [$($elems:expr),*] [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
150        json_unexpected!($unexpected)
151    };
152
153    // Next value is `null`.
154    (@object [$($elems:expr),*] ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
155        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!(null)) $($rest)*)
156    };
157
158    // Next value is `true`.
159    (@object [$($elems:expr),*] ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
160        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!(true)) $($rest)*)
161    };
162
163    // Next value is `false`.
164    (@object [$($elems:expr),*] ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
165        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!(false)) $($rest)*)
166    };
167
168    // Next value is an array.
169    (@object [$($elems:expr),*] ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
170        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!([$($array)*])) $($rest)*)
171    };
172
173    // Next value is a map.
174    (@object [$($elems:expr),*] ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
175        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!({$($map)*})) $($rest)*)
176    };
177
178    // Next value is an expression followed by comma.
179    (@object [$($elems:expr),*] ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
180        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!($value)) , $($rest)*)
181    };
182
183    // Last value is an expression with no trailing comma.
184    (@object [$($elems:expr),*] ($($key:tt)+) (: $value:expr) $copy:tt) => {
185        json_internal!(@object [$($elems),*] [$($key)+] (json_internal!($value)))
186    };
187
188    // Missing value for last entry. Trigger a reasonable error message.
189    (@object [$($elems:expr),*] ($($key:tt)+) (:) $copy:tt) => {
190        // "unexpected end of macro invocation"
191        json_internal!()
192    };
193
194    // Missing colon and value for last entry. Trigger a reasonable error
195    // message.
196    (@object [$($elems:expr),*] ($($key:tt)+) () $copy:tt) => {
197        // "unexpected end of macro invocation"
198        json_internal!()
199    };
200
201    // Misplaced colon. Trigger a reasonable error message.
202    (@object [$($elems:expr),*] () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
203        // Takes no arguments so "no rules expected the token `:`".
204        json_unexpected!($colon)
205    };
206
207    // Found a comma inside a key. Trigger a reasonable error message.
208    (@object [$($elems:expr),*] ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
209        // Takes no arguments so "no rules expected the token `,`".
210        json_unexpected!($comma)
211    };
212
213    // Key is fully parenthesized. This avoids clippy double_parens false
214    // positives because the parenthesization may be necessary here.
215    (@object [$($elems:expr),*] () (($key:expr) : $($rest:tt)*) $copy:tt) => {
216        json_internal!(@object [$($elems),*] ($key) (: $($rest)*) (: $($rest)*))
217    };
218
219    // Refuse to absorb colon token into key expression.
220    (@object [$($elems:expr),*] ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
221        json_expect_expr_comma!($($unexpected)+)
222    };
223
224    // Munch a token into the current key.
225    (@object [$($elems:expr),*] ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
226        json_internal!(@object [$($elems),*] ($($key)* $tt) ($($rest)*) ($($rest)*))
227    };
228
229    //////////////////////////////////////////////////////////////////////////
230    // The main implementation.
231    //
232    // Must be invoked as: json_internal!($($json)+)
233    //////////////////////////////////////////////////////////////////////////
234
235    (null) => {
236        $crate::__private::Null
237    };
238
239    (true) => {
240        $crate::__private::Expr(true)
241    };
242
243    (false) => {
244        $crate::__private::Expr(false)
245    };
246
247    ([]) => {
248        $crate::__private::Array(())
249    };
250
251    ([ $($tt:tt)+ ]) => {
252        $crate::__private::Array(json_internal!(@array [] $($tt)+))
253    };
254
255    ({}) => {
256        $crate::__private::Map(())
257    };
258
259    ({ $($tt:tt)+ }) => {
260        $crate::__private::Map(json_internal!(@object [] () ($($tt)+) ($($tt)+)))
261    };
262
263    // Any Serialize type: numbers, strings, struct literals, variables etc.
264    // Must be below every other rule.
265    ($other:expr) => {
266        $crate::__private::Expr($other)
267    };
268}
269
270// The json_internal macro above cannot invoke vec directly because it uses
271// local_inner_macros. A vec invocation there would resolve to $crate::__private::vec.
272// Instead invoke vec here outside of local_inner_macros.
273#[macro_export(local_inner_macros)]
274#[doc(hidden)]
275macro_rules! json_internal_vec {
276    ($($g:expr),* $(,)?) => {
277        $crate::hlist![
278            $(::core::option::Option::Some($g)),*
279        ]
280    };
281}
282
283#[macro_export]
284#[doc(hidden)]
285macro_rules! json_unexpected {
286    () => {};
287}
288
289#[macro_export]
290#[doc(hidden)]
291macro_rules! json_expect_expr_comma {
292    ($e:expr , $($tt:tt)*) => {};
293}
294
295#[macro_export]
296#[doc(hidden)]
297macro_rules! hlist {
298    // edgecase: no elements
299    () => { () };
300    // edgecase: 1 element
301    ($g0:expr) => {
302        $g0
303    };
304    // take pairs of elements and turn them into tuple-pairs. recurse
305    ($($g0:expr, $g1:expr),*) => {
306        $crate::hlist!($(
307            ($g0, $g1)
308        ),*)
309    };
310    // take pairs of elements, skipping the first, and turn them into tuple-pairs. recurse
311    ($g:expr, $($g0:expr, $g1:expr),*) => {
312        $crate::hlist!(
313            $g,
314            $(
315                ($g0, $g1)
316            ),*
317        )
318    };
319}