#![doc(hidden)]
#[macro_export]
#[doc(hidden)]
macro_rules! __property {
($($t:tt)*) => { $crate::property_internal!($($t)*) }
}
#[doc(hidden)]
#[macro_export]
macro_rules! property_internal {
(
// The 3 cases of `$(& $($_amp:literal)?)?` -> `$(& $($_amp)*)*`
$(& $($_amp:literal)?)? $(:: $($_cs:literal)?)? $($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)?
.$method:tt $(::<$($m_ty_args:ty),* $(,)?>)? ($($argument:expr),* $(,)?),
ref $m:expr
) => {{
$crate::property_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
$crate::property_internal!(
@self_arg
struct_type: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::+ $( <$($t_ty_args),*>)*]
method_prefix: [ $(:: $($_cs)*)* $($t)::+ $(::<$($t_ty_args),*>)*]
[$method $(::<$($m_ty_args),*>)*] [$($argument),*] [$m])
}};
(
$(& $($_amp:literal)?)? $(:: $($_cs:literal)?)? $($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)?
.$method:tt $(::<$($m_ty_args:ty),* $(,)?>)? ($($argument:expr),* $(,)?),
$m:expr) => {{
$crate::property_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
$crate::property_internal!(
@self_dot
struct_type: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::+ $(<$($t_ty_args),*>)*]
[$method $(::<$($m_ty_args),*>)*] [$($argument),*] [$m])
}};
(@assert_empty) => {};
(@assert_empty $($l:literal)+) => {
compile_error!("property! argument must start with an optional `&` followed by a path")
};
(@self_arg struct_type: [$struct_ty:ty]
method_prefix: [$($method_prefix:tt)+]
[$($method:tt)*] [$($argument:expr),*] [$m:expr]) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
|o: $struct_ty| $($method_prefix)*::$($method)* (o, $($argument),*),
&stringify!($($method)* ($($argument),*)),
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}};
(@self_dot struct_type: [$struct_ty:ty]
[$($method:tt)*] [$($argument:expr),*] [$m:expr]) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
|o: &$struct_ty| o.$($method)* ($($argument),*),
&stringify!($($method)* ($($argument),*)),
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}};
}
#[doc(hidden)]
pub mod internal {
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
#[doc(hidden)]
pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>(
extractor: fn(&OuterT) -> InnerT,
property_desc: &'static str,
inner: MatcherT,
) -> PropertyMatcher<OuterT, InnerT, MatcherT> {
PropertyMatcher { extractor, property_desc, inner }
}
#[derive(MatcherBase)]
pub struct PropertyMatcher<OuterT, InnerT, MatcherT> {
extractor: fn(&OuterT) -> InnerT,
property_desc: &'static str,
inner: MatcherT,
}
impl<InnerT, OuterT, MatcherT> Matcher<OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
where
InnerT: Debug + Copy,
OuterT: Debug + Copy,
MatcherT: Matcher<InnerT>,
{
fn matches(&self, actual: OuterT) -> MatcherResult {
self.inner.matches((self.extractor)(&actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has property `{}`, which {}",
self.property_desc,
self.inner.describe(matcher_result)
)
.into()
}
fn explain_match(&self, actual: OuterT) -> Description {
let actual_inner = (self.extractor)(&actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
self.inner.explain_match(actual_inner)
)
.into()
}
}
impl<'a, InnerT, OuterT, MatcherT> Matcher<&'a OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
where
InnerT: Debug,
OuterT: Debug,
MatcherT: for<'b> Matcher<&'b InnerT>,
{
fn matches(&self, actual: &'a OuterT) -> MatcherResult {
self.inner.matches(&(self.extractor)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has property `{}`, which {}",
self.property_desc,
self.inner.describe(matcher_result)
)
.into()
}
fn explain_match(&self, actual: &'a OuterT) -> Description {
let actual_inner = (self.extractor)(actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
self.inner.explain_match(&actual_inner)
)
.into()
}
}
#[doc(hidden)]
pub fn property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>(
extractor: ExtractorT,
property_desc: &'static str,
inner: MatcherT,
) -> PropertyRefMatcher<ExtractorT, MatcherT>
where
OuterT: Debug,
InnerT: Debug,
MatcherT: for<'a> Matcher<&'a InnerT>,
ExtractorT: Fn(OuterT) -> InnerT,
{
PropertyRefMatcher { extractor, property_desc, inner }
}
#[derive(MatcherBase)]
pub struct PropertyRefMatcher<ExtractorT, MatcherT> {
extractor: ExtractorT,
property_desc: &'static str,
inner: MatcherT,
}
impl<
InnerT: Debug,
OuterT: Debug + Copy,
MatcherT: for<'a> Matcher<&'a InnerT>,
ExtractorT: Fn(OuterT) -> InnerT,
> Matcher<OuterT> for PropertyRefMatcher<ExtractorT, MatcherT>
{
fn matches(&self, actual: OuterT) -> MatcherResult {
self.inner.matches(&(self.extractor)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has property `{}`, which {}",
self.property_desc,
self.inner.describe(matcher_result)
)
.into()
}
fn explain_match(&self, actual: OuterT) -> Description {
let actual_inner = (self.extractor)(actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
self.inner.explain_match(&actual_inner)
)
.into()
}
}
}