1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/// A related trait to `PartialEq`, `EventDataEq` tests if two events
/// contain the same data, exclusive of the metadata. This is used to
/// test for events having the same values but potentially different
/// parts of the metadata that not fixed between runs, without removing
/// the ability to compare them for exact equality.
pub trait EventDataEq<Rhs: ?Sized = Self> {
    fn event_data_eq(&self, other: &Rhs) -> bool;
}

#[allow(clippy::module_name_repetitions)] // full name is better since this macro is typically imported
#[macro_export]
macro_rules! assert_event_data_eq {
    ($left:expr, $right:expr, $message:expr) => {{
        use $crate::EventDataEq as _;
        match (&($left), &($right)) {
            (left, right) => {
                assert!(
                    left.event_data_eq(right),
                    "assertion failed: {}\n\n{}\n",
                    $message,
                    similar_asserts::SimpleDiff::from_str(
                        format!("{:#?}", left).as_str(),
                        format!("{:#?}", right).as_str(),
                        "left",
                        "right"
                    ),
                );
            }
        }
    }};
    ($left:expr, $right:expr,) => {
        $crate::assert_event_data_eq!($left, $right)
    };
    ($left:expr, $right:expr) => {
        $crate::assert_event_data_eq!($left, $right, "`left.event_data_eq(right)`")
    };
}

#[allow(clippy::module_name_repetitions)] // full name is better since this macro is typically imported
#[macro_export]
macro_rules! impl_event_data_eq {
    ($type:ty) => {
        impl $crate::EventDataEq for $type {
            fn event_data_eq(&self, other: &Self) -> bool {
                self == other
            }
        }
    };
}

impl<T: EventDataEq> EventDataEq for &[T] {
    fn event_data_eq(&self, other: &Self) -> bool {
        self.len() == other.len()
            && self
                .iter()
                .zip(other.iter())
                .all(|(a, b)| a.event_data_eq(b))
    }
}

impl<T: EventDataEq> EventDataEq for Vec<T> {
    fn event_data_eq(&self, other: &Self) -> bool {
        self.as_slice().event_data_eq(&other.as_slice())
    }
}

impl<T: EventDataEq> EventDataEq for Option<T> {
    fn event_data_eq(&self, other: &Self) -> bool {
        match (self, other) {
            (None, None) => true,
            (Some(left), Some(right)) => left.event_data_eq(right),
            _ => false,
        }
    }
}

impl<R: EventDataEq, E: EventDataEq> EventDataEq for Result<R, E> {
    fn event_data_eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Ok(left), Ok(right)) => left.event_data_eq(right),
            (Err(left), Err(right)) => left.event_data_eq(right),
            _ => false,
        }
    }
}