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