#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
use crate::matchers::{and, or, And, Or};
use std::fmt::Debug;
pub mod matchers;
#[cfg(feature = "http")]
pub mod http;
#[cfg(test)]
mod tests;
#[derive(PartialEq, Debug, Default)]
pub enum MatchType {
#[default]
To,
ToBe,
ToHave,
}
impl MatchType {
fn description(&self) -> &str {
match self {
MatchType::To => "to",
MatchType::ToBe => "to be",
MatchType::ToHave => "to have",
}
}
}
pub trait Matcher<T> {
fn matches(&self, value: &T) -> bool;
fn description(&self) -> String;
}
pub trait TypedMatcher<T>: Matcher<T> {
fn matcher_type(&self) -> MatchType;
}
pub trait MatcherExt<T>: TypedMatcher<T> + Sized + 'static {
fn and<M: TypedMatcher<T> + 'static>(self, matcher: M) -> And<T> {
and(vec![Box::new(self), Box::new(matcher)])
}
fn or<M: TypedMatcher<T> + 'static>(self, matcher: M) -> Or<T> {
or(vec![Box::new(self), Box::new(matcher)])
}
}
impl<M, T> MatcherExt<T> for M where M: TypedMatcher<T> + 'static {}
pub struct Expect<T> {
value: T,
}
impl<T> Expect<T>
where
T: Debug,
{
pub fn new(value: T) -> Self {
Expect { value }
}
fn assert<M>(&mut self, matcher: M, match_type: MatchType)
where
M: Matcher<T>,
{
if !matcher.matches(&self.value) {
panic!(
"Expected {:?} {} {}",
self.value,
match_type.description(),
matcher.description()
);
}
}
pub fn to<M>(mut self, matcher: M) -> Self
where
M: TypedMatcher<T>,
{
if TypedMatcher::<T>::matcher_type(&matcher) == MatchType::To {
self.assert(matcher, MatchType::To);
} else {
panic!("Matcher must be a 'to' matcher");
}
self
}
pub fn to_be<M>(mut self, matcher: M) -> Self
where
M: TypedMatcher<T>,
{
if TypedMatcher::<T>::matcher_type(&matcher) == MatchType::ToBe {
self.assert(matcher, MatchType::ToBe);
} else {
panic!("Matcher must be a 'to be' matcher");
}
self
}
pub fn to_have<M>(mut self, matcher: M) -> Self
where
M: TypedMatcher<T>,
{
if TypedMatcher::<T>::matcher_type(&matcher) == MatchType::ToHave {
self.assert(matcher, MatchType::ToHave);
} else {
panic!("Matcher must be a 'to have' matcher");
}
self
}
pub fn to_match<M>(mut self, matcher: M) -> Self
where
M: TypedMatcher<T>,
{
let matcher_type = TypedMatcher::<T>::matcher_type(&matcher);
self.assert(matcher, matcher_type);
self
}
}
pub fn expect<T: Debug>(value: T) -> Expect<T> {
Expect::new(value)
}
pub struct Verify<T> {
value: T,
}
impl<T> Verify<T>
where
T: Debug,
{
pub fn new(value: T) -> Self {
Verify { value }
}
pub fn assert<M>(&mut self, matcher: M) -> bool
where
M: Matcher<T>,
{
matcher.matches(&self.value)
}
}
pub fn verify<T: Debug>(value: T) -> Verify<T> {
Verify::new(value)
}
#[macro_export]
macro_rules! and {
($($matcher:expr),*) => {
$crate::matchers::and(vec![$(Box::new($matcher)),*])
};
}
#[macro_export]
macro_rules! or {
($($matcher:expr),*) => {
$crate::matchers::or(vec![$(Box::new($matcher)),*])
};
}