kaspa_core/
assert.rs

1//!
2//! Assertion macros based on the standard nightly version
3//! <https://doc.rust-lang.org/src/core/macros/mod.rs.html#144-169>
4//!
5
6use std::fmt;
7
8/// Asserts that an expression matches any of the given patterns.
9///
10/// Like in a `match` expression, the pattern can be optionally followed by `if`
11/// and a guard expression that has access to names bound by the pattern.
12///
13/// On panic, this macro will print the value of the expression with its
14/// debug representation.
15///
16/// Like [`assert!`], this macro has a second form, where a custom
17/// panic message can be provided.
18///
19/// # Examples
20///
21/// ```ignore
22/// use crate::assert_match;
23///
24/// let a = 1u32.checked_add(2);
25/// let b = 1u32.checked_sub(2);
26/// assert_match!(a, Some(_));
27/// assert_match!(b, None);
28///
29/// let c = Ok("abc".to_string());
30/// assert_match!(c, Ok(x) | Err(x) if x.len() < 100);
31/// ```
32#[macro_export]
33macro_rules! assert_match {
34    ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {
35        match $left {
36            $( $pattern )|+ $( if $guard )? => {}
37            ref left_val => {
38                $crate::assert::assert_matches_failed(
39                    left_val,
40                    stringify!($($pattern)|+ $(if $guard)?),
41                    core::option::Option::None
42                );
43            }
44        }
45    };
46    ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => {
47        match $left {
48            $( $pattern )|+ $( if $guard )? => {}
49            ref left_val => {
50                $crate::assert::assert_matches_failed(
51                    left_val,
52                    stringify!($($pattern)|+ $(if $guard)?),
53                    std::option::Option::Some(std::format_args!($($arg)+))
54                );
55            }
56        }
57    };
58}
59
60#[derive(Debug)]
61#[doc(hidden)]
62pub enum AssertKind {
63    Eq,
64    Ne,
65    Match,
66}
67
68/// Internal function for `assert_match!`
69#[cold]
70#[track_caller]
71#[doc(hidden)]
72pub fn assert_matches_failed<T: fmt::Debug + ?Sized>(left: &T, right: &str, args: Option<fmt::Arguments<'_>>) -> ! {
73    // Use the Display implementation to display the pattern.
74    struct Pattern<'a>(&'a str);
75    impl fmt::Debug for Pattern<'_> {
76        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77            fmt::Display::fmt(self.0, f)
78        }
79    }
80    assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args);
81}
82
83/// Non-generic version of the above functions, to avoid code bloat.
84#[track_caller]
85fn assert_failed_inner(kind: AssertKind, left: &dyn fmt::Debug, right: &dyn fmt::Debug, args: Option<fmt::Arguments<'_>>) -> ! {
86    let op = match kind {
87        AssertKind::Eq => "==",
88        AssertKind::Ne => "!=",
89        AssertKind::Match => "matches",
90    };
91
92    match args {
93        Some(args) => panic!(
94            r#"assertion failed: `(left {op} right)`
95  left: `{left:?}`,
96 right: `{right:?}`: {args}"#
97        ),
98        None => panic!(
99            r#"assertion failed: `(left {op} right)`
100  left: `{left:?}`,
101 right: `{right:?}`"#,
102        ),
103    }
104}