#![doc(hidden)]
#[macro_export]
#[doc(hidden)]
macro_rules! __any {
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal::AnyMatcher::new($crate::__matcher_list!($($matcher),*))
}}
}
#[doc(hidden)]
pub mod __internal {
use super::super::all_matcher::__internal::{
Components, descriptions, explanations, failure_explanations,
};
use crate::description::Description;
use crate::matcher::{Describable, Matcher, MatcherResult};
use crate::matchers::anything;
use core::fmt::Debug;
use core::marker::PhantomData;
#[doc(hidden)]
pub struct AnyMatcher<T: Debug + ?Sized, ComponentsT> {
components: ComponentsT,
phantom: PhantomData<fn(&T)>,
}
impl<T: Debug + ?Sized, ComponentsT> AnyMatcher<T, ComponentsT> {
pub fn new(components: ComponentsT) -> Self {
Self { components, phantom: PhantomData }
}
}
impl<T: Debug + ?Sized, ComponentsT: Components<T>> Matcher<T> for AnyMatcher<T, ComponentsT> {
fn matches(&self, actual: &T) -> MatcherResult {
let mut result = MatcherResult::NoMatch;
self.components.for_each(&mut |component| {
if component.matches(actual).is_match() {
result = MatcherResult::Match;
}
});
result
}
fn explain_match(&self, actual: &T) -> Description {
match self.components.count() {
0 => format!("which {}", anything().describe(MatcherResult::NoMatch)).into(),
1 => explanations(&self.components, actual).remove(0),
_ => {
let mut failures = failure_explanations(&self.components, actual);
if failures.len() == 1 {
failures.remove(0)
} else {
Description::new().collect(failures).bullet_list()
}
}
}
}
}
impl<T: Debug + ?Sized, ComponentsT: Components<T>> Describable for AnyMatcher<T, ComponentsT> {
fn describe(&self, matcher_result: MatcherResult) -> Description {
match self.components.count() {
0 => anything().describe(matcher_result),
1 => descriptions(&self.components, matcher_result).remove(0),
_ => {
let properties = descriptions(&self.components, matcher_result)
.into_iter()
.collect::<Description>()
.bullet_list()
.indent();
format!(
"{}:\n{properties}",
if matcher_result.into() {
"has at least one of the following properties"
} else {
"has none of the following properties"
}
)
.into()
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::__internal;
use crate::matcher::{Describable as _, Matcher, MatcherResult};
use crate::prelude::*;
use alloc::string::String;
use indoc::indoc;
#[test]
fn description_shows_more_than_one_matcher() -> TestResult<()> {
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
let matcher: __internal::AnyMatcher<String, _> = any!(first_matcher, second_matcher);
verify_that!(
matcher.describe(MatcherResult::Match),
displays_as(eq(indoc!(
"
has at least one of the following properties:
* starts with prefix \"A\"
* ends with suffix \"string\""
)))
)
}
#[test]
fn description_shows_one_matcher_directly() -> TestResult<()> {
let first_matcher = starts_with("A");
let matcher: __internal::AnyMatcher<String, _> = any!(first_matcher);
verify_that!(
matcher.describe(MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}
#[test]
fn mismatch_description_shows_which_matcher_failed_if_more_than_one_constituent()
-> TestResult<()> {
let first_matcher = starts_with("Another");
let second_matcher = ends_with("string");
let matcher: __internal::AnyMatcher<str, _> = any!(first_matcher, second_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
#[test]
fn mismatch_description_is_simple_when_only_one_constituent() -> TestResult<()> {
let first_matcher = starts_with("Another");
let matcher: __internal::AnyMatcher<str, _> = any!(first_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
}