|
1 |
| -/// Construct a BSON value |
| 1 | +// BSON macro based on the serde_json json! implementation. |
| 2 | + |
| 3 | +/// Construct a bson::BSON value from a literal. |
| 4 | +/// |
| 5 | +/// ```rust |
| 6 | +/// # #[macro_use] |
| 7 | +/// # extern crate bson; |
| 8 | +/// # |
| 9 | +/// # fn main() { |
| 10 | +/// let value = bson!({ |
| 11 | +/// "code": 200, |
| 12 | +/// "success": true, |
| 13 | +/// "payload": { |
| 14 | +/// "some": [ |
| 15 | +/// "pay", |
| 16 | +/// "loads", |
| 17 | +/// ] |
| 18 | +/// } |
| 19 | +/// }); |
| 20 | +/// # } |
| 21 | +/// ``` |
2 | 22 | #[macro_export]
|
3 | 23 | macro_rules! bson {
|
4 |
| - ([]) => {{ $crate::Bson::Array(Vec::new()) }}; |
| 24 | + ////////////////////////////////////////////////////////////////////////// |
| 25 | + // TT muncher for parsing the inside of an array [...]. Produces a vec![...] |
| 26 | + // of the elements. |
| 27 | + // |
| 28 | + // Must be invoked as: bson!(@array [] $($tt)*) |
| 29 | + ////////////////////////////////////////////////////////////////////////// |
5 | 30 |
|
6 |
| - ([$($val:tt),*]) => {{ |
7 |
| - let mut array = Vec::new(); |
| 31 | + // Finished with trailing comma. |
| 32 | + (@array [$($elems:expr,)*]) => { |
| 33 | + vec![$($elems,)*] |
| 34 | + }; |
8 | 35 |
|
9 |
| - $( |
10 |
| - array.push(bson!($val)); |
11 |
| - )* |
| 36 | + // Finished without trailing comma. |
| 37 | + (@array [$($elems:expr),*]) => { |
| 38 | + vec![$($elems),*] |
| 39 | + }; |
12 | 40 |
|
13 |
| - $crate::Bson::Array(array) |
14 |
| - }}; |
| 41 | + // Next element is `null`. |
| 42 | + (@array [$($elems:expr,)*] null $($rest:tt)*) => { |
| 43 | + bson!(@array [$($elems,)* bson!(null)] $($rest)*) |
| 44 | + }; |
15 | 45 |
|
16 |
| - ([$val:expr]) => {{ |
17 |
| - $crate::Bson::Array(vec!(::std::convert::From::from($val))) |
18 |
| - }}; |
| 46 | + // Next element is an array. |
| 47 | + (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => { |
| 48 | + bson!(@array [$($elems,)* bson!([$($array)*])] $($rest)*) |
| 49 | + }; |
19 | 50 |
|
20 |
| - ({ $($k:expr => $v:tt),* }) => {{ |
21 |
| - $crate::Bson::Document(doc! { |
22 |
| - $( |
23 |
| - $k => $v |
24 |
| - ),* |
25 |
| - }) |
26 |
| - }}; |
| 51 | + // Next element is a map. |
| 52 | + (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => { |
| 53 | + bson!(@array [$($elems,)* bson!({$($map)*})] $($rest)*) |
| 54 | + }; |
27 | 55 |
|
28 |
| - ($val:expr) => {{ |
29 |
| - ::std::convert::From::from($val) |
30 |
| - }}; |
| 56 | + // Next element is an expression followed by comma. |
| 57 | + (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => { |
| 58 | + bson!(@array [$($elems,)* bson!($next),] $($rest)*) |
| 59 | + }; |
| 60 | + |
| 61 | + // Last element is an expression with no trailing comma. |
| 62 | + (@array [$($elems:expr,)*] $last:expr) => { |
| 63 | + bson!(@array [$($elems,)* bson!($last)]) |
| 64 | + }; |
| 65 | + |
| 66 | + // Comma after the most recent element. |
| 67 | + (@array [$($elems:expr),*] , $($rest:tt)*) => { |
| 68 | + bson!(@array [$($elems,)*] $($rest)*) |
| 69 | + }; |
| 70 | + |
| 71 | + ////////////////////////////////////////////////////////////////////////// |
| 72 | + // TT muncher for parsing the inside of an object {...}. Each entry is |
| 73 | + // inserted into the given map variable. |
| 74 | + // |
| 75 | + // Must be invoked as: bson!(@object $map () ($($tt)*) ($($tt)*)) |
| 76 | + // |
| 77 | + // We require two copies of the input tokens so that we can match on one |
| 78 | + // copy and trigger errors on the other copy. |
| 79 | + ////////////////////////////////////////////////////////////////////////// |
| 80 | + |
| 81 | + // Finished. |
| 82 | + (@object $object:ident () () ()) => {}; |
| 83 | + |
| 84 | + // Insert the current entry followed by trailing comma. |
| 85 | + (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { |
| 86 | + $object.insert_bson(($($key)+).into(), $value); |
| 87 | + bson!(@object $object () ($($rest)*) ($($rest)*)); |
| 88 | + }; |
| 89 | + |
| 90 | + // Insert the last entry without trailing comma. |
| 91 | + (@object $object:ident [$($key:tt)+] ($value:expr)) => { |
| 92 | + $object.insert_bson(($($key)+).into(), $value); |
| 93 | + }; |
| 94 | + |
| 95 | + // Next value is `null`. |
| 96 | + (@object $object:ident ($($key:tt)+) (=> null $($rest:tt)*) $copy:tt) => { |
| 97 | + bson!(@object $object [$($key)+] (bson!(null)) $($rest)*); |
| 98 | + }; |
| 99 | + |
| 100 | + (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { |
| 101 | + bson!(@object $object [$($key)+] (bson!(null)) $($rest)*); |
| 102 | + }; |
| 103 | + |
| 104 | + // Next value is an array. |
| 105 | + (@object $object:ident ($($key:tt)+) (=> [$($array:tt)*] $($rest:tt)*) $copy:tt) => { |
| 106 | + bson!(@object $object [$($key)+] (bson!([$($array)*])) $($rest)*); |
| 107 | + }; |
| 108 | + |
| 109 | + (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { |
| 110 | + bson!(@object $object [$($key)+] (bson!([$($array)*])) $($rest)*); |
| 111 | + }; |
| 112 | + |
| 113 | + // Next value is a map. |
| 114 | + (@object $object:ident ($($key:tt)+) (=> {$($map:tt)*} $($rest:tt)*) $copy:tt) => { |
| 115 | + bson!(@object $object [$($key)+] (bson!({$($map)*})) $($rest)*); |
| 116 | + }; |
| 117 | + |
| 118 | + (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { |
| 119 | + bson!(@object $object [$($key)+] (bson!({$($map)*})) $($rest)*); |
| 120 | + }; |
| 121 | + |
| 122 | + // Next value is an expression followed by comma. |
| 123 | + (@object $object:ident ($($key:tt)+) (=> $value:expr , $($rest:tt)*) $copy:tt) => { |
| 124 | + bson!(@object $object [$($key)+] (bson!($value)) , $($rest)*); |
| 125 | + }; |
| 126 | + |
| 127 | + (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { |
| 128 | + bson!(@object $object [$($key)+] (bson!($value)) , $($rest)*); |
| 129 | + }; |
| 130 | + |
| 131 | + // Last value is an expression with no trailing comma. |
| 132 | + (@object $object:ident ($($key:tt)+) (=> $value:expr) $copy:tt) => { |
| 133 | + bson!(@object $object [$($key)+] (bson!($value))); |
| 134 | + }; |
| 135 | + |
| 136 | + (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { |
| 137 | + bson!(@object $object [$($key)+] (bson!($value))); |
| 138 | + }; |
| 139 | + |
| 140 | + // Missing value for last entry. Trigger a reasonable error message. |
| 141 | + (@object $object:ident ($($key:tt)+) (=>) $copy:tt) => { |
| 142 | + // "unexpected end of macro invocation" |
| 143 | + bson!(); |
| 144 | + }; |
| 145 | + |
| 146 | + (@object $object:ident ($($key:tt)+) (:) $copy:tt) => { |
| 147 | + // "unexpected end of macro invocation" |
| 148 | + bson!(); |
| 149 | + }; |
| 150 | + |
| 151 | + // Missing key-value separator and value for last entry. |
| 152 | + // Trigger a reasonable error message. |
| 153 | + (@object $object:ident ($($key:tt)+) () $copy:tt) => { |
| 154 | + // "unexpected end of macro invocation" |
| 155 | + bson!(); |
| 156 | + }; |
| 157 | + |
| 158 | + // Misplaced key-value separator. Trigger a reasonable error message. |
| 159 | + (@object $object:ident () (=> $($rest:tt)*) ($kv_separator:tt $($copy:tt)*)) => { |
| 160 | + // Takes no arguments so "no rules expected the token `=>`". |
| 161 | + unimplemented!($kv_separator); |
| 162 | + }; |
| 163 | + |
| 164 | + (@object $object:ident () (: $($rest:tt)*) ($kv_separator:tt $($copy:tt)*)) => { |
| 165 | + // Takes no arguments so "no rules expected the token `:`". |
| 166 | + unimplemented!($kv_separator); |
| 167 | + }; |
| 168 | + |
| 169 | + // Found a comma inside a key. Trigger a reasonable error message. |
| 170 | + (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { |
| 171 | + // Takes no arguments so "no rules expected the token `,`". |
| 172 | + unimplemented!($comma); |
| 173 | + }; |
| 174 | + |
| 175 | + // Key is fully parenthesized. This avoids clippy double_parens false |
| 176 | + // positives because the parenthesization may be necessary here. |
| 177 | + (@object $object:ident () (($key:expr) => $($rest:tt)*) $copy:tt) => { |
| 178 | + bson!(@object $object ($key) (=> $($rest)*) (=> $($rest)*)); |
| 179 | + }; |
| 180 | + |
| 181 | + (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { |
| 182 | + bson!(@object $object ($key) (: $($rest)*) (: $($rest)*)); |
| 183 | + }; |
| 184 | + |
| 185 | + // Munch a token into the current key. |
| 186 | + (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { |
| 187 | + bson!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); |
| 188 | + }; |
| 189 | + |
| 190 | + ////////////////////////////////////////////////////////////////////////// |
| 191 | + // The main implementation. |
| 192 | + // |
| 193 | + // Must be invoked as: bson!($($bson)+) |
| 194 | + ////////////////////////////////////////////////////////////////////////// |
| 195 | + |
| 196 | + (null) => { |
| 197 | + $crate::Bson::Null |
| 198 | + }; |
| 199 | + |
| 200 | + ([]) => { |
| 201 | + $crate::Bson::Array(vec![]) |
| 202 | + }; |
| 203 | + |
| 204 | + ([ $($tt:tt)+ ]) => { |
| 205 | + $crate::Bson::Array(bson!(@array [] $($tt)+)) |
| 206 | + }; |
| 207 | + |
| 208 | + ({}) => { |
| 209 | + $crate::Bson::Document(doc!{}) |
| 210 | + }; |
| 211 | + |
| 212 | + ({$($tt:tt)+}) => { |
| 213 | + $crate::Bson::Document(doc!{$($tt)+}); |
| 214 | + }; |
| 215 | + |
| 216 | + // Any Serialize type: numbers, strings, struct literals, variables etc. |
| 217 | + // Must be below every other rule. |
| 218 | + ($other:expr) => { |
| 219 | + ::std::convert::From::from($other) |
| 220 | + }; |
31 | 221 | }
|
32 | 222 |
|
33 |
| -/// Construct a BSON Document |
| 223 | +/// Construct a bson::Document value. |
| 224 | +/// |
| 225 | +/// ```rust |
| 226 | +/// # #[macro_use] |
| 227 | +/// # extern crate bson; |
| 228 | +/// # |
| 229 | +/// # fn main() { |
| 230 | +/// let value = doc! { |
| 231 | +/// "code": 200, |
| 232 | +/// "success": true, |
| 233 | +/// "payload": { |
| 234 | +/// "some": [ |
| 235 | +/// "pay", |
| 236 | +/// "loads", |
| 237 | +/// ] |
| 238 | +/// } |
| 239 | +/// }; |
| 240 | +/// # } |
| 241 | +/// ``` |
34 | 242 | #[macro_export]
|
35 | 243 | macro_rules! doc {
|
36 | 244 | () => {{ $crate::Document::new() }};
|
37 |
| - |
38 |
| - ( $($key:expr => $val:tt),* ) => {{ |
39 |
| - let mut document = $crate::Document::new(); |
40 |
| - |
41 |
| - $( |
42 |
| - document.insert_bson($key.to_owned(), bson!($val)); |
43 |
| - )* |
44 |
| - |
45 |
| - document |
| 245 | + ( $($tt:tt)+ ) => {{ |
| 246 | + let mut object = $crate::Document::new(); |
| 247 | + bson!(@object object () ($($tt)+) ($($tt)+)); |
| 248 | + object |
46 | 249 | }};
|
47 | 250 | }
|
0 commit comments