dbn/
macros.rs

1//! Helper macros for working with multiple RTypes, Schemas, and types of records.
2
3// Re-export
4pub use dbn_macros::{dbn_record, CsvSerialize, DbnAttr, JsonSerialize, PyFieldDesc, RecordDebug};
5
6/// Base macro for type dispatch based on rtype.
7///
8/// # Errors
9/// This macro returns an error if the rtype is not recognized.
10#[doc(hidden)]
11#[macro_export]
12macro_rules! rtype_dispatch_base {
13    ($rec_ref:expr, $handler:ident) => {{
14        // Introduced new scope so new `use`s are ok
15        use $crate::enums::RType;
16        use $crate::record::*;
17        match $rec_ref.rtype() {
18            Ok(RType::Mbp0) => Ok($handler!(TradeMsg)),
19            Ok(RType::Mbp1) => Ok($handler!(Mbp1Msg)),
20            Ok(RType::Mbp10) => Ok($handler!(Mbp10Msg)),
21            #[allow(deprecated)]
22            Ok(RType::OhlcvDeprecated)
23            | Ok(RType::Ohlcv1S)
24            | Ok(RType::Ohlcv1M)
25            | Ok(RType::Ohlcv1H)
26            | Ok(RType::Ohlcv1D)
27            | Ok(RType::OhlcvEod) => Ok($handler!(OhlcvMsg)),
28            Ok(RType::Imbalance) => Ok($handler!(ImbalanceMsg)),
29            Ok(RType::Status) => Ok($handler!(StatusMsg)),
30            Ok(RType::InstrumentDef)
31                if $rec_ref.record_size() < std::mem::size_of::<$crate::v2::InstrumentDefMsg>() =>
32            {
33                Ok($handler!($crate::v1::InstrumentDefMsg))
34            }
35            Ok(RType::InstrumentDef)
36                if $rec_ref.record_size() < std::mem::size_of::<$crate::v3::InstrumentDefMsg>() =>
37            {
38                Ok($handler!($crate::v2::InstrumentDefMsg))
39            }
40            Ok(RType::InstrumentDef) => Ok($handler!($crate::v3::InstrumentDefMsg)),
41            Ok(RType::SymbolMapping)
42                if $rec_ref.record_size() < std::mem::size_of::<SymbolMappingMsg>() =>
43            {
44                Ok($handler!($crate::v1::SymbolMappingMsg))
45            }
46            Ok(RType::SymbolMapping) => Ok($handler!(SymbolMappingMsg)),
47            Ok(RType::Error) if $rec_ref.record_size() < std::mem::size_of::<ErrorMsg>() => {
48                Ok($handler!($crate::v1::ErrorMsg))
49            }
50            Ok(RType::Error) => Ok($handler!(ErrorMsg)),
51            Ok(RType::System) if $rec_ref.record_size() < std::mem::size_of::<SystemMsg>() => {
52                Ok($handler!($crate::v1::SystemMsg))
53            }
54            Ok(RType::System) => Ok($handler!(SystemMsg)),
55            Ok(RType::Statistics)
56                if $rec_ref.record_size() < std::mem::size_of::<$crate::v3::StatMsg>() =>
57            {
58                Ok($handler!($crate::v1::StatMsg))
59            }
60            Ok(RType::Statistics) => Ok($handler!($crate::v3::StatMsg)),
61            Ok(RType::Mbo) => Ok($handler!(MboMsg)),
62            Ok(RType::Cmbp1) | Ok(RType::Tcbbo) => Ok($handler!(Cmbp1Msg)),
63            Ok(RType::Bbo1S) | Ok(RType::Bbo1M) => Ok($handler!(BboMsg)),
64            Ok(RType::Cbbo1S) | Ok(RType::Cbbo1M) => Ok($handler!(CbboMsg)),
65            Err(e) => Err(e),
66        }
67    }};
68}
69
70/// Dispatches to a generic function or method based on `$rtype` and optionally `$ts_out`.
71///
72/// # Panics
73/// This function will panic if the encoded length of the given record is shorter
74/// than expected for its `rtype`.
75///
76/// # Errors
77/// This macro returns an error if the rtype is not recognized.
78#[macro_export]
79macro_rules! rtype_dispatch {
80    // generic async method
81    ($rec_ref:expr, $this:ident.$generic_method:ident($($arg:expr),*).await$(,)?) => {{
82        macro_rules! handler {
83            ($r:ty) => {{
84                $this.$generic_method($rec_ref.get::<$r>().unwrap() $(, $arg)*).await
85            }}
86        }
87        $crate::rtype_dispatch_base!($rec_ref, handler)
88    }};
89    // generic method
90    ($rec_ref:expr, $this:ident.$generic_method:ident($($arg:expr),*)$(,)?) => {{
91        macro_rules! handler {
92            ($r:ty) => {{
93                $this.$generic_method($rec_ref.get::<$r>().unwrap() $(, $arg)*)
94            }}
95        }
96        $crate::rtype_dispatch_base!($rec_ref, handler)
97    }};
98    // generic async function
99    ($rec_ref:expr, $generic_fn:ident($($arg:expr),*).await$(,)?) => {{
100        macro_rules! handler {
101            ($r:ty) => {{
102                $generic_fn($rec_ref.get::<$r>().unwrap() $(, $arg)*).await
103            }}
104        }
105        $crate::rtype_dispatch_base!($rec_ref, handler)
106    }};
107    // generic function
108    ($rec_ref:expr, $generic_fn:ident($($arg:expr),*)$(,)?) => {{
109        macro_rules! handler {
110            ($r:ty) => {{
111                $generic_fn($rec_ref.get::<$r>().unwrap() $(, $arg)*)
112            }}
113        }
114        $crate::rtype_dispatch_base!($rec_ref, handler)
115    }};
116    // generic always ts_out async method
117    ($rec_ref:expr, ts_out: true, $this:ident.$generic_method:ident($($arg:expr),*).await$(,)?) => {{
118        macro_rules! handler {
119            ($r:ty) => {{
120                $this.$generic_method($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*).await
121            }}
122        }
123        $crate::rtype_dispatch_base!($rec_ref, handler)
124    }};
125    // generic always ts_out method
126    ($rec_ref:expr, ts_out: true, $this:ident.$generic_method:ident($($arg:expr),*)$(,)?) => {{
127        macro_rules! handler {
128            ($r:ty) => {{
129                $this.$generic_method($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap(), $(, $arg)*)
130            }}
131        }
132        $crate::rtype_dispatch_base!($rec_ref, handler)
133    }};
134    // generic always ts_out async function
135    ($rec_ref:expr, ts_out: true, $generic_fn:ident($($arg:expr),*).await$(,)?) => {{
136        macro_rules! handler {
137            ($r:ty) => {{
138                $generic_fn($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*).await
139            }}
140        }
141        $crate::rtype_dispatch_base!($rec_ref, handler)
142    }};
143    // generic always ts_out function
144    ($rec_ref:expr, ts_out: true, $generic_fn:ident($($arg:expr),*)$(,)?) => {{
145        macro_rules! handler {
146            ($r:ty) => {{
147                $generic_fn($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*)
148            }}
149        }
150        $crate::rtype_dispatch_base!($rec_ref, handler)
151    }};
152    // ts_out generic async method
153    ($rec_ref:expr, ts_out: $ts_out:expr, $this:ident.$generic_method:ident($($arg:expr),*).await$(,)?) => {{
154        macro_rules! handler {
155            ($r:ty) => {{
156                if $ts_out {
157                    $this.$generic_method($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*).await
158                } else {
159                    $this.$generic_method($rec_ref.get::<$r>().unwrap() $(, $arg)*).await
160                }
161            }}
162        }
163        $crate::rtype_dispatch_base!($rec_ref, handler)
164    }};
165    // ts_out generic method
166    ($rec_ref:expr, ts_out: $ts_out:expr, $this:ident.$generic_method:ident($($arg:expr),*)$(,)?) => {{
167        macro_rules! handler {
168            ($r:ty) => {{
169                if $ts_out {
170                    $this.$generic_method($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*)
171                } else {
172                    $this.$generic_method($rec_ref.get::<$r>().unwrap() $(, $arg)*)
173                }
174            }}
175        }
176        $crate::rtype_dispatch_base!($rec_ref, handler)
177    }};
178    // ts_out generic async function
179    ($rec_ref:expr, ts_out: $ts_out:expr, $generic_fn:ident($($arg:expr),*).await$(,)?) => {{
180        macro_rules! handler {
181            ($r:ty) => {{
182                if $ts_out {
183                    $generic_fn($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*).await
184                } else {
185                    $generic_fn($rec_ref.get::<$r>().unwrap() $(, $arg)*).await
186                }
187            }}
188        }
189        $crate::rtype_dispatch_base!($rec_ref, handler)
190    }};
191    // ts_out generic function
192    ($rec_ref:expr, ts_out: $ts_out:expr, $generic_fn:ident($($arg:expr),*)$(,)?) => {{
193        macro_rules! handler {
194            ($r:ty) => {{
195                if $ts_out {
196                    $generic_fn($rec_ref.get::<$crate::WithTsOut<$r>>().unwrap() $(, $arg)*)
197                } else {
198                    $generic_fn($rec_ref.get::<$r>().unwrap() $(, $arg)*)
199                }
200            }}
201        }
202        $crate::rtype_dispatch_base!($rec_ref, handler)
203    }};
204}
205
206#[doc(hidden)]
207#[macro_export]
208macro_rules! schema_dispatch_base {
209    ($schema:expr, $handler:ident) => {{
210        use $crate::enums::Schema;
211        use $crate::record::*;
212        match $schema {
213            Schema::Mbo => $handler!(MboMsg),
214            Schema::Mbp1 | Schema::Tbbo => $handler!(Mbp1Msg),
215            Schema::Bbo1S | Schema::Bbo1M => $handler!(BboMsg),
216            Schema::Mbp10 => $handler!(Mbp10Msg),
217            Schema::Trades => $handler!(TradeMsg),
218            Schema::Ohlcv1D
219            | Schema::Ohlcv1H
220            | Schema::Ohlcv1M
221            | Schema::Ohlcv1S
222            | Schema::OhlcvEod => {
223                $handler!(OhlcvMsg)
224            }
225            Schema::Definition => $handler!(InstrumentDefMsg),
226            Schema::Statistics => $handler!(StatMsg),
227            Schema::Status => $handler!(StatusMsg),
228            Schema::Imbalance => $handler!(ImbalanceMsg),
229            Schema::Cmbp1 | Schema::Tcbbo => {
230                $handler!(Cmbp1Msg)
231            }
232            Schema::Cbbo1S | Schema::Cbbo1M => {
233                $handler!(CbboMsg)
234            }
235        }
236    }};
237}
238
239/// Dispatches to a generic function or method based on `$schema` and optionally `$ts_out`.
240///
241/// # Errors
242/// This macro returns an error when the generic function or method returns an error.
243#[macro_export]
244macro_rules! schema_dispatch {
245    // generic async method
246    ($schema:expr, $this:ident.$generic_method:ident($($arg:expr),*).await$(,)?) => {{
247        macro_rules! handler {
248            ($r:ty) => {{
249                $this.$generic_method::<$r>($($arg),*).await
250            }}
251        }
252        $crate::schema_dispatch_base!($schema, handler)
253    }};
254    // generic method
255    ($schema:expr, $this:ident.$generic_method:ident($($arg:expr),*)$(,)?) => {{
256        macro_rules! handler {
257            ($r:ty) => {{
258                $this.$generic_method::<$r>($($arg),*)
259            }}
260        }
261        $crate::schema_dispatch_base!($schema, handler)
262    }};
263    // generic async function
264    ($schema:expr, $generic_fn:ident($($arg:expr),*).await$(,)?) => {{
265        macro_rules! handler {
266            ($r:ty) => {{
267                $generic_fn::<$r>($($arg),*).await
268            }}
269        }
270        $crate::schema_dispatch_base!($schema, handler)
271    }};
272    // generic function
273    ($schema:expr, $generic_fn:ident($($arg:expr),*)$(,)?) => {{
274        macro_rules! handler {
275            ($r:ty) => {{
276                $generic_fn::<$r>($($arg),*)
277            }}
278        }
279        $crate::schema_dispatch_base!($schema, handler)
280    }};
281    // generic async method
282    ($schema:expr, ts_out: $ts_out:expr, $this:ident.$generic_method:ident($($arg:expr),*).await$(,)?) => {{
283        macro_rules! handler {
284            ($r:ty) => {{
285                if $ts_out {
286                    $this.$generic_method::<$crate::WithTsOut<$r>>($($arg),*).await
287                } else {
288                    $this.$generic_method::<$r>($($arg),*).await
289                }
290            }}
291        }
292        $crate::schema_dispatch_base!($schema, handler)
293   }};
294    // generic method
295    ($schema:expr, ts_out: $ts_out:expr, $this:ident.$generic_method:ident($($arg:expr),*)$(,)?) => {{
296        macro_rules! handler {
297            ($r:ty) => {{
298                if $ts_out {
299                    $this.$generic_method::<$crate::WithTsOut<$r>>($($arg),*)
300                } else {
301                    $this.$generic_method::<$r>($($arg),*)
302                }
303            }}
304        }
305        $crate::schema_dispatch_base!($schema, handler)
306    }};
307    // generic async function
308    ($schema:expr, ts_out: $ts_out:expr, $generic_fn:ident($($arg:expr),*).await$(,)?) => {{
309        macro_rules! handler {
310            ($r:ty) => {{
311                if $ts_out {
312                    $generic_fn::<$crate::WithTsOut<$r>>($($arg),*).await
313                } else {
314                    $generic_fn::<$r>($($arg),*).await
315                }
316            }}
317        }
318        $crate::schema_dispatch_base!($schema, handler)
319    }};
320    // generic function
321    ($schema:expr, ts_out: $ts_out:expr, $generic_fn:ident($($arg:expr),*)$(,)?) => {{
322        macro_rules! handler {
323            ($r:ty) => {{
324                if $ts_out {
325                    $generic_fn::<$crate::WithTsOut<$r>>($($arg),*)
326                } else {
327                    $generic_fn::<$r>($($arg),*)
328                }
329            }}
330        }
331        $crate::schema_dispatch_base!($schema, handler)
332    }};
333}
334
335// DEPRECATED macros
336
337/// Specializes a generic function to all record types and dispatches based on the
338/// `rtype` and `ts_out`.
339///
340/// # Safety
341/// Assumes `$rec_ref` contains a record with `ts_out` appended. If this is not the
342/// case, the reading the record will read beyond the end of the record.
343///
344/// # Errors
345/// This macro returns an error if the rtype is not recognized.
346#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
347#[macro_export]
348macro_rules! rtype_ts_out_dispatch {
349    ($rec_ref:expr, $ts_out:expr, $generic_fn:expr $(,$arg:expr)*) => {{
350        macro_rules! maybe_ts_out {
351            ($r:ty) => {{
352                if $ts_out {
353                    $generic_fn($rec_ref.get_unchecked::<WithTsOut<$r>>() $(, $arg)*)
354                } else {
355                    $generic_fn(unsafe { $rec_ref.get_unchecked::<$r>() } $(, $arg)*)
356                }
357            }};
358        }
359        $crate::rtype_dispatch_base!($rec_ref, maybe_ts_out)
360    }};
361}
362/// Specializes a generic method to all record types and dispatches based on the
363/// `rtype` and `ts_out`.
364///
365/// # Safety
366/// Assumes `$rec_ref` contains a record with `ts_out` appended. If this is not the
367/// case, the reading the record will read beyond the end of the record.
368///
369/// # Errors
370/// This macro returns an error if the rtype is not recognized.
371#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
372#[macro_export]
373macro_rules! rtype_ts_out_method_dispatch {
374    ($rec_ref:expr, $ts_out:expr, $this:expr, $generic_method:ident $(,$arg:expr)*) => {{
375        macro_rules! maybe_ts_out {
376            ($r:ty) => {{
377                if $ts_out {
378                    $this.$generic_method($rec_ref.get_unchecked::<WithTsOut<$r>>() $(, $arg)*)
379                } else {
380                    $this.$generic_method(unsafe { $rec_ref.get_unchecked::<$r>() } $(, $arg)*)
381                }
382            }};
383        }
384        $crate::rtype_dispatch_base!($rec_ref, maybe_ts_out)
385    }};
386}
387/// Specializes a generic async function to all record types and dispatches based
388/// `rtype` and `ts_out`.
389///
390/// # Errors
391/// This macro returns an error if the rtype is not recognized.
392#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
393#[macro_export]
394macro_rules! rtype_ts_out_async_dispatch {
395    ($rec_ref:expr, $ts_out:expr, $generic_fn:expr $(,$arg:expr)*) => {{
396        macro_rules! maybe_ts_out {
397            ($r:ty) => {{
398                if $ts_out {
399                    $generic_fn($rec_ref.get_unchecked::<WithTsOut<$r>>() $(, $arg)*).await
400                } else {
401                    $generic_fn(unsafe { $rec_ref.get_unchecked::<$r>() } $(, $arg)*).await
402                }
403            }};
404        }
405        $crate::rtype_dispatch_base!($rec_ref, maybe_ts_out)
406    }};
407}
408/// Specializes a generic async method to all record types and dispatches based
409/// `rtype` and `ts_out`.
410///
411/// # Errors
412/// This macro returns an error if the rtype is not recognized.
413#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
414#[macro_export]
415macro_rules! rtype_ts_out_async_method_dispatch {
416    ($rec_ref:expr, $ts_out:expr, $this:expr, $generic_method:ident $(,$arg:expr)*) => {{
417        macro_rules! maybe_ts_out {
418            ($r:ty) => {{
419                if $ts_out {
420                    $this.$generic_method($rec_ref.get_unchecked::<WithTsOut<$r>>() $(, $arg)*).await
421                } else {
422                    $this.$generic_method(unsafe { $rec_ref.get_unchecked::<$r>() } $(, $arg)*).await
423                }
424            }};
425        }
426        $crate::rtype_dispatch_base!($rec_ref, maybe_ts_out)
427    }};
428}
429/// Specializes a generic method to all record types and dispatches based `rtype`.
430///
431/// # Errors
432/// This macro returns an error if the rtype is not recognized.
433#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
434#[macro_export]
435macro_rules! rtype_method_dispatch {
436    ($rec_ref:expr, $this:expr, $generic_method:ident $(,$arg:expr)*) => {{
437        macro_rules! handler {
438            ($r:ty) => {{
439                // Safety: checks rtype before converting.
440                $this.$generic_method( unsafe { $rec_ref.get_unchecked::<$r>() } $(, $arg)*)
441            }}
442        }
443        $crate::rtype_dispatch_base!($rec_ref, handler)
444    }};
445}
446/// Specializes a generic async function to all record types and dispatches based
447/// `rtype`.
448///
449/// # Errors
450/// This macro returns an error if the rtype is not recognized.
451#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
452#[macro_export]
453macro_rules! rtype_async_dispatch {
454    ($rec_ref:expr, $generic_fn:expr $(,$arg:expr)*) => {{
455        macro_rules! handler {
456            ($r:ty) => {{
457                // Safety: checks rtype before converting.
458                $generic_fn( unsafe { $rec_ref.get_unchecked::<$r>() } $(, $arg)*).await
459            }}
460        }
461        $crate::rtype_dispatch_base!($rec_ref, handler)
462    }};
463}
464/// Specializes a generic function to all record types wrapped in
465/// [`WithTsOut`](crate::record::WithTsOut) and dispatches based on the `rtype`.
466///
467/// # Safety
468/// Assumes `$rec_ref` contains a record with `ts_out` appended. If this is not the
469/// case, the reading the record will read beyond the end of the record.
470///
471/// # Errors
472/// This macro returns an error if the rtype is not recognized.
473#[deprecated(since = "0.32.0", note = "Use the updated rtype_dispatch macro")]
474#[macro_export]
475macro_rules! rtype_dispatch_with_ts_out {
476    ($rec_ref:expr, $generic_fn:expr $(,$arg:expr)*) => {{
477        macro_rules! handler {
478            ($r:ty) => {{
479                $generic_fn( $rec_ref.get_unchecked::<WithTsOut<$r>>() $(, $arg)*)
480            }}
481        }
482        $crate::rtype_dispatch_base!($rec_ref, handler)
483    };
484}}
485
486/// Specializes a generic method to all record types with an associated schema.
487#[deprecated(since = "0.32.0", note = "Use the updated schema_dispatch macro")]
488#[macro_export]
489macro_rules! schema_method_dispatch {
490    ($schema:expr, $this:expr, $generic_method:ident $(,$arg:expr)*) => {{
491        macro_rules! handler {
492            ($r:ty) => {{
493                $this.$generic_method::<$r>($($arg),*)
494            }}
495        }
496        $crate::schema_dispatch_base!($schema, handler)
497    }};
498}
499/// Specializes a generic method to all record types based on the associated type for
500/// `schema` and `ts_out`.
501#[deprecated(since = "0.32.0", note = "Use the updated schema_dispatch macro")]
502#[macro_export]
503macro_rules! schema_ts_out_method_dispatch {
504    ($schema:expr, $ts_out:expr, $this:expr, $generic_method:ident $(,$arg:expr)*) => {{
505        macro_rules! handler {
506            ($r:ty) => {{
507                if $ts_out {
508                    $this.$generic_method::<WithTsOut<$r>>($($arg),*)
509                } else {
510                    $this.$generic_method::<$r>($($arg),*)
511                }
512            }}
513        }
514        $crate::schema_dispatch_base!($schema, handler)
515    }};
516}
517/// Specializes a generic async method to all record types with an associated
518/// schema.
519#[deprecated(since = "0.32.0", note = "Use the updated schema_dispatch macro")]
520#[macro_export]
521macro_rules! schema_async_method_dispatch {
522    ($schema:expr, $this:expr, $generic_method:ident $(,$arg:expr)*) => {{
523        macro_rules! handler {
524            ($r:ty) => {{
525                $this.$generic_method::<$r>($($arg),*).await
526            }}
527        }
528        $crate::schema_dispatch_base!($schema, handler)
529    }};
530}
531
532#[cfg(test)]
533mod tests {
534    use crate::{record::HasRType, rtype, schema_dispatch};
535
536    struct Dummy {}
537
538    #[allow(dead_code)]
539    impl Dummy {
540        fn on_rtype<T: HasRType>(&self) -> bool {
541            T::has_rtype(0xFF)
542        }
543
544        #[allow(clippy::extra_unused_type_parameters)]
545        fn on_rtype_2<T: HasRType>(&self, x: u64, y: u64) -> u64 {
546            x + y
547        }
548
549        async fn do_something<T: HasRType>(&self, arg: u8) -> bool {
550            T::has_rtype(arg)
551        }
552    }
553
554    fn has_rtype<T: HasRType>(arg: u8) -> bool {
555        T::has_rtype(arg)
556    }
557
558    #[test]
559    fn test_two_args() {
560        assert!(schema_dispatch!(
561            Schema::Imbalance,
562            has_rtype(rtype::IMBALANCE)
563        ))
564    }
565
566    #[test]
567    fn test_method_two_args() {
568        let dummy = Dummy {};
569        let ret = schema_dispatch!(Schema::Definition, dummy.on_rtype_2(5, 6));
570        assert_eq!(ret, 11);
571    }
572
573    #[test]
574    fn test_method_no_args() {
575        let dummy = Dummy {};
576        let ret = schema_dispatch!(Schema::Definition, dummy.on_rtype());
577        assert!(!ret);
578    }
579
580    #[cfg(feature = "async")]
581    mod r#async {
582        use super::*;
583
584        #[tokio::test]
585        async fn test_self() {
586            let target = Dummy {};
587            let ret_true = schema_dispatch!(
588                Schema::Trades,
589                target.do_something(crate::enums::rtype::MBP_0).await,
590            );
591            let ret_false = schema_dispatch!(Schema::Trades, target.do_something(0xff).await);
592            assert!(ret_true);
593            assert!(!ret_false);
594        }
595    }
596}