| 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 | |