use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque},
ops::ControlFlow,
};
use crate::Equality;
pub trait MaybeEq<Rhs = Self>
where
Rhs: ?Sized,
{
fn maybe_eq(&self, other: &Rhs) -> Equality;
}
macro_rules! maybe_eq_impl {
($($name:tt,)+) => {
$(
impl MaybeEq for $name {
fn maybe_eq(&self, other: &Self) -> Equality {
Equality::from(self == other)
}
}
)+
};
}
macro_rules! maybe_eq_impl_list {
($($name:tt<$param:ident>,)+) => {
$(
impl<$param> MaybeEq for $name<$param>
where $param: PartialEq {
fn maybe_eq(&self, other: &Self) -> Equality {
Equality::from(self == other)
}
}
)+
};
}
macro_rules! maybe_eq_impl_set {
($($name:tt<$param:ident>,)+) => {
$(
impl<$param> MaybeEq for $name<$param>
where $param: MaybeEq + Eq + std::hash::Hash {
fn maybe_eq(&self, other: &Self) -> Equality {
Equality::from(self == other)
}
}
)+
};
}
macro_rules! maybe_eq_impl_map {
($($name:tt<$key:ident, $value:ident> $(+ $bound:tt)*,)+) => {
$(
impl<$key, $value> MaybeEq for $name<$key, $value>
where
$key: Eq + std::hash::Hash $(+ $bound)*,
$value: MaybeEq {
fn maybe_eq(&self, other: &Self) -> Equality {
if self.len() != other.len() {
Equality::NotEqual
} else {
let equality = self.iter()
.try_fold(Equality::Equal, |equality, (k1, v1)| {
if let Some(v2) = other.get(k1) {
match v1.maybe_eq(v2) {
Equality::NotEqual => ControlFlow::Break(Equality::NotEqual),
Equality::Equal => ControlFlow::Continue(equality),
Equality::Unknown => ControlFlow::Continue(Equality::Unknown),
}
} else {
ControlFlow::Break(Equality::NotEqual)
}
});
match equality {
ControlFlow::Continue(equality) | ControlFlow::Break(equality) => equality,
}
}
}
}
)+
};
}
maybe_eq_impl! {
(),
bool, char, str, String,
f32, f64,
i128, i16, i32, i64, i8, isize,
u128, u16, u32, u64, u8, usize,
}
maybe_eq_impl_list! {
Vec<T>, VecDeque<T>, LinkedList<T>,
}
maybe_eq_impl_set! {
HashSet<T>, BTreeSet<T>,
}
maybe_eq_impl_map! {
HashMap<K, V>,
BTreeMap<K, V> + Ord,
}