#![doc = include_str!("../README.md")]
#![forbid(unsafe_code, missing_docs, missing_debug_implementations)]
use std::borrow::Cow;
use std::hash::Hash;
use std::rc::Rc;
use std::sync::Arc;
use std::{cmp::Ordering, hash::Hasher};
use either::{for_both, Either};
#[cfg(feature = "derive")]
pub use equivalence_derive::Equivalence;
mod macros;
pub use macros::*;
pub trait PartialEqWith<C: ?Sized, T: ?Sized = Self> {
fn eq_with(&self, other: &T, ctx: &C) -> bool;
#[inline(always)]
fn ne_with(&self, other: &T, ctx: &C) -> bool {
!self.eq_with(other, ctx)
}
}
pub trait EqWith<C: ?Sized>: PartialEqWith<C> {}
pub trait PartialOrdWith<C: ?Sized, T: ?Sized = Self>: PartialEqWith<C, T> {
fn partial_cmp_with(&self, other: &T, ctx: &C) -> Option<Ordering>;
#[inline(always)]
fn ge_with(&self, other: &T, ctx: &C) -> bool {
matches!(
self.partial_cmp_with(other, ctx),
Some(Ordering::Greater) | Some(Ordering::Equal)
)
}
#[inline(always)]
fn gt_with(&self, other: &T, ctx: &C) -> bool {
self.partial_cmp_with(other, ctx) == Some(Ordering::Greater)
}
#[inline(always)]
fn le_with(&self, other: &T, ctx: &C) -> bool {
matches!(
self.partial_cmp_with(other, ctx),
Some(Ordering::Less) | Some(Ordering::Equal)
)
}
#[inline(always)]
fn lt_with(&self, other: &T, ctx: &C) -> bool {
self.partial_cmp_with(other, ctx) == Some(Ordering::Less)
}
}
pub trait OrdWith<C: ?Sized>: PartialOrdWith<C, Self> + EqWith<C> {
fn cmp_with(&self, other: &Self, ctx: &C) -> Ordering;
#[inline]
fn max_with(self, other: Self, ctx: &C) -> Self
where
Self: Sized,
{
if self.ge_with(&other, ctx) {
self
} else {
other
}
}
#[inline]
fn min_with(self, other: Self, ctx: &C) -> Self
where
Self: Sized,
{
if self.le_with(&other, ctx) {
self
} else {
other
}
}
#[inline]
fn clamp_with(self, min: Self, max: Self, ctx: &C) -> Self
where
Self: Sized,
{
if self.le_with(&max, ctx) {
if self.ge_with(&min, ctx) {
self
} else {
min
}
} else {
max
}
}
}
pub trait HashWith<C: ?Sized, T: ?Sized = Self> {
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C);
}
impl<A, B, C> PartialEqWith<C, &B> for &A
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &&B, ctx: &C) -> bool {
(*self).eq_with(*other, ctx)
}
}
impl<A, C> EqWith<C> for &A where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, &B> for &A
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &&B, ctx: &C) -> Option<Ordering> {
(*self).partial_cmp_with(*other, ctx)
}
}
impl<A, C> OrdWith<C> for &A
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &&A, ctx: &C) -> Ordering {
(*self).cmp_with(*other, ctx)
}
}
impl<A, C> HashWith<C> for &A
where
A: HashWith<C>,
{
#[inline(always)]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
(*self).hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, &mut B> for &mut A
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &&mut B, ctx: &C) -> bool {
(**self).eq_with(*other, ctx)
}
}
impl<A, C> EqWith<C> for &mut A where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, &mut B> for &mut A
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &&mut B, ctx: &C) -> Option<Ordering> {
(**self).partial_cmp_with(*other, ctx)
}
}
impl<A, C> OrdWith<C> for &mut A
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &&mut A, ctx: &C) -> Ordering {
(**self).cmp_with(*other, ctx)
}
}
impl<A, C> HashWith<C> for &mut A
where
A: HashWith<C>,
{
#[inline(always)]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
(**self).hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, Box<B>> for Box<A>
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &Box<B>, ctx: &C) -> bool {
(**self).eq_with(&**other, ctx)
}
}
impl<A, C> EqWith<C> for Box<A> where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, Box<B>> for Box<A>
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &Box<B>, ctx: &C) -> Option<Ordering> {
(**self).partial_cmp_with(&**other, ctx)
}
}
impl<A, C> OrdWith<C> for Box<A>
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &Box<A>, ctx: &C) -> Ordering {
(**self).cmp_with(&**other, ctx)
}
}
impl<A, C> HashWith<C> for Box<A>
where
A: HashWith<C>,
{
#[inline(always)]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
(**self).hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, [B]> for [A]
where
A: PartialEqWith<C, B>,
{
fn eq_with(&self, other: &[B], ctx: &C) -> bool {
self.len() == other.len()
&& self
.iter()
.zip(other.iter())
.all(|(a, b)| a.eq_with(b, ctx))
}
}
impl<A, C> EqWith<C> for [A] where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, [B]> for [A]
where
A: PartialOrdWith<C, B>,
{
fn partial_cmp_with(&self, other: &[B], ctx: &C) -> Option<Ordering> {
for (a, b) in self.iter().zip(other.iter()) {
match a.partial_cmp_with(b, ctx) {
Some(Ordering::Equal) => {}
o => return o,
}
}
Some(self.len().cmp(&other.len()))
}
}
impl<A, C> OrdWith<C> for [A]
where
A: OrdWith<C>,
{
fn cmp_with(&self, other: &[A], ctx: &C) -> Ordering {
for (a, b) in self.iter().zip(other.iter()) {
match a.cmp_with(b, ctx) {
Ordering::Equal => {}
o => return o,
}
}
self.len().cmp(&other.len())
}
}
impl<A, C> HashWith<C> for [A]
where
A: HashWith<C>,
{
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
for a in self.iter() {
a.hash_with(hasher, ctx)
}
}
}
impl<A, B, C> PartialEqWith<C, Vec<B>> for Vec<A>
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &Vec<B>, ctx: &C) -> bool {
self[..].eq_with(&other[..], ctx)
}
}
impl<A, C> EqWith<C> for Vec<A> where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, Vec<B>> for Vec<A>
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &Vec<B>, ctx: &C) -> Option<Ordering> {
self[..].partial_cmp_with(&other[..], ctx)
}
}
impl<A, C> OrdWith<C> for Vec<A>
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &Vec<A>, ctx: &C) -> Ordering {
self[..].cmp_with(&other[..], ctx)
}
}
impl<A, C> HashWith<C> for Vec<A>
where
A: HashWith<C>,
{
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
self[..].hash_with(hasher, ctx)
}
}
impl<const N: usize, A, B, C> PartialEqWith<C, [B; N]> for [A; N]
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &[B; N], ctx: &C) -> bool {
self[..].eq_with(&other[..], ctx)
}
}
impl<const N: usize, A, C> EqWith<C> for [A; N] where A: EqWith<C> {}
impl<const N: usize, A, B, C> PartialOrdWith<C, [B; N]> for [A; N]
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &[B; N], ctx: &C) -> Option<Ordering> {
self[..].partial_cmp_with(&other[..], ctx)
}
}
impl<const N: usize, A, C> OrdWith<C> for [A; N]
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &[A; N], ctx: &C) -> Ordering {
self[..].cmp_with(&other[..], ctx)
}
}
impl<const N: usize, A, C> HashWith<C> for [A; N]
where
A: HashWith<C>,
{
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
self[..].hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, Rc<B>> for Rc<A>
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &Rc<B>, ctx: &C) -> bool {
(**self).eq_with(&**other, ctx)
}
}
impl<A, C> EqWith<C> for Rc<A> where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, Rc<B>> for Rc<A>
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &Rc<B>, ctx: &C) -> Option<Ordering> {
(**self).partial_cmp_with(&**other, ctx)
}
}
impl<A, C> OrdWith<C> for Rc<A>
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &Rc<A>, ctx: &C) -> Ordering {
(**self).cmp_with(&**other, ctx)
}
}
impl<A, C> HashWith<C> for Rc<A>
where
A: HashWith<C>,
{
#[inline(always)]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
(**self).hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, Arc<B>> for Arc<A>
where
A: PartialEqWith<C, B>,
{
#[inline(always)]
fn eq_with(&self, other: &Arc<B>, ctx: &C) -> bool {
(**self).eq_with(&**other, ctx)
}
}
impl<A, C> EqWith<C> for Arc<A> where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, Arc<B>> for Arc<A>
where
A: PartialOrdWith<C, B>,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &Arc<B>, ctx: &C) -> Option<Ordering> {
(**self).partial_cmp_with(&**other, ctx)
}
}
impl<A, C> OrdWith<C> for Arc<A>
where
A: OrdWith<C>,
{
#[inline(always)]
fn cmp_with(&self, other: &Arc<A>, ctx: &C) -> Ordering {
(**self).cmp_with(&**other, ctx)
}
}
impl<A, C> HashWith<C> for Arc<A>
where
A: HashWith<C>,
{
#[inline(always)]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
(**self).hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, Cow<'_, B>> for Cow<'_, A>
where
A: PartialEqWith<C, B> + ToOwned,
B: ToOwned,
{
#[inline(always)]
fn eq_with(&self, other: &Cow<B>, ctx: &C) -> bool {
(**self).eq_with(&**other, ctx)
}
}
impl<A, C> EqWith<C> for Cow<'_, A> where A: EqWith<C> + ToOwned {}
impl<A, B, C> PartialOrdWith<C, Cow<'_, B>> for Cow<'_, A>
where
A: PartialOrdWith<C, B> + ToOwned,
B: ToOwned,
{
#[inline(always)]
fn partial_cmp_with(&self, other: &Cow<B>, ctx: &C) -> Option<Ordering> {
(**self).partial_cmp_with(&**other, ctx)
}
}
impl<A, C> OrdWith<C> for Cow<'_, A>
where
A: OrdWith<C> + ToOwned,
{
#[inline(always)]
fn cmp_with(&self, other: &Cow<A>, ctx: &C) -> Ordering {
(**self).cmp_with(&**other, ctx)
}
}
impl<A, C> HashWith<C> for Cow<'_, A>
where
A: HashWith<C> + ToOwned,
{
#[inline(always)]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
(**self).hash_with(hasher, ctx)
}
}
impl<A, B, C> PartialEqWith<C, Option<B>> for Option<A>
where
A: PartialEqWith<C, B>,
{
#[inline]
fn eq_with(&self, other: &Option<B>, ctx: &C) -> bool {
match (self, other) {
(Some(this), Some(other)) => this.eq_with(other, ctx),
(None, None) => true,
_ => false,
}
}
}
impl<A, C> EqWith<C> for Option<A> where A: EqWith<C> {}
impl<A, B, C> PartialOrdWith<C, Option<B>> for Option<A>
where
A: PartialOrdWith<C, B>,
{
#[inline]
fn partial_cmp_with(&self, other: &Option<B>, ctx: &C) -> Option<Ordering> {
match (self, other) {
(Some(this), Some(other)) => this.partial_cmp_with(other, ctx),
(None, Some(_)) => Some(Ordering::Less),
(Some(_), None) => Some(Ordering::Greater),
(None, None) => Some(Ordering::Equal),
}
}
}
impl<A, C> OrdWith<C> for Option<A>
where
A: OrdWith<C>,
{
#[inline]
fn cmp_with(&self, other: &Option<A>, ctx: &C) -> Ordering {
match (self, other) {
(Some(this), Some(other)) => this.cmp_with(other, ctx),
(None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater,
(None, None) => Ordering::Equal,
}
}
}
impl<A, C> HashWith<C> for Option<A>
where
A: HashWith<C>,
{
#[inline]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
std::mem::discriminant(self).hash(hasher);
if let Some(this) = self {
this.hash_with(hasher, ctx)
}
}
}
impl<A1, B1, A2, B2, C> PartialEqWith<C, Either<A2, B2>> for Either<A1, B1>
where
A1: PartialEqWith<C, A2>,
B1: PartialEqWith<C, B2>,
{
#[inline]
fn eq_with(&self, other: &Either<A2, B2>, ctx: &C) -> bool {
match (self, other) {
(Either::Left(this), Either::Left(other)) => this.eq_with(other, ctx),
(Either::Right(this), Either::Right(other)) => this.eq_with(other, ctx),
_ => false,
}
}
}
impl<A, B, C> EqWith<C> for Either<A, B>
where
A: EqWith<C>,
B: EqWith<C>,
{
}
impl<A1, B1, A2, B2, C> PartialOrdWith<C, Either<A2, B2>> for Either<A1, B1>
where
A1: PartialOrdWith<C, A2>,
B1: PartialOrdWith<C, B2>,
{
#[inline]
fn partial_cmp_with(&self, other: &Either<A2, B2>, ctx: &C) -> Option<Ordering> {
match (self, other) {
(Either::Left(this), Either::Left(other)) => this.partial_cmp_with(other, ctx),
(Either::Right(this), Either::Right(other)) => this.partial_cmp_with(other, ctx),
(Either::Left(_), Either::Right(_)) => Some(Ordering::Less),
(Either::Right(_), Either::Left(_)) => Some(Ordering::Greater),
}
}
}
impl<A, B, C> OrdWith<C> for Either<A, B>
where
A: OrdWith<C>,
B: OrdWith<C>,
{
#[inline]
fn cmp_with(&self, other: &Either<A, B>, ctx: &C) -> Ordering {
match (self, other) {
(Either::Left(this), Either::Left(other)) => this.cmp_with(other, ctx),
(Either::Right(this), Either::Right(other)) => this.cmp_with(other, ctx),
(Either::Left(_), Either::Right(_)) => Ordering::Less,
(Either::Right(_), Either::Left(_)) => Ordering::Greater,
}
}
}
impl<A, B, C> HashWith<C> for Either<A, B>
where
A: HashWith<C>,
B: HashWith<C>,
{
#[inline]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
std::mem::discriminant(self).hash(hasher);
for_both!(self, x => x.hash_with(hasher, ctx))
}
}
impl<A1, B1, A2, B2, C> PartialEqWith<C, (A2, B2)> for (A1, B1)
where
A1: PartialEqWith<C, A2>,
B1: PartialEqWith<C, B2>,
{
#[inline]
fn eq_with(&self, other: &(A2, B2), ctx: &C) -> bool {
self.0.eq_with(&other.0, ctx) && self.1.eq_with(&other.1, ctx)
}
}
impl<A, B, C> EqWith<C> for (A, B)
where
A: EqWith<C>,
B: EqWith<C>,
{
}
impl<A1, B1, A2, B2, C> PartialOrdWith<C, (A2, B2)> for (A1, B1)
where
A1: PartialOrdWith<C, A2>,
B1: PartialOrdWith<C, B2>,
{
#[inline]
fn partial_cmp_with(&self, other: &(A2, B2), ctx: &C) -> Option<Ordering> {
match self.0.partial_cmp_with(&other.0, ctx) {
Some(Ordering::Equal) => self.1.partial_cmp_with(&other.1, ctx),
o => o,
}
}
}
impl<A, B, C> OrdWith<C> for (A, B)
where
A: OrdWith<C>,
B: OrdWith<C>,
{
#[inline]
fn cmp_with(&self, other: &(A, B), ctx: &C) -> Ordering {
self.0
.cmp_with(&other.0, ctx)
.then_with(|| self.1.cmp_with(&other.1, ctx))
}
}
impl<A, B, C> HashWith<C> for (A, B)
where
A: HashWith<C>,
B: HashWith<C>,
{
#[inline]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &C) {
self.0.hash_with(hasher, ctx);
self.1.hash_with(hasher, ctx);
}
}
impl<A1, B1, C1, A2, B2, C2, X> PartialEqWith<X, (A2, B2, C2)> for (A1, B1, C1)
where
A1: PartialEqWith<X, A2>,
B1: PartialEqWith<X, B2>,
C1: PartialEqWith<X, C2>,
{
#[inline]
fn eq_with(&self, other: &(A2, B2, C2), ctx: &X) -> bool {
self.0.eq_with(&other.0, ctx)
&& self.1.eq_with(&other.1, ctx)
&& self.2.eq_with(&other.2, ctx)
}
}
impl<A, B, C, X> EqWith<X> for (A, B, C)
where
A: EqWith<X>,
B: EqWith<X>,
C: EqWith<X>,
{
}
impl<A1, B1, C1, A2, B2, C2, X> PartialOrdWith<X, (A2, B2, C2)> for (A1, B1, C1)
where
A1: PartialOrdWith<X, A2>,
B1: PartialOrdWith<X, B2>,
C1: PartialOrdWith<X, C2>,
{
#[inline]
fn partial_cmp_with(&self, other: &(A2, B2, C2), ctx: &X) -> Option<Ordering> {
match self.0.partial_cmp_with(&other.0, ctx) {
Some(Ordering::Equal) => {}
o => return o,
}
self.1.partial_cmp_with(&other.1, ctx)
}
}
impl<A, B, C, X> OrdWith<X> for (A, B, C)
where
A: OrdWith<X>,
B: OrdWith<X>,
C: OrdWith<X>,
{
#[inline]
fn cmp_with(&self, other: &(A, B, C), ctx: &X) -> Ordering {
self.0
.cmp_with(&other.0, ctx)
.then_with(|| self.1.cmp_with(&other.1, ctx))
.then_with(|| self.2.cmp_with(&other.2, ctx))
}
}
impl<A, B, C, X> HashWith<X> for (A, B, C)
where
A: HashWith<X>,
B: HashWith<X>,
C: HashWith<X>,
{
#[inline]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &X) {
self.0.hash_with(hasher, ctx);
self.1.hash_with(hasher, ctx);
self.2.hash_with(hasher, ctx);
}
}
impl<A1, B1, C1, D1, A2, B2, C2, D2, X> PartialEqWith<X, (A2, B2, C2, D2)> for (A1, B1, C1, D1)
where
A1: PartialEqWith<X, A2>,
B1: PartialEqWith<X, B2>,
C1: PartialEqWith<X, C2>,
D1: PartialEqWith<X, D2>,
{
#[inline]
fn eq_with(&self, other: &(A2, B2, C2, D2), ctx: &X) -> bool {
self.0.eq_with(&other.0, ctx)
&& self.1.eq_with(&other.1, ctx)
&& self.2.eq_with(&other.2, ctx)
&& self.3.eq_with(&other.3, ctx)
}
}
impl<A, B, C, D, X> EqWith<X> for (A, B, C, D)
where
A: EqWith<X>,
B: EqWith<X>,
C: EqWith<X>,
D: EqWith<X>,
{
}
impl<A1, B1, C1, D1, A2, B2, C2, D2, X> PartialOrdWith<X, (A2, B2, C2, D2)> for (A1, B1, C1, D1)
where
A1: PartialOrdWith<X, A2>,
B1: PartialOrdWith<X, B2>,
C1: PartialOrdWith<X, C2>,
D1: PartialOrdWith<X, D2>,
{
#[inline]
fn partial_cmp_with(&self, other: &(A2, B2, C2, D2), ctx: &X) -> Option<Ordering> {
match self.0.partial_cmp_with(&other.0, ctx) {
Some(Ordering::Equal) => {}
o => return o,
}
match self.1.partial_cmp_with(&other.1, ctx) {
Some(Ordering::Equal) => {}
o => return o,
}
match self.2.partial_cmp_with(&other.2, ctx) {
Some(Ordering::Equal) => {}
o => return o,
}
self.3.partial_cmp_with(&other.3, ctx)
}
}
impl<A, B, C, D, X> OrdWith<X> for (A, B, C, D)
where
A: OrdWith<X>,
B: OrdWith<X>,
C: OrdWith<X>,
D: OrdWith<X>,
{
#[inline]
fn cmp_with(&self, other: &(A, B, C, D), ctx: &X) -> Ordering {
self.0
.cmp_with(&other.0, ctx)
.then_with(|| self.1.cmp_with(&other.1, ctx))
.then_with(|| self.2.cmp_with(&other.2, ctx))
.then_with(|| self.3.cmp_with(&other.3, ctx))
}
}
impl<A, B, C, D, X> HashWith<X> for (A, B, C, D)
where
A: HashWith<X>,
B: HashWith<X>,
C: HashWith<X>,
D: HashWith<X>,
{
#[inline]
fn hash_with<H: Hasher>(&self, hasher: &mut H, ctx: &X) {
self.0.hash_with(hasher, ctx);
self.1.hash_with(hasher, ctx);
self.2.hash_with(hasher, ctx);
self.3.hash_with(hasher, ctx);
}
}