use std::cmp::Ordering;
use std::fmt;
use std::marker::PhantomData;
use crate::core::Match;
use super::{Expectation, Mismatch};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Inequality {
Less,
LessOrEqual,
Greater,
GreaterOrEqual,
}
#[derive(Debug)]
pub struct OrdMatcher<Expected> {
expected: Expected,
kind: Inequality,
}
impl<Expected> OrdMatcher<Expected> {
pub fn new(expected: Expected, kind: Inequality) -> Self {
Self { expected, kind }
}
}
impl<Expected, Actual> Match<Actual> for OrdMatcher<Expected>
where
Actual: PartialOrd<Expected>,
{
type Fail = Mismatch<Expected, Actual>;
fn matches(&mut self, actual: &Actual) -> crate::Result<bool> {
Ok(match self.kind {
Inequality::Less => actual < &self.expected,
Inequality::LessOrEqual => actual <= &self.expected,
Inequality::Greater => actual > &self.expected,
Inequality::GreaterOrEqual => actual >= &self.expected,
})
}
fn fail(self, actual: Actual) -> Self::Fail {
Mismatch {
actual,
expected: self.expected,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum SortOrder {
#[allow(missing_docs)]
Asc,
#[allow(missing_docs)]
Desc,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct BeSortedMatcher<T> {
order: SortOrder,
marker: PhantomData<T>,
}
impl<T> BeSortedMatcher<T> {
pub fn new(order: SortOrder) -> Self {
Self {
order,
marker: PhantomData,
}
}
}
impl<T, Actual> Match<Actual> for BeSortedMatcher<T>
where
T: Ord,
Actual: AsRef<[T]>,
{
type Fail = Expectation<Actual>;
fn matches(&mut self, actual: &Actual) -> crate::Result<bool> {
Ok(actual.as_ref().windows(2).all(|window| match self.order {
SortOrder::Asc => window[0] <= window[1],
SortOrder::Desc => window[0] >= window[1],
}))
}
fn fail(self, actual: Actual) -> Self::Fail {
Expectation { actual }
}
}
type BoxSortPredicate<'a, T> = Box<dyn Fn(&T, &T) -> Ordering + 'a>;
#[non_exhaustive]
pub struct BeSortedByMatcher<'a, T> {
predicate: BoxSortPredicate<'a, T>,
marker: PhantomData<T>,
}
impl<'a, T> fmt::Debug for BeSortedByMatcher<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BeSortedByMatcher").finish_non_exhaustive()
}
}
impl<'a, T> BeSortedByMatcher<'a, T> {
pub fn new(predicate: impl Fn(&T, &T) -> Ordering + 'a) -> Self {
Self {
predicate: Box::new(predicate),
marker: PhantomData,
}
}
}
impl<'a, T, Actual> Match<Actual> for BeSortedByMatcher<'a, T>
where
T: Ord,
Actual: AsRef<[T]>,
{
type Fail = Expectation<Actual>;
fn matches(&mut self, actual: &Actual) -> crate::Result<bool> {
Ok(actual.as_ref().windows(2).all(|window| {
let ordering = (self.predicate)(&window[0], &window[1]);
ordering == Ordering::Less || ordering == Ordering::Equal
}))
}
fn fail(self, actual: Actual) -> Self::Fail {
Expectation { actual }
}
}