]>
Commit | Line | Data |
---|---|---|
7cac9316 XL |
1 | /// Construct a `serde_json::Value` from a JSON literal. |
2 | /// | |
72b1a166 | 3 | /// ``` |
416331ca | 4 | /// # use serde_json::json; |
041b39d2 | 5 | /// # |
7cac9316 XL |
6 | /// let value = json!({ |
7 | /// "code": 200, | |
8 | /// "success": true, | |
9 | /// "payload": { | |
10 | /// "features": [ | |
11 | /// "serde", | |
12 | /// "json" | |
13 | /// ] | |
14 | /// } | |
15 | /// }); | |
7cac9316 XL |
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 | /// | |
72b1a166 | 25 | /// ``` |
416331ca | 26 | /// # use serde_json::json; |
041b39d2 | 27 | /// # |
7cac9316 XL |
28 | /// let code = 200; |
29 | /// let features = vec!["serde", "json"]; | |
30 | /// | |
31 | /// let value = json!({ | |
416331ca XL |
32 | /// "code": code, |
33 | /// "success": code == 200, | |
34 | /// "payload": { | |
35 | /// features[0]: features[1] | |
36 | /// } | |
7cac9316 | 37 | /// }); |
7cac9316 XL |
38 | /// ``` |
39 | /// | |
40 | /// Trailing commas are allowed inside both arrays and objects. | |
41 | /// | |
72b1a166 | 42 | /// ``` |
416331ca | 43 | /// # use serde_json::json; |
041b39d2 | 44 | /// # |
7cac9316 XL |
45 | /// let value = json!([ |
46 | /// "notice", | |
47 | /// "the", | |
48 | /// "trailing", | |
49 | /// "comma -->", | |
50 | /// ]); | |
7cac9316 | 51 | /// ``` |
b7449926 | 52 | #[macro_export(local_inner_macros)] |
7cac9316 XL |
53 | macro_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)+)`. | |
b7449926 | 67 | #[macro_export(local_inner_macros)] |
7cac9316 XL |
68 | #[doc(hidden)] |
69 | macro_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,)*]) => { | |
b7449926 | 79 | json_internal_vec![$($elems,)*] |
7cac9316 XL |
80 | }; |
81 | ||
82 | // Done without trailing comma. | |
83 | (@array [$($elems:expr),*]) => { | |
b7449926 | 84 | json_internal_vec![$($elems),*] |
7cac9316 XL |
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 | ||
8faf50e0 XL |
127 | // Unexpected token after most recent element. |
128 | (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => { | |
129 | json_unexpected!($unexpected) | |
130 | }; | |
131 | ||
7cac9316 XL |
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)*) => { | |
8faf50e0 | 147 | let _ = $object.insert(($($key)+).into(), $value); |
7cac9316 XL |
148 | json_internal!(@object $object () ($($rest)*) ($($rest)*)); |
149 | }; | |
150 | ||
8faf50e0 XL |
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 | ||
7cac9316 XL |
156 | // Insert the last entry without trailing comma. |
157 | (@object $object:ident [$($key:tt)+] ($value:expr)) => { | |
8faf50e0 | 158 | let _ = $object.insert(($($key)+).into(), $value); |
7cac9316 XL |
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 `:`". | |
8faf50e0 | 212 | json_unexpected!($colon); |
7cac9316 XL |
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 `,`". | |
8faf50e0 | 218 | json_unexpected!($comma); |
7cac9316 XL |
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 | ||
72b1a166 FG |
227 | // Refuse to absorb colon token into key expression. |
228 | (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { | |
229 | json_expect_expr_comma!($($unexpected)+); | |
230 | }; | |
231 | ||
7cac9316 XL |
232 | // Munch a token into the current key. |
233 | (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { | |
234 | json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); | |
235 | }; | |
236 | ||
237 | ////////////////////////////////////////////////////////////////////////// | |
238 | // The main implementation. | |
239 | // | |
240 | // Must be invoked as: json_internal!($($json)+) | |
241 | ////////////////////////////////////////////////////////////////////////// | |
242 | ||
243 | (null) => { | |
244 | $crate::Value::Null | |
245 | }; | |
246 | ||
247 | (true) => { | |
248 | $crate::Value::Bool(true) | |
249 | }; | |
250 | ||
251 | (false) => { | |
252 | $crate::Value::Bool(false) | |
253 | }; | |
254 | ||
255 | ([]) => { | |
b7449926 | 256 | $crate::Value::Array(json_internal_vec![]) |
7cac9316 XL |
257 | }; |
258 | ||
259 | ([ $($tt:tt)+ ]) => { | |
260 | $crate::Value::Array(json_internal!(@array [] $($tt)+)) | |
261 | }; | |
262 | ||
263 | ({}) => { | |
264 | $crate::Value::Object($crate::Map::new()) | |
265 | }; | |
266 | ||
267 | ({ $($tt:tt)+ }) => { | |
268 | $crate::Value::Object({ | |
269 | let mut object = $crate::Map::new(); | |
270 | json_internal!(@object object () ($($tt)+) ($($tt)+)); | |
271 | object | |
272 | }) | |
273 | }; | |
274 | ||
275 | // Any Serialize type: numbers, strings, struct literals, variables etc. | |
276 | // Must be below every other rule. | |
277 | ($other:expr) => { | |
041b39d2 | 278 | $crate::to_value(&$other).unwrap() |
7cac9316 XL |
279 | }; |
280 | } | |
8faf50e0 | 281 | |
b7449926 XL |
282 | // The json_internal macro above cannot invoke vec directly because it uses |
283 | // local_inner_macros. A vec invocation there would resolve to $crate::vec. | |
284 | // Instead invoke vec here outside of local_inner_macros. | |
285 | #[macro_export] | |
286 | #[doc(hidden)] | |
287 | macro_rules! json_internal_vec { | |
288 | ($($content:tt)*) => { | |
289 | vec![$($content)*] | |
290 | }; | |
291 | } | |
292 | ||
8faf50e0 XL |
293 | #[macro_export] |
294 | #[doc(hidden)] | |
295 | macro_rules! json_unexpected { | |
296 | () => {}; | |
297 | } | |
72b1a166 FG |
298 | |
299 | #[macro_export] | |
300 | #[doc(hidden)] | |
301 | macro_rules! json_expect_expr_comma { | |
302 | ($e:expr , $($tt:tt)*) => {}; | |
303 | } |