bson/
macros.rs

1// BSON macro based on the serde_json json! implementation.
2
3/// Construct a bson::BSON value from a literal.
4///
5/// ```rust
6/// # use bson::bson;
7/// #
8/// # fn main() {
9/// let value = bson!({
10///     "code": 200,
11///     "success": true,
12///     "payload": {
13///       "some": [
14///           "pay",
15///           "loads",
16///       ]
17///     }
18/// });
19/// # }
20/// ```
21#[macro_export]
22macro_rules! bson {
23    //////////////////////////////////////////////////////////////////////////
24    // TT muncher for parsing the inside of an array [...]. Produces a vec![...]
25    // of the elements.
26    //
27    // Must be invoked as: bson!(@array [] $($tt)*)
28    //////////////////////////////////////////////////////////////////////////
29
30    // Finished with trailing comma.
31    (@array [$($elems:expr,)*]) => {
32        vec![$($elems,)*]
33    };
34
35    // Finished without trailing comma.
36    (@array [$($elems:expr),*]) => {
37        vec![$($elems),*]
38    };
39
40    // Next element is `null`.
41    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
42        $crate::bson!(@array [$($elems,)* $crate::bson!(null)] $($rest)*)
43    };
44
45    // Next element is an array.
46    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
47        $crate::bson!(@array [$($elems,)* $crate::bson!([$($array)*])] $($rest)*)
48    };
49
50    // Next element is a map.
51    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
52        $crate::bson!(@array [$($elems,)* $crate::bson!({$($map)*})] $($rest)*)
53    };
54
55    // Next element is an expression followed by comma.
56    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
57        $crate::bson!(@array [$($elems,)* $crate::bson!($next),] $($rest)*)
58    };
59
60    // Last element is an expression with no trailing comma.
61    (@array [$($elems:expr,)*] $last:expr) => {
62        $crate::bson!(@array [$($elems,)* $crate::bson!($last)])
63    };
64
65    // Comma after the most recent element.
66    (@array [$($elems:expr),*] , $($rest:tt)*) => {
67        $crate::bson!(@array [$($elems,)*] $($rest)*)
68    };
69
70    //////////////////////////////////////////////////////////////////////////
71    // TT muncher for parsing the inside of an object {...}. Each entry is
72    // inserted into the given map variable.
73    //
74    // Must be invoked as: bson!(@object $map () ($($tt)*) ($($tt)*))
75    //
76    // We require two copies of the input tokens so that we can match on one
77    // copy and trigger errors on the other copy.
78    //////////////////////////////////////////////////////////////////////////
79
80    // Finished.
81    (@object $object:ident () () ()) => {};
82
83    // Insert the current entry followed by trailing comma.
84    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
85        $object.insert::<_, $crate::Bson>(($($key)+), $value);
86        $crate::bson!(@object $object () ($($rest)*) ($($rest)*));
87    };
88
89    // Insert the last entry without trailing comma.
90    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
91        $object.insert::<_, $crate::Bson>(($($key)+), $value);
92    };
93
94    // Next value is `null`.
95    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
96        $crate::bson!(@object $object [$($key)+] ($crate::bson!(null)) $($rest)*);
97    };
98
99    // Next value is an array.
100    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
101        $crate::bson!(@object $object [$($key)+] ($crate::bson!([$($array)*])) $($rest)*);
102    };
103
104    // Next value is a map.
105    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
106        $crate::bson!(@object $object [$($key)+] ($crate::bson!({$($map)*})) $($rest)*);
107    };
108
109    // Next value is an expression followed by comma.
110    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
111        $crate::bson!(@object $object [$($key)+] ($crate::bson!($value)) , $($rest)*);
112    };
113
114    // Last value is an expression with no trailing comma.
115    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
116        $crate::bson!(@object $object [$($key)+] ($crate::bson!($value)));
117    };
118
119    // Missing value for last entry. Trigger a reasonable error message.
120    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
121        // "unexpected end of macro invocation"
122        $crate::bson!();
123    };
124
125    // Missing key-value separator and value for last entry.
126    // Trigger a reasonable error message.
127    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
128        // "unexpected end of macro invocation"
129        $crate::bson!();
130    };
131
132    // Misplaced key-value separator. Trigger a reasonable error message.
133    (@object $object:ident () (: $($rest:tt)*) ($kv_separator:tt $($copy:tt)*)) => {
134        // Takes no arguments so "no rules expected the token `:`".
135        unimplemented!($kv_separator);
136    };
137
138    // Found a comma inside a key. Trigger a reasonable error message.
139    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
140        // Takes no arguments so "no rules expected the token `,`".
141        unimplemented!($comma);
142    };
143
144    // Key is fully parenthesized. This avoids clippy double_parens false
145    // positives because the parenthesization may be necessary here.
146    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
147        $crate::bson!(@object $object ($key) (: $($rest)*) (: $($rest)*));
148    };
149
150    // Munch a token into the current key.
151    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
152        $crate::bson!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
153    };
154
155    //////////////////////////////////////////////////////////////////////////
156    // The main implementation.
157    //
158    // Must be invoked as: bson!($($bson)+)
159    //////////////////////////////////////////////////////////////////////////
160
161    (null) => {
162        $crate::Bson::Null
163    };
164
165    ([]) => {
166        $crate::Bson::Array(vec![])
167    };
168
169    ([ $($tt:tt)+ ]) => {
170        $crate::Bson::Array($crate::bson!(@array [] $($tt)+))
171    };
172
173    ({}) => {
174        $crate::Bson::Document($crate::doc!{})
175    };
176
177    ({$($tt:tt)+}) => {
178        $crate::Bson::Document($crate::doc!{$($tt)+})
179    };
180
181    // Any Into<Bson> type.
182    // Must be below every other rule.
183    ($other:expr) => {
184        <_ as ::std::convert::Into<$crate::Bson>>::into($other)
185    };
186}
187
188/// Construct a bson::Document value.
189///
190/// ```rust
191/// # use bson::doc;
192/// #
193/// # fn main() {
194/// let value = doc! {
195///     "code": 200,
196///     "success": true,
197///     "payload": {
198///       "some": [
199///           "pay",
200///           "loads",
201///       ]
202///     }
203/// };
204/// # }
205/// ```
206#[macro_export]
207macro_rules! doc {
208    () => {{ $crate::Document::new() }};
209    ( $($tt:tt)+ ) => {{
210        let mut object = $crate::Document::new();
211        $crate::bson!(@object object () ($($tt)+) ($($tt)+));
212        object
213    }};
214}
215
216/// Construct a [`crate::RawBson`] value from a literal.
217///
218/// ```rust
219/// use bson::rawbson;
220///
221/// let value = rawbson!({
222///     "code": 200,
223///     "success": true,
224///     "payload": {
225///       "some": [
226///           "pay",
227///           "loads",
228///       ]
229///     }
230/// });
231/// ```
232#[macro_export]
233macro_rules! rawbson {
234    //////////////////////////////////////////////////////////////////////////
235    // TT muncher for parsing the inside of an array [...]. Produces a
236    // RawArrayBuf containing the elements.
237    //
238    // Must be invoked as: bson!(@array [] $($tt)*)
239    //////////////////////////////////////////////////////////////////////////
240
241    // Finished with trailing comma.
242    (@array [$($elems:expr,)*]) => {
243        $crate::RawArrayBuf::from_iter(vec![$($elems,)*])
244    };
245
246    // Finished without trailing comma.
247    (@array [$($elems:expr),*]) => {
248        $crate::RawArrayBuf::from_iter(vec![$($elems),*])
249    };
250
251    // Next element is `null`.
252    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
253        $crate::rawbson!(@array [$($elems,)* $crate::rawbson!(null)] $($rest)*)
254    };
255
256    // Next element is an array.
257    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
258        $crate::rawbson!(@array [$($elems,)* $crate::rawbson!([$($array)*])] $($rest)*)
259    };
260
261    // Next element is a map.
262    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
263        $crate::rawbson!(@array [$($elems,)* $crate::rawbson!({$($map)*})] $($rest)*)
264    };
265
266    // Next element is an expression followed by comma.
267    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
268        $crate::rawbson!(@array [$($elems,)* $crate::rawbson!($next),] $($rest)*)
269    };
270
271    // Last element is an expression with no trailing comma.
272    (@array [$($elems:expr,)*] $last:expr) => {
273        $crate::rawbson!(@array [$($elems,)* $crate::rawbson!($last)])
274    };
275
276    // Comma after the most recent element.
277    (@array [$($elems:expr),*] , $($rest:tt)*) => {
278        $crate::rawbson!(@array [$($elems,)*] $($rest)*)
279    };
280
281    //////////////////////////////////////////////////////////////////////////
282    // TT muncher for parsing the inside of an object {...}. Each entry is
283    // inserted into the given map variable.
284    //
285    // Must be invoked as: rawbson!(@object $map () ($($tt)*) ($($tt)*))
286    //
287    // We require two copies of the input tokens so that we can match on one
288    // copy and trigger errors on the other copy.
289    //////////////////////////////////////////////////////////////////////////
290
291    // Finished.
292    (@object $object:ident () () ()) => {};
293
294    // Insert the current entry with followed by trailing comma, with a key literal.
295    (@object $object:ident [$key:literal] ($value:expr) , $($rest:tt)*) => {{
296        $object.append($crate::raw::cstr!($key), $value);
297        $crate::rawbson!(@object $object () ($($rest)*) ($($rest)*));
298    }};
299
300    // Insert the current entry with followed by trailing comma, with a key expression.
301    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {{
302        $object.append($($key)+, $value);
303        $crate::rawbson!(@object $object () ($($rest)*) ($($rest)*));
304    }};
305
306    // Insert the last entry without trailing comma, with a key literal.
307    (@object $object:ident [$key:literal] ($value:expr)) => {
308        $object.append($crate::raw::cstr!($key), $value);
309    };
310
311    // Insert the last entry without trailing comma, with a key expression.
312    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
313        $object.append($($key)+, $value);
314    };
315
316    // Next value is `null`.
317    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
318        $crate::rawbson!(@object $object [$($key)+] ($crate::rawbson!(null)) $($rest)*);
319    };
320
321    // Next value is an array.
322    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
323        $crate::rawbson!(@object $object [$($key)+] ($crate::rawbson!([$($array)*])) $($rest)*);
324    };
325
326    // Next value is a map.
327    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
328        $crate::rawbson!(@object $object [$($key)+] ($crate::rawbson!({$($map)*})) $($rest)*);
329    };
330
331    // Next value is an expression followed by comma.
332    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
333        $crate::rawbson!(@object $object [$($key)+] ($crate::rawbson!($value)) , $($rest)*);
334    };
335
336    // Last value is an expression with no trailing comma.
337    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
338        $crate::rawbson!(@object $object [$($key)+] ($crate::rawbson!($value)));
339    };
340
341    // Missing value for last entry. Trigger a reasonable error message.
342    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
343        // "unexpected end of macro invocation"
344        $crate::rawbson!();
345    };
346
347    // Missing key-value separator and value for last entry.
348    // Trigger a reasonable error message.
349    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
350        // "unexpected end of macro invocation"
351        $crate::rawbson!();
352    };
353
354    // Misplaced key-value separator. Trigger a reasonable error message.
355    (@object $object:ident () (: $($rest:tt)*) ($kv_separator:tt $($copy:tt)*)) => {
356        // Takes no arguments so "no rules expected the token `:`".
357        unimplemented!($kv_separator);
358    };
359
360    // Found a comma inside a key. Trigger a reasonable error message.
361    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
362        // Takes no arguments so "no rules expected the token `,`".
363        unimplemented!($comma);
364    };
365
366    // Key is fully parenthesized. This avoids clippy double_parens false
367    // positives because the parenthesization may be necessary here.
368    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
369        $crate::rawbson!(@object $object ($key) (: $($rest)*) (: $($rest)*));
370    };
371
372    // Munch a token into the current key.
373    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
374        $crate::rawbson!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
375    };
376
377    //////////////////////////////////////////////////////////////////////////
378    // The main implementation.
379    //
380    // Must be invoked as: rawbson!($($bson)+)
381    //////////////////////////////////////////////////////////////////////////
382
383    (null) => {
384        $crate::RawBson::Null
385    };
386
387    ([]) => {
388        $crate::RawBson::Array($crate::RawArrayBuf::new())
389    };
390
391    ([ $($tt:tt)+ ]) => {
392        $crate::RawBson::Array($crate::rawbson!(@array [] $($tt)+))
393    };
394
395    ({}) => {
396        $crate::RawBson::Document($crate::rawdoc!{})
397    };
398
399    ({$($tt:tt)+}) => {
400        $crate::RawBson::Document($crate::rawdoc!{$($tt)+})
401    };
402
403    // Any Into<RawBson> type.
404    // Must be below every other rule.
405    ($other:expr) => {
406        <_ as ::std::convert::Into<$crate::RawBson>>::into($other)
407    };
408}
409
410/// Construct a [`crate::RawDocumentBuf`] value.
411///
412/// ```rust
413/// use bson::rawdoc;
414///
415/// let value = rawdoc! {
416///     "code": 200,
417///     "success": true,
418///     "payload": {
419///       "some": [
420///           "pay",
421///           "loads",
422///       ]
423///     }
424/// };
425/// ```
426#[macro_export]
427macro_rules! rawdoc {
428    () => {{ $crate::RawDocumentBuf::new() }};
429    ( $($tt:tt)+ ) => {{
430        let mut object = $crate::RawDocumentBuf::new();
431        $crate::rawbson!(@object object () ($($tt)+) ($($tt)+));
432        object
433    }};
434}
435
436/// Like [`serde_with::serde_conv!`], but with additional functionality:
437/// 1. Supports attaching documentation (`///`) and other attributes to the generated struct
438/// 2. Allows serializers that return a [`Result`]`, enabling error handling during serialization
439///
440/// This macro generates a `SerializeAs`/`DeserializeAs` implementation for a given type,
441/// with optional struct-level attributes like `#[derive(...)]` or `/// doc comments`.
442#[cfg(feature = "serde")]
443macro_rules! serde_conv_doc {
444    ($(#[$meta:meta])* $vis:vis $m:ident, $t:ty, $ser:expr, $de:expr) => {
445        #[allow(non_camel_case_types)]
446        $(#[$meta])*
447        $vis struct $m;
448
449        // Prevent clippy lints triggering because of the template here
450        // https://github.com/jonasbb/serde_with/pull/320
451        // https://github.com/jonasbb/serde_with/pull/729
452        #[allow(clippy::all)]
453        #[allow(missing_docs)]
454        const _:() = {
455            impl $m {
456                $vis fn serialize<S>(x: &$t, serializer: S) -> Result<S::Ok, S::Error>
457                where
458                    S: Serializer,
459                {
460                    let y = $ser(x).map_err(serde::ser::Error::custom)?;
461                    Serialize::serialize(&y, serializer)
462                }
463
464                $vis fn deserialize<'de, D>(deserializer: D) -> Result<$t, D::Error>
465                where
466                    D: Deserializer<'de>,
467                {
468                    let y = Deserialize::deserialize(deserializer)?;
469                    $de(y).map_err(serde::de::Error::custom)
470                }
471            }
472
473            #[cfg(feature = "serde_with-3")]
474            impl serde_with::SerializeAs<$t> for $m {
475                fn serialize_as<S>(x: &$t, serializer: S) -> Result<S::Ok, S::Error>
476                where
477                    S: Serializer,
478                {
479                    Self::serialize(x, serializer)
480                }
481            }
482
483            #[cfg(feature = "serde_with-3")]
484            impl<'de> serde_with::DeserializeAs<'de, $t> for $m {
485                fn deserialize_as<D>(deserializer: D) -> Result<$t, D::Error>
486                where
487                    D: Deserializer<'de>,
488                {
489                    Self::deserialize(deserializer)
490                }
491            }
492        };
493    };
494}
495
496#[cfg(feature = "serde")]
497pub(crate) use serde_conv_doc;