use crate::description::Description;
pub trait Matcher<T: ?Sized> {
fn check(&self, actual: &T) -> MatchResult;
fn description(&self) -> Description;
}
#[derive(Debug, Clone)]
pub struct MatchResult {
pub matched: bool,
pub failure: Option<Mismatch>,
}
impl MatchResult {
#[must_use]
pub fn pass() -> Self {
Self {
matched: true,
failure: None,
}
}
#[must_use]
pub fn fail(mismatch: Mismatch) -> Self {
Self {
matched: false,
failure: Some(mismatch),
}
}
}
#[derive(Debug, Clone)]
pub struct Mismatch {
pub expected: Description,
pub actual: String,
pub diff: Option<String>,
}
impl Mismatch {
#[must_use]
pub fn new(expected: Description, actual: impl Into<String>) -> Self {
Self {
expected,
actual: actual.into(),
diff: None,
}
}
#[must_use]
pub fn with_diff(mut self, diff: impl Into<String>) -> Self {
self.diff = Some(diff.into());
self
}
}
#[cfg(test)]
mod tests {
use test_better_core::{OrFail, TestResult};
use super::*;
use crate::{check, eq, is_false, is_true};
#[test]
fn pass_has_no_failure() -> TestResult {
let result = MatchResult::pass();
check!(result.matched).satisfies(is_true())?;
check!(result.failure.is_none()).satisfies(is_true())?;
Ok(())
}
#[test]
fn fail_carries_the_mismatch() -> TestResult {
let mismatch = Mismatch::new(Description::text("equal to 4"), "5");
let result = MatchResult::fail(mismatch);
check!(result.matched).satisfies(is_false())?;
let failure = result.failure.or_fail_with("fail() stores the mismatch")?;
check!(failure.expected.to_string()).satisfies(eq("equal to 4".to_string()))?;
check!(failure.actual).satisfies(eq("5".to_string()))?;
check!(failure.diff.is_none()).satisfies(is_true())?;
Ok(())
}
#[test]
fn mismatch_with_diff_stores_the_diff() -> TestResult {
let mismatch = Mismatch::new(Description::text("the file"), "other").with_diff("- a\n+ b");
check!(mismatch.diff.as_deref()).satisfies(eq(Some("- a\n+ b")))?;
Ok(())
}
}