use std::fmt;
use std::hash::Hash;
use similar::ChangeTag;
use crate::core::Match;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DiffKind {
String,
Slice,
Set,
Map,
Custom(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DiffTag {
Insert,
Delete,
Equal,
}
impl DiffTag {
pub(super) fn from_tag(tag: ChangeTag) -> Self {
match tag {
ChangeTag::Equal => Self::Equal,
ChangeTag::Delete => Self::Delete,
ChangeTag::Insert => Self::Insert,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DiffSegment {
pub value: String,
pub tag: DiffTag,
}
impl DiffSegment {
pub fn from_debug(value: impl fmt::Debug, tag: DiffTag) -> Self {
Self {
value: format!("{:?}", value),
tag,
}
}
pub fn from_display(value: impl fmt::Display, tag: DiffTag) -> Self {
Self {
value: format!("{}", value),
tag,
}
}
}
pub type Diff = Vec<DiffSegment>;
pub trait Diffable<Other> {
const KIND: DiffKind;
fn diff(&self, other: Other) -> Diff;
}
#[derive(Debug)]
pub struct EqDiffMatcher<Expected> {
expected: Expected,
}
impl<Expected> EqDiffMatcher<Expected> {
pub fn new(expected: Expected) -> Self {
Self { expected }
}
}
impl<Expected, Actual> Match<Actual> for EqDiffMatcher<Expected>
where
Actual: PartialEq<Expected> + Eq,
Expected: Diffable<Actual>,
{
type Fail = Diff;
fn matches(&mut self, actual: &Actual) -> crate::Result<bool> {
Ok(actual == &self.expected)
}
fn fail(self, actual: Actual) -> Self::Fail {
self.expected.diff(actual)
}
}