#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "visit_diff_derive")]
pub use visit_diff_derive::*;
mod debug;
mod detect;
mod unit;
pub mod constant;
#[macro_use]
mod impls;
#[cfg(feature = "std")]
mod std_impls;
pub mod record;
use core::fmt::Debug;
use itertools::{EitherOrBoth, Itertools};
pub use debug::debug_diff;
pub use detect::{all_different, any_difference};
pub trait Diff: Debug {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where
D: Differ;
}
pub trait Differ {
type Ok;
type Err;
type StructDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>;
type StructVariantDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>;
type TupleDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>;
type TupleVariantDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>;
type SeqDiffer: SeqDiffer<Ok = Self::Ok, Err = Self::Err>;
type MapDiffer: MapDiffer<Ok = Self::Ok, Err = Self::Err>;
type SetDiffer: SetDiffer<Ok = Self::Ok, Err = Self::Err>;
fn difference(self, a: &Debug, b: &Debug) -> Result<Self::Ok, Self::Err>;
fn same(self, a: &Debug, b: &Debug) -> Result<Self::Ok, Self::Err>;
fn diff_newtype<T: ?Sized>(
self,
ty: &'static str,
a: &T,
b: &T,
) -> Result<Self::Ok, Self::Err>
where
T: Diff;
fn begin_struct(self, ty: &'static str) -> Self::StructDiffer;
fn begin_struct_variant(
self,
ty: &'static str,
var: &'static str,
) -> Self::StructVariantDiffer;
fn begin_tuple(self, ty: &'static str) -> Self::TupleDiffer;
fn begin_tuple_variant(
self,
ty: &'static str,
var: &'static str,
) -> Self::TupleVariantDiffer;
fn begin_seq(self) -> Self::SeqDiffer;
fn begin_map(self) -> Self::MapDiffer;
fn begin_set(self) -> Self::SetDiffer;
}
pub trait StructDiffer {
type Ok;
type Err;
fn diff_field<T: ?Sized>(&mut self, name: &'static str, a: &T, b: &T)
where
T: Diff;
fn skip_field<T: ?Sized>(&mut self, _name: &'static str) {}
fn end(self) -> Result<Self::Ok, Self::Err>;
}
pub trait TupleDiffer {
type Ok;
type Err;
fn diff_field<T: ?Sized>(&mut self, a: &T, b: &T)
where
T: Diff;
fn skip_field<T: ?Sized>(&mut self) {}
fn end(self) -> Result<Self::Ok, Self::Err>;
}
pub trait SeqDiffer {
type Ok;
type Err;
fn diff_element<T: ?Sized>(&mut self, a: &T, b: &T)
where
T: Diff;
fn left_excess<T: ?Sized>(&mut self, a: &T)
where
T: Diff;
fn right_excess<T: ?Sized>(&mut self, b: &T)
where
T: Diff;
fn diff_elements<T, I>(&mut self, a: I, b: I)
where
T: Diff,
I: IntoIterator<Item = T>,
{
for ab in a.into_iter().zip_longest(b) {
match ab {
EitherOrBoth::Both(a, b) => self.diff_element(&a, &b),
EitherOrBoth::Left(a) => self.left_excess(&a),
EitherOrBoth::Right(b) => self.right_excess(&b),
}
}
}
fn end(self) -> Result<Self::Ok, Self::Err>;
}
pub trait MapDiffer {
type Ok;
type Err;
fn diff_entry<K, V>(&mut self, key: &K, a: &V, b: &V)
where
K: ?Sized + Debug,
V: ?Sized + Diff;
fn only_in_left<K, V>(&mut self, key: &K, a: &V)
where
K: ?Sized + Debug,
V: ?Sized + Diff;
fn only_in_right<K, V>(&mut self, key: &K, b: &V)
where
K: ?Sized + Debug,
V: ?Sized + Diff;
fn end(self) -> Result<Self::Ok, Self::Err>;
}
pub trait SetDiffer {
type Ok;
type Err;
fn diff_equal<V>(&mut self, a: &V, b: &V)
where
V: ?Sized + Diff;
fn only_in_left<V>(&mut self, a: &V)
where
V: ?Sized + Diff;
fn only_in_right<V>(&mut self, b: &V)
where
V: ?Sized + Diff;
fn end(self) -> Result<Self::Ok, Self::Err>;
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug)]
pub enum TestEnum {
First,
Second,
Struct { a: usize, b: bool },
}
impl Diff for TestEnum {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where
D: Differ,
{
match (a, b) {
(TestEnum::First, TestEnum::First) => out.same(a, b),
(TestEnum::Second, TestEnum::Second) => out.same(a, b),
(
TestEnum::Struct { a: aa, b: ab },
TestEnum::Struct { a: ba, b: bb },
) => {
let mut s = out.begin_struct_variant("TestEnum", "Struct");
s.diff_field("a", &aa, &ba);
s.diff_field("b", &ab, &bb);
s.end()
}
_ => out.difference(a, b),
}
}
}
#[derive(Clone, Debug)]
pub struct TestStruct {
pub distance: usize,
pub silly: bool,
}
impl Diff for TestStruct {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where
D: Differ,
{
let mut s = out.begin_struct("TestStruct");
s.diff_field("distance", &a.distance, &b.distance);
s.diff_field("silly", &a.silly, &b.silly);
s.end()
}
}
}