serde_json/macros.rs
1/// Construct a `serde_json::Value` from a JSON literal.
2///
3/// ```edition2018
4/// # use serde_json::json;
5/// #
6/// let value = json!({
7/// "code": 200,
8/// "success": true,
9/// "payload": {
10/// "features": [
11/// "serde",
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 Serde's
20/// `Serialize` 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/// ```edition2018
26/// # use serde_json::json;
27/// #
28/// let code = 200;
29/// let features = vec!["serde", "json"];
30///
31/// let value = json!({
32/// "code": code,
33/// "success": code == 200,
34/// "payload": {
35/// features[0]: features[1]
36/// }
37/// });
38/// ```
39///
40/// Trailing commas are allowed inside both arrays and objects.
41///
42/// ```edition2018
43/// # use serde_json::json;
44/// #
45/// let value = json!([
46/// "notice",
47/// "the",
48/// "trailing",
49/// "comma -->",
50/// ]);
51/// ```
52#[macro_export(local_inner_macros)]
53macro_rules! json {
54 // Hide distracting implementation details from the generated rustdoc.
55 ($($json:tt)+) => {
56 json_internal!($($json)+)
57 };
58}
59
60// Rocket relies on this because they export their own `json!` with a different
61// doc comment than ours, and various Rust bugs prevent them from calling our
62// `json!` from their `json!` so they call `json_internal!` directly. Check with
63// @SergioBenitez before making breaking changes to this macro.
64//
65// Changes are fine as long as `json_internal!` does not call any new helper
66// macros and can still be invoked as `json_internal!($($json)+)`.
67#[macro_export(local_inner_macros)]
68#[doc(hidden)]
69macro_rules! json_internal {
70 //////////////////////////////////////////////////////////////////////////
71 // TT muncher for parsing the inside of an array [...]. Produces a vec![...]
72 // of the elements.
73 //
74 // Must be invoked as: json_internal!(@array [] $($tt)*)
75 //////////////////////////////////////////////////////////////////////////
76
77 // Done with trailing comma.
78 (@array [$($elems:expr,)*]) => {
79 json_internal_vec![$($elems,)*]
80 };
81
82 // Done without trailing comma.
83 (@array [$($elems:expr),*]) => {
84 json_internal_vec![$($elems),*]
85 };
86
87 // Next element is `null`.
88 (@array [$($elems:expr,)*] null $($rest:tt)*) => {
89 json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*)
90 };
91
92 // Next element is `true`.
93 (@array [$($elems:expr,)*] true $($rest:tt)*) => {
94 json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*)
95 };
96
97 // Next element is `false`.
98 (@array [$($elems:expr,)*] false $($rest:tt)*) => {
99 json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*)
100 };
101
102 // Next element is an array.
103 (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
104 json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*)
105 };
106
107 // Next element is a map.
108 (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
109 json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*)
110 };
111
112 // Next element is an expression followed by comma.
113 (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
114 json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*)
115 };
116
117 // Last element is an expression with no trailing comma.
118 (@array [$($elems:expr,)*] $last:expr) => {
119 json_internal!(@array [$($elems,)* json_internal!($last)])
120 };
121
122 // Comma after the most recent element.
123 (@array [$($elems:expr),*] , $($rest:tt)*) => {
124 json_internal!(@array [$($elems,)*] $($rest)*)
125 };
126
127 // Unexpected token after most recent element.
128 (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
129 json_unexpected!($unexpected)
130 };
131
132 //////////////////////////////////////////////////////////////////////////
133 // TT muncher for parsing the inside of an object {...}. Each entry is
134 // inserted into the given map variable.
135 //
136 // Must be invoked as: json_internal!(@object $map () ($($tt)*) ($($tt)*))
137 //
138 // We require two copies of the input tokens so that we can match on one
139 // copy and trigger errors on the other copy.
140 //////////////////////////////////////////////////////////////////////////
141
142 // Done.
143 (@object $object:ident () () ()) => {};
144
145 // Insert the current entry followed by trailing comma.
146 (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
147 let _ = $object.insert(($($key)+).into(), $value);
148 json_internal!(@object $object () ($($rest)*) ($($rest)*));
149 };
150
151 // Current entry followed by unexpected token.
152 (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
153 json_unexpected!($unexpected);
154 };
155
156 // Insert the last entry without trailing comma.
157 (@object $object:ident [$($key:tt)+] ($value:expr)) => {
158 let _ = $object.insert(($($key)+).into(), $value);
159 };
160
161 // Next value is `null`.
162 (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
163 json_internal!(@object $object [$($key)+] (json_internal!(null)) $($rest)*);
164 };
165
166 // Next value is `true`.
167 (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
168 json_internal!(@object $object [$($key)+] (json_internal!(true)) $($rest)*);
169 };
170
171 // Next value is `false`.
172 (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
173 json_internal!(@object $object [$($key)+] (json_internal!(false)) $($rest)*);
174 };
175
176 // Next value is an array.
177 (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
178 json_internal!(@object $object [$($key)+] (json_internal!([$($array)*])) $($rest)*);
179 };
180
181 // Next value is a map.
182 (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
183 json_internal!(@object $object [$($key)+] (json_internal!({$($map)*})) $($rest)*);
184 };
185
186 // Next value is an expression followed by comma.
187 (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
188 json_internal!(@object $object [$($key)+] (json_internal!($value)) , $($rest)*);
189 };
190
191 // Last value is an expression with no trailing comma.
192 (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
193 json_internal!(@object $object [$($key)+] (json_internal!($value)));
194 };
195
196 // Missing value for last entry. Trigger a reasonable error message.
197 (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
198 // "unexpected end of macro invocation"
199 json_internal!();
200 };
201
202 // Missing colon and value for last entry. Trigger a reasonable error
203 // message.
204 (@object $object:ident ($($key:tt)+) () $copy:tt) => {
205 // "unexpected end of macro invocation"
206 json_internal!();
207 };
208
209 // Misplaced colon. Trigger a reasonable error message.
210 (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
211 // Takes no arguments so "no rules expected the token `:`".
212 json_unexpected!($colon);
213 };
214
215 // Found a comma inside a key. Trigger a reasonable error message.
216 (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
217 // Takes no arguments so "no rules expected the token `,`".
218 json_unexpected!($comma);
219 };
220
221 // Key is fully parenthesized. This avoids clippy double_parens false
222 // positives because the parenthesization may be necessary here.
223 (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
224 json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
225 };
226
227 // Munch a token into the current key.
228 (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
229 json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
230 };
231
232 //////////////////////////////////////////////////////////////////////////
233 // The main implementation.
234 //
235 // Must be invoked as: json_internal!($($json)+)
236 //////////////////////////////////////////////////////////////////////////
237
238 (null) => {
239 $crate::Value::Null
240 };
241
242 (true) => {
243 $crate::Value::Bool(true)
244 };
245
246 (false) => {
247 $crate::Value::Bool(false)
248 };
249
250 ([]) => {
251 $crate::Value::Array(json_internal_vec![])
252 };
253
254 ([ $($tt:tt)+ ]) => {
255 $crate::Value::Array(json_internal!(@array [] $($tt)+))
256 };
257
258 ({}) => {
259 $crate::Value::Object($crate::Map::new())
260 };
261
262 ({ $($tt:tt)+ }) => {
263 $crate::Value::Object({
264 let mut object = $crate::Map::new();
265 json_internal!(@object object () ($($tt)+) ($($tt)+));
266 object
267 })
268 };
269
270 // Any Serialize type: numbers, strings, struct literals, variables etc.
271 // Must be below every other rule.
272 ($other:expr) => {
273 $crate::to_value(&$other).unwrap()
274 };
275}
276
277// The json_internal macro above cannot invoke vec directly because it uses
278// local_inner_macros. A vec invocation there would resolve to $crate::vec.
279// Instead invoke vec here outside of local_inner_macros.
280#[macro_export]
281#[doc(hidden)]
282macro_rules! json_internal_vec {
283 ($($content:tt)*) => {
284 vec![$($content)*]
285 };
286}
287
288#[macro_export]
289#[doc(hidden)]
290macro_rules! json_unexpected {
291 () => {};
292}