#![doc(hidden)]
#[macro_export]
#[doc(hidden)]
macro_rules! __pointwise {
($matcher:expr, $container:expr) => {{
use $crate::matchers::__internal::PointwiseMatcher;
PointwiseMatcher::new($container.into_iter().map($matcher).collect())
}};
($matcher:expr, $left_container:expr, $right_container:expr) => {{
use $crate::matchers::__internal::PointwiseMatcher;
PointwiseMatcher::new(
$left_container
.into_iter()
.zip($right_container.into_iter())
.map(|(l, r)| $matcher(l, r))
.collect(),
)
}};
($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
use $crate::matchers::__internal::PointwiseMatcher;
PointwiseMatcher::new(
$left_container
.into_iter()
.zip($right_container.into_iter().zip($middle_container.into_iter()))
.map(|(l, (m, r))| $matcher(l, m, r))
.collect(),
)
}};
}
#[doc(hidden)]
pub mod __internal {
use crate::description::Description;
use crate::matcher::{Describable, Matcher, MatcherResult};
use crate::matcher_support::zipped_iterator::zip;
use crate::matchers::containers::{OwnedItems, RefItems};
use std::{fmt::Debug, marker::PhantomData};
#[doc(hidden)]
pub struct PointwiseMatcher<MatcherT, Mode> {
matchers: Vec<MatcherT>,
phantom: PhantomData<Mode>,
}
impl<MatcherT, Mode> PointwiseMatcher<MatcherT, Mode> {
pub fn new(matchers: Vec<MatcherT>) -> Self {
Self { matchers, phantom: Default::default() }
}
}
impl<T: Debug, MatcherT: Matcher<T>, ContainerT: ?Sized + Debug> Matcher<ContainerT>
for PointwiseMatcher<MatcherT, RefItems>
where
for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
{
fn matches(&self, actual: &ContainerT) -> MatcherResult {
let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
for (element, matcher) in zipped_iterator.by_ref() {
if matcher.matches(element).is_no_match() {
return MatcherResult::NoMatch;
}
}
if zipped_iterator.has_size_mismatch() {
MatcherResult::NoMatch
} else {
MatcherResult::Match
}
}
fn explain_match(&self, actual: &ContainerT) -> Description {
let actual_iterator = actual.into_iter();
let mut zipped_iterator = zip(actual_iterator, self.matchers.iter());
let mut mismatches = Vec::new();
for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
if e.matches(a).is_no_match() {
mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
}
}
if mismatches.is_empty() {
if !zipped_iterator.has_size_mismatch() {
"which matches all elements".into()
} else {
format!(
"which has size {} (expected {})",
zipped_iterator.left_size(),
self.matchers.len()
)
.into()
}
} else if mismatches.len() == 1 {
format!("where {}", mismatches[0]).into()
} else {
let mismatches = mismatches.into_iter().collect::<Description>();
format!("where:\n{}", mismatches.bullet_list().indent()).into()
}
}
}
impl<T: Debug, MatcherT: Matcher<T>, ContainerT: ?Sized + Debug> Matcher<ContainerT>
for PointwiseMatcher<MatcherT, OwnedItems>
where
for<'b> &'b ContainerT: IntoIterator<Item = T>,
{
fn matches(&self, actual: &ContainerT) -> MatcherResult {
let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
for (element, matcher) in zipped_iterator.by_ref() {
if matcher.matches(&element).is_no_match() {
return MatcherResult::NoMatch;
}
}
if zipped_iterator.has_size_mismatch() {
MatcherResult::NoMatch
} else {
MatcherResult::Match
}
}
fn explain_match(&self, actual: &ContainerT) -> Description {
let actual_iterator = actual.into_iter();
let mut zipped_iterator = zip(actual_iterator, self.matchers.iter());
let mut mismatches = Vec::new();
for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
if e.matches(&a).is_no_match() {
mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(&a)));
}
}
if mismatches.is_empty() {
if !zipped_iterator.has_size_mismatch() {
"which matches all elements".into()
} else {
format!(
"which has size {} (expected {})",
zipped_iterator.left_size(),
self.matchers.len()
)
.into()
}
} else if mismatches.len() == 1 {
format!("where {}", mismatches[0]).into()
} else {
let mismatches = mismatches.into_iter().collect::<Description>();
format!("where:\n{}", mismatches.bullet_list().indent()).into()
}
}
}
impl<MatcherT: Describable, Mode> Describable for PointwiseMatcher<MatcherT, Mode> {
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"{} elements satisfying respectively:\n{}",
if matcher_result.into() { "has" } else { "doesn't have" },
self.matchers
.iter()
.map(|m| m.describe(MatcherResult::Match))
.collect::<Description>()
.enumerate()
.indent()
)
.into()
}
}
}