diff --git a/README.md b/README.md index d442770a..a0e9b06e 100644 --- a/README.md +++ b/README.md @@ -156,9 +156,7 @@ struct MyEqMatcher { expected: T, } -impl Matcher for MyEqMatcher { - type ActualT = T; - +impl Matcher for MyEqMatcher { fn matches(&self, actual: &Self::ActualT) -> MatcherResult { (self.expected == *actual).into() } @@ -179,7 +177,7 @@ impl Matcher for MyEqMatcher { It is recommended to expose a function which constructs the matcher: ```rust -pub fn eq_my_way(expected: T) -> impl Matcher { +pub fn eq_my_way(expected: T) -> impl Matcher { MyEqMatcher { expected } } ``` diff --git a/googletest/crate_docs.md b/googletest/crate_docs.md index 8c8b47cf..7acde881 100644 --- a/googletest/crate_docs.md +++ b/googletest/crate_docs.md @@ -206,20 +206,19 @@ The following matchers are provided in GoogleTest Rust: One can extend the library by writing additional matchers. To do so, create a struct holding the matcher's data and have it implement the trait -[`Matcher`]: +[`Matcher`] (and optionally the [`MatcherExt`] trait): ```no_run -use googletest::{description::Description, matcher::{Matcher, MatcherResult}}; +use googletest::{description::Description, matcher::{Matcher, MatcherExt, MatcherResult}}; use std::fmt::Debug; +#[derive(MatcherExt)] struct MyEqMatcher { expected: T, } -impl Matcher for MyEqMatcher { - type ActualT = T; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { +impl<'a, T: PartialEq + Debug> Matcher<'a, T> for MyEqMatcher { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ if self.expected == *actual { MatcherResult::Match } else { @@ -243,17 +242,15 @@ impl Matcher for MyEqMatcher { It is recommended to expose a function which constructs the matcher: ```no_run - # use googletest::{description::Description, matcher::{Matcher, MatcherResult}}; + # use googletest::{description::Description, matcher::{Matcher, MatcherExt, MatcherResult}}; # use std::fmt::Debug; - # + # #[derive(MatcherExt)] # struct MyEqMatcher { # expected: T, # } # - # impl Matcher for MyEqMatcher { - # type ActualT = T; - # - # fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + # impl<'a, T: PartialEq + Debug> Matcher<'a, T> for MyEqMatcher { + # fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ # if self.expected == *actual { # MatcherResult::Match # } else { @@ -273,7 +270,7 @@ impl Matcher for MyEqMatcher { # } # } # - pub fn eq_my_way(expected: T) -> impl Matcher { + pub fn eq_my_way<'a, T: PartialEq + Debug>(expected: T) -> impl Matcher<'a, T> { MyEqMatcher { expected } } ``` @@ -282,17 +279,15 @@ impl Matcher for MyEqMatcher { ``` # use googletest::prelude::*; -# use googletest::{description::Description, matcher::{Matcher, MatcherResult}}; +# use googletest::{description::Description, matcher::{Matcher, MatcherExt, MatcherResult}}; # use std::fmt::Debug; -# +# #[derive(MatcherExt)] # struct MyEqMatcher { # expected: T, # } # -# impl Matcher for MyEqMatcher { -# type ActualT = T; -# -# fn matches(&self, actual: &Self::ActualT) -> MatcherResult { +# impl<'a, T: PartialEq + Debug> Matcher<'a, T> for MyEqMatcher { +# fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ # if self.expected == *actual { # MatcherResult::Match # } else { @@ -312,7 +307,7 @@ impl Matcher for MyEqMatcher { # } # } # -# pub fn eq_my_way(expected: T) -> impl Matcher { +# pub fn eq_my_way<'a, T: PartialEq + Debug>(expected: T) -> impl Matcher<'a, T> { # MyEqMatcher { expected } # } # /* The attribute macro would prevent the function from being compiled in a doctest. @@ -493,3 +488,4 @@ through the `?` operator as the example above shows. [`and_log_failure()`]: GoogleTestSupport::and_log_failure [`into_test_result()`]: IntoTestResult::into_test_result [`Matcher`]: matcher::Matcher +[`MatcherExt`]: matcher::MatcherExt diff --git a/googletest/src/assertions.rs b/googletest/src/assertions.rs index 26680281..877a3fe1 100644 --- a/googletest/src/assertions.rs +++ b/googletest/src/assertions.rs @@ -497,9 +497,9 @@ pub mod internal { /// /// **For internal use only. API stablility is not guaranteed!** #[must_use = "The assertion result must be evaluated to affect the test result."] - pub fn check_matcher( - actual: &T, - expected: impl Matcher, + pub fn check_matcher<'a, T: Debug + ?Sized>( + actual: &'a T, + expected: impl Matcher<'a, T>, actual_expr: &'static str, source_location: SourceLocation, ) -> Result<(), TestAssertionFailure> { diff --git a/googletest/src/lib.rs b/googletest/src/lib.rs index 51b63455..76ec4e39 100644 --- a/googletest/src/lib.rs +++ b/googletest/src/lib.rs @@ -42,7 +42,7 @@ pub mod matchers; /// } /// ``` pub mod prelude { - pub use super::matcher::Matcher; + pub use super::matcher::{Matcher, MatcherExt}; pub use super::matchers::*; pub use super::verify_current_test_outcome; pub use super::GoogleTestSupport; diff --git a/googletest/src/matcher.rs b/googletest/src/matcher.rs index 4edabbd5..f9ba7041 100644 --- a/googletest/src/matcher.rs +++ b/googletest/src/matcher.rs @@ -19,6 +19,7 @@ use crate::internal::source_location::SourceLocation; use crate::internal::test_outcome::TestAssertionFailure; use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher; use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher; +pub use googletest_macro::MatcherExt; use std::fmt::Debug; /// An interface for checking an arbitrary condition on a datum. @@ -26,17 +27,14 @@ use std::fmt::Debug; /// This trait is automatically implemented for a reference of any type /// implementing `Matcher`. This simplifies reusing a matcher in different /// assertions. -pub trait Matcher { - /// The type against which this matcher matches. - type ActualT: Debug + ?Sized; - +pub trait Matcher<'a, ActualT: Debug + ?Sized> { /// Returns whether the condition matches the datum `actual`. /// /// The trait implementation defines what it means to "match". Often the /// matching condition is based on data stored in the matcher. For example, /// `eq` matches when its stored expected value is equal (in the sense of /// the `==` operator) to the value `actual`. - fn matches(&self, actual: &Self::ActualT) -> MatcherResult; + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b; /// Returns a description of `self` or a negative description if /// `matcher_result` is `DoesNotMatch`. @@ -137,10 +135,22 @@ pub trait Matcher { /// .nested(self.expected.explain_match(actual.deref())) /// } /// ``` - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'a: 'b{ format!("which {}", self.describe(self.matches(actual))).into() } +} +/// Trait extension for matchers. It is highly recommended to implement it for +/// each type implementing `Matcher`. +// The `and` and `or` functions cannot be part of the `Matcher` traits since it +// is parametric. Consider that `and` and `or` are part of the `Matcher` trait +// and `MyMatcher` implements both `Matcher` and `Matcher`. +// Then `MyMatcher{...}.and(...)` can be either: +// * `Matcher::::and(MyMatcher{...}, ...)` or +// * `Matcher::::and(MyMatcher{...}, ...)`. +// Moving the `and` and `or` functions in a non-generic trait remove this +// confusion by making `and` and `or` unique for a given type. +pub trait MatcherExt { /// Constructs a matcher that matches both `self` and `right`. /// /// ``` @@ -164,10 +174,7 @@ pub trait Matcher { // TODO(b/264518763): Replace the return type with impl Matcher and reduce // visibility of ConjunctionMatcher once impl in return position in trait // methods is stable. - fn and>( - self, - right: Right, - ) -> ConjunctionMatcher + fn and(self, right: Right) -> ConjunctionMatcher where Self: Sized, { @@ -194,10 +201,7 @@ pub trait Matcher { // TODO(b/264518763): Replace the return type with impl Matcher and reduce // visibility of DisjunctionMatcher once impl in return position in trait // methods is stable. - fn or>( - self, - right: Right, - ) -> DisjunctionMatcher + fn or(self, right: Right) -> DisjunctionMatcher where Self: Sized, { @@ -214,9 +218,9 @@ const PRETTY_PRINT_LENGTH_THRESHOLD: usize = 60; /// /// The parameter `actual_expr` contains the expression which was evaluated to /// obtain `actual`. -pub(crate) fn create_assertion_failure( - matcher: &impl Matcher, - actual: &T, +pub(crate) fn create_assertion_failure<'a, T: Debug + ?Sized>( + matcher: &impl Matcher<'a, T>, + actual: &'a T, actual_expr: &'static str, source_location: SourceLocation, ) -> TestAssertionFailure { @@ -273,10 +277,10 @@ impl MatcherResult { } } -impl Matcher for &M { - type ActualT = M::ActualT; +impl MatcherExt for &M {} - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { +impl<'a, T: Debug + ?Sized, M: Matcher<'a, T>> Matcher<'a, T> for &M { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a : 'b{ (*self).matches(actual) } @@ -284,7 +288,7 @@ impl Matcher for &M { (*self).describe(matcher_result) } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description where 'a: 'b{ (*self).explain_match(actual) } } diff --git a/googletest/src/matchers/all_matcher.rs b/googletest/src/matchers/all_matcher.rs index 2557c3bc..79e0bc0a 100644 --- a/googletest/src/matchers/all_matcher.rs +++ b/googletest/src/matchers/all_matcher.rs @@ -38,7 +38,7 @@ /// ``` /// /// Using this macro is equivalent to using the -/// [`and`][crate::matcher::Matcher::and] method: +/// [`and`][crate::matcher::MatcherExt::and] method: /// /// ``` /// # use googletest::prelude::*; @@ -78,12 +78,12 @@ mod tests { #[test] fn description_shows_more_than_one_matcher() -> Result<()> { - let first_matcher: StrMatcher = starts_with("A"); + let first_matcher = starts_with("A"); let second_matcher = ends_with("string"); let matcher = all!(first_matcher, second_matcher); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq(indoc!( " has all the following properties: @@ -95,11 +95,11 @@ mod tests { #[test] fn description_shows_one_matcher_directly() -> Result<()> { - let first_matcher: StrMatcher = starts_with("A"); + let first_matcher = starts_with("A"); let matcher = all!(first_matcher); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("starts with prefix \"A\"")) ) } @@ -107,7 +107,7 @@ mod tests { #[test] fn mismatch_description_shows_which_matcher_failed_if_more_than_one_constituent() -> Result<()> { - let first_matcher: StrMatcher = starts_with("Another"); + let first_matcher = starts_with("Another"); let second_matcher = ends_with("string"); let matcher = all!(first_matcher, second_matcher); diff --git a/googletest/src/matchers/any_matcher.rs b/googletest/src/matchers/any_matcher.rs index f279c9e7..50f7eef4 100644 --- a/googletest/src/matchers/any_matcher.rs +++ b/googletest/src/matchers/any_matcher.rs @@ -40,7 +40,7 @@ /// ``` /// /// Using this macro is equivalent to using the -/// [`or`][crate::matcher::Matcher::or] method: +/// [`or`][crate::matcher::MatcherExt::or] method: /// /// ``` /// # use googletest::prelude::*; @@ -80,12 +80,12 @@ mod tests { #[test] fn description_shows_more_than_one_matcher() -> Result<()> { - let first_matcher: StrMatcher = starts_with("A"); + let first_matcher = starts_with("A"); let second_matcher = ends_with("string"); let matcher = any!(first_matcher, second_matcher); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq(indoc!( " has at least one of the following properties: @@ -97,11 +97,11 @@ mod tests { #[test] fn description_shows_one_matcher_directly() -> Result<()> { - let first_matcher: StrMatcher = starts_with("A"); + let first_matcher = starts_with("A"); let matcher = any!(first_matcher); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("starts with prefix \"A\"")) ) } diff --git a/googletest/src/matchers/anything_matcher.rs b/googletest/src/matchers/anything_matcher.rs index 36be478b..51a970e8 100644 --- a/googletest/src/matchers/anything_matcher.rs +++ b/googletest/src/matchers/anything_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches anything. This matcher always succeeds. /// @@ -32,16 +32,18 @@ use std::{fmt::Debug, marker::PhantomData}; /// # } /// # should_pass().unwrap(); /// ``` -pub fn anything() -> impl Matcher { - Anything::(Default::default()) +pub fn anything() -> Anything { + Anything } -struct Anything(PhantomData); +#[derive(MatcherExt)] +pub struct Anything; -impl Matcher for Anything { - type ActualT = T; - - fn matches(&self, _: &T) -> MatcherResult { +impl<'a, T: Debug + ?Sized> Matcher<'a, T> for Anything { + fn matches<'b>(&self, _: &'b T) -> MatcherResult + where + 'a: 'b, + { MatcherResult::Match } diff --git a/googletest/src/matchers/char_count_matcher.rs b/googletest/src/matchers/char_count_matcher.rs index 70977d74..b27f728c 100644 --- a/googletest/src/matchers/char_count_matcher.rs +++ b/googletest/src/matchers/char_count_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a string whose number of Unicode scalars matches `expected`. /// @@ -56,21 +56,22 @@ use std::{fmt::Debug, marker::PhantomData}; /// # } /// # should_pass().unwrap(); /// ``` -pub fn char_count, E: Matcher>( - expected: E, -) -> impl Matcher { - CharLenMatcher { expected, phantom: Default::default() } +pub fn char_count Matcher<'a, usize>>(expected: E) -> CharLenMatcher { + CharLenMatcher { expected } } -struct CharLenMatcher { +#[derive(MatcherExt)] +pub struct CharLenMatcher { expected: E, - phantom: PhantomData, } -impl, E: Matcher> Matcher for CharLenMatcher { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { +impl<'s, T: Debug + ?Sized + AsRef, E: for<'a> Matcher<'a, usize>> Matcher<'s, T> + for CharLenMatcher +{ + fn matches<'b>(&self, actual: &'b T) -> MatcherResult + where + 's: 'b, + { self.expected.matches(&actual.as_ref().chars().count()) } @@ -89,7 +90,10 @@ impl, E: Matcher> Matcher for Ch } } - fn explain_match(&self, actual: &T) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description + where + 's: 'b, + { let actual_size = actual.as_ref().chars().count(); format!( "which has character count {}, {}", @@ -108,7 +112,6 @@ mod tests { use crate::prelude::*; use indoc::indoc; use std::fmt::Debug; - use std::marker::PhantomData; #[test] fn char_count_matches_string_slice() -> Result<()> { @@ -130,11 +133,14 @@ mod tests { #[test] fn char_count_explains_match() -> Result<()> { - struct TestMatcher(PhantomData); - impl Matcher for TestMatcher { - type ActualT = T; - - fn matches(&self, _: &T) -> MatcherResult { + #[derive(MatcherExt)] + struct TestMatcher; + + impl<'a, T: Debug> Matcher<'a, T> for TestMatcher { + fn matches<'b>(&self, _: &'b T) -> MatcherResult + where + 'a: 'b, + { false.into() } @@ -142,12 +148,15 @@ mod tests { "called described".into() } - fn explain_match(&self, _: &T) -> Description { + fn explain_match<'b>(&self, _: &'b T) -> Description + where + 'a: 'b, + { "called explain_match".into() } } verify_that!( - char_count(TestMatcher(Default::default())).explain_match(&"A string"), + char_count(TestMatcher).explain_match(&"A string"), displays_as(eq("which has character count 8, called explain_match")) ) } diff --git a/googletest/src/matchers/conjunction_matcher.rs b/googletest/src/matchers/conjunction_matcher.rs index 0f3c606d..e0a1e2cf 100644 --- a/googletest/src/matchers/conjunction_matcher.rs +++ b/googletest/src/matchers/conjunction_matcher.rs @@ -17,7 +17,7 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; use std::fmt::Debug; @@ -41,6 +41,7 @@ use std::fmt::Debug; /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] +#[derive(MatcherExt)] pub struct ConjunctionMatcher { m1: M1, m2: M2, @@ -52,20 +53,23 @@ impl ConjunctionMatcher { } } -impl> Matcher for ConjunctionMatcher -where - M1::ActualT: Debug, +impl<'a, T: Debug + ?Sized, M1: Matcher<'a, T>, M2: Matcher<'a, T>> Matcher<'a, T> + for ConjunctionMatcher { - type ActualT = M1::ActualT; - - fn matches(&self, actual: &M1::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult + where + 'a: 'b, + { match (self.m1.matches(actual), self.m2.matches(actual)) { (MatcherResult::Match, MatcherResult::Match) => MatcherResult::Match, _ => MatcherResult::NoMatch, } } - fn explain_match(&self, actual: &M1::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description + where + 'a: 'b, + { match (self.m1.matches(actual), self.m2.matches(actual)) { (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual), (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual), diff --git a/googletest/src/matchers/container_eq_matcher.rs b/googletest/src/matchers/container_eq_matcher.rs index d4f872c7..934594db 100644 --- a/googletest/src/matchers/container_eq_matcher.rs +++ b/googletest/src/matchers/container_eq_matcher.rs @@ -13,9 +13,8 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use std::fmt::Debug; -use std::marker::PhantomData; /// Matches a container equal (in the sense of `==`) to `expected`. /// @@ -89,23 +88,22 @@ use std::marker::PhantomData; // ContainerEqMatcher has some specialisations for slice types (see // documentation above). Returning impl Matcher would hide those from the // compiler. -pub fn container_eq( +pub fn container_eq( expected: ExpectedContainerT, -) -> ContainerEqMatcher +) -> ContainerEqMatcher where - ActualContainerT: PartialEq + Debug + ?Sized, ExpectedContainerT: Debug, { - ContainerEqMatcher { expected, phantom: Default::default() } + ContainerEqMatcher { expected } } -pub struct ContainerEqMatcher { +#[derive(MatcherExt)] +pub struct ContainerEqMatcher { expected: ExpectedContainerT, - phantom: PhantomData, } -impl Matcher - for ContainerEqMatcher +impl<'z, ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT> + Matcher<'z, ActualContainerT> for ContainerEqMatcher where ActualElementT: PartialEq + Debug + ?Sized, ActualContainerT: PartialEq + Debug + ?Sized, @@ -114,13 +112,17 @@ where for<'a> &'a ActualContainerT: IntoIterator, for<'a> &'a ExpectedContainerT: IntoIterator, { - type ActualT = ActualContainerT; - - fn matches(&self, actual: &ActualContainerT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualContainerT) -> MatcherResult + where + 'z: 'b, + { (*actual == self.expected).into() } - fn explain_match(&self, actual: &ActualContainerT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualContainerT) -> Description + where + 'z: 'b, + { build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into() } @@ -132,19 +134,31 @@ where } } -impl - ContainerEqMatcher +impl ContainerEqMatcher where - ActualElementT: PartialEq + ?Sized, - ActualContainerT: PartialEq + ?Sized, - for<'a> &'a ActualContainerT: IntoIterator, for<'a> &'a ExpectedContainerT: IntoIterator, { - fn get_missing_items(&self, actual: &ActualContainerT) -> Vec<&ExpectedElementT> { + fn get_missing_items( + &self, + actual: &ActualContainerT, + ) -> Vec<&ExpectedElementT> + where + ActualElementT: PartialEq + ?Sized, + ActualContainerT: PartialEq + ?Sized, + for<'a> &'a ActualContainerT: IntoIterator, + { self.expected.into_iter().filter(|&i| !actual.into_iter().any(|j| j == i)).collect() } - fn get_unexpected_items<'a>(&self, actual: &'a ActualContainerT) -> Vec<&'a ActualElementT> { + fn get_unexpected_items<'b, ActualElementT, ActualContainerT>( + &self, + actual: &'b ActualContainerT, + ) -> Vec<&'b ActualElementT> + where + ActualElementT: PartialEq + ?Sized, + ActualContainerT: PartialEq + ?Sized, + for<'a> &'a ActualContainerT: IntoIterator, + { actual.into_iter().filter(|&i| !self.expected.into_iter().any(|j| i == j)).collect() } } diff --git a/googletest/src/matchers/contains_matcher.rs b/googletest/src/matchers/contains_matcher.rs index 1a27ce04..abb9243a 100644 --- a/googletest/src/matchers/contains_matcher.rs +++ b/googletest/src/matchers/contains_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches an iterable type whose elements contain a value matched by `inner`. /// @@ -43,19 +43,19 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_fail_1().unwrap_err(); /// # should_fail_2().unwrap_err(); /// ``` -pub fn contains(inner: InnerMatcherT) -> ContainsMatcher { - ContainsMatcher { inner, count: None, phantom: Default::default() } +pub fn contains(inner: InnerMatcherT) -> ContainsMatcher { + ContainsMatcher { inner, count: None } } /// A matcher which matches a container containing one or more elements a given /// inner [`Matcher`] matches. -pub struct ContainsMatcher { +#[derive(MatcherExt)] +pub struct ContainsMatcher { inner: InnerMatcherT, - count: Option>>, - phantom: PhantomData, + count: Option Matcher<'a, usize>>>, } -impl ContainsMatcher { +impl ContainsMatcher { /// Configures this instance to match containers which contain a number of /// matching items matched by `count`. /// @@ -68,7 +68,7 @@ impl ContainsMatcher { /// /// One can also use `times(eq(0))` to test for the *absence* of an item /// matching the expected value. - pub fn times(mut self, count: impl Matcher + 'static) -> Self { + pub fn times(mut self, count: impl for<'a> Matcher<'a, usize> + 'static) -> Self { self.count = Some(Box::new(count)); self } @@ -84,14 +84,12 @@ impl ContainsMatcher { // because val is dropped before matcher but the trait bound requires that // the argument to matches outlive the matcher. It works fine if one defines // val before matcher. -impl, ContainerT: Debug> Matcher - for ContainsMatcher +impl<'c, T: Debug + 'c, InnerMatcherT: Matcher<'c, T>, ContainerT: Debug + 'c> Matcher<'c, ContainerT> + for ContainsMatcher where for<'a> &'a ContainerT: IntoIterator, { - type ActualT = ContainerT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ContainerT) -> MatcherResult where 'c: 'b{ if let Some(count) = &self.count { count.matches(&self.count_matches(actual)) } else { @@ -104,7 +102,7 @@ where } } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ContainerT) -> Description where 'c: 'b{ let count = self.count_matches(actual); match (count, &self.count) { (_, Some(_)) => format!("which contains {} matching elements", count).into(), @@ -140,11 +138,11 @@ where } } -impl ContainsMatcher { - fn count_matches(&self, actual: &ContainerT) -> usize +impl ContainsMatcher { + fn count_matches<'a, 'b: 'a, T: Debug + 'a, ContainerT: 'a>(&self, actual: &'a ContainerT) -> usize where - for<'b> &'b ContainerT: IntoIterator, - InnerMatcherT: Matcher, + for<'c> &'c ContainerT: IntoIterator, + InnerMatcherT: Matcher<'b, T>, { let mut count = 0; for v in actual.into_iter() { @@ -158,7 +156,7 @@ impl ContainsMatcher { #[cfg(test)] mod tests { - use super::{contains, ContainsMatcher}; + use super::contains; use crate::matcher::{Matcher, MatcherResult}; use crate::prelude::*; @@ -200,9 +198,9 @@ mod tests { #[test] fn contains_does_not_match_empty_slice() -> Result<()> { - let matcher = contains(eq::(1)); + let matcher = contains(eq(1)); - let result = matcher.matches(&[]); + let result = matcher.matches(&[1; 0]); verify_that!(result, eq(MatcherResult::NoMatch)) } @@ -236,20 +234,20 @@ mod tests { #[test] fn contains_formats_without_multiplicity_by_default() -> Result<()> { - let matcher: ContainsMatcher, _> = contains(eq(1)); + let matcher = contains(eq(1)); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::>::describe(&matcher, MatcherResult::Match), displays_as(eq("contains at least one element which is equal to 1")) ) } #[test] fn contains_formats_with_multiplicity_when_specified() -> Result<()> { - let matcher: ContainsMatcher, _> = contains(eq(1)).times(eq(2)); + let matcher = contains(eq(1)).times(eq(2)); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::>::describe(&matcher, MatcherResult::Match), displays_as(eq("contains n elements which is equal to 1\n where n is equal to 2")) ) } diff --git a/googletest/src/matchers/contains_regex_matcher.rs b/googletest/src/matchers/contains_regex_matcher.rs index 6f681680..16ae9036 100644 --- a/googletest/src/matchers/contains_regex_matcher.rs +++ b/googletest/src/matchers/contains_regex_matcher.rs @@ -13,10 +13,9 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use regex::Regex; use std::fmt::Debug; -use std::marker::PhantomData; use std::ops::Deref; /// Matches a string containing a substring which matches the given regular @@ -52,13 +51,8 @@ use std::ops::Deref; // compiler treats it as a Matcher only and the code // verify_that!("Some value".to_string(), contains_regex(".*value"))?; // doesn't compile. -pub fn contains_regex>( - pattern: PatternT, -) -> ContainsRegexMatcher { - ContainsRegexMatcher { - regex: Regex::new(pattern.deref()).unwrap(), - phantom: Default::default(), - } +pub fn contains_regex>(pattern: PatternT) -> ContainsRegexMatcher { + ContainsRegexMatcher { regex: Regex::new(pattern.deref()).unwrap() } } /// A matcher matching a string-like type containing a substring matching a @@ -66,15 +60,13 @@ pub fn contains_regex>( /// /// Intended only to be used from the function [`contains_regex`] only. /// Should not be referenced by code outside this library. -pub struct ContainsRegexMatcher { +#[derive(MatcherExt)] +pub struct ContainsRegexMatcher { regex: Regex, - phantom: PhantomData, } -impl + Debug + ?Sized> Matcher for ContainsRegexMatcher { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { +impl<'a, ActualT: AsRef + Debug + ?Sized> Matcher<'a, ActualT> for ContainsRegexMatcher { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ self.regex.is_match(actual.as_ref()).into() } @@ -92,7 +84,7 @@ impl + Debug + ?Sized> Matcher for ContainsRegexMatcher Result<()> { - let matcher: ContainsRegexMatcher<&str> = contains_regex("\n"); + let matcher = contains_regex("\n"); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("contains the regular expression \"\\n\"")) ) } diff --git a/googletest/src/matchers/disjunction_matcher.rs b/googletest/src/matchers/disjunction_matcher.rs index 5e0f130d..951b41c4 100644 --- a/googletest/src/matchers/disjunction_matcher.rs +++ b/googletest/src/matchers/disjunction_matcher.rs @@ -17,7 +17,7 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; use std::fmt::Debug; @@ -35,6 +35,7 @@ use std::fmt::Debug; /// ``` /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] +#[derive(MatcherExt)] pub struct DisjunctionMatcher { m1: M1, m2: M2, @@ -46,20 +47,23 @@ impl DisjunctionMatcher { } } -impl> Matcher for DisjunctionMatcher -where - M1::ActualT: Debug, +impl<'a, T: Debug + ?Sized, M1: Matcher<'a, T>, M2: Matcher<'a, T>> Matcher<'a, T> + for DisjunctionMatcher { - type ActualT = M1::ActualT; - - fn matches(&self, actual: &M1::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult + where + 'a: 'b, + { match (self.m1.matches(actual), self.m2.matches(actual)) { (MatcherResult::NoMatch, MatcherResult::NoMatch) => MatcherResult::NoMatch, _ => MatcherResult::Match, } } - fn explain_match(&self, actual: &M1::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description + where + 'a: 'b, + { match (self.m1.matches(actual), self.m2.matches(actual)) { (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual), (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual), diff --git a/googletest/src/matchers/display_matcher.rs b/googletest/src/matchers/display_matcher.rs index afc22a47..87268e0e 100644 --- a/googletest/src/matchers/display_matcher.rs +++ b/googletest/src/matchers/display_matcher.rs @@ -13,9 +13,8 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use std::fmt::{Debug, Display}; -use std::marker::PhantomData; /// Matches the string representation of types that implement `Display`. /// @@ -23,27 +22,25 @@ use std::marker::PhantomData; /// let result: impl Display = ...; /// verify_that!(result, displays_as(eq(format!("{}", result))))?; /// ``` -pub fn displays_as>( +pub fn displays_as<'a, InnerMatcher: Matcher<'a, String>>( inner: InnerMatcher, -) -> impl Matcher { - DisplayMatcher:: { inner, phantom: Default::default() } +) -> DisplayMatcher { + DisplayMatcher { inner } } -struct DisplayMatcher { +#[derive(MatcherExt)] +pub struct DisplayMatcher { inner: InnerMatcher, - phantom: PhantomData, } -impl> Matcher - for DisplayMatcher +impl<'a, 's, T: Debug + Display, InnerMatcher: Matcher<'s, String>> Matcher<'a, T> + for DisplayMatcher { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ self.inner.matches(&format!("{actual}")) } - fn explain_match(&self, actual: &T) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description where 'a: 'b{ format!( "which displays as {:?} {}", actual.to_string(), diff --git a/googletest/src/matchers/each_matcher.rs b/googletest/src/matchers/each_matcher.rs index 08a07426..4ab16779 100644 --- a/googletest/src/matchers/each_matcher.rs +++ b/googletest/src/matchers/each_matcher.rs @@ -13,8 +13,8 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; -use std::{fmt::Debug, marker::PhantomData}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; +use std::fmt::Debug; /// Matches a container all of whose elements are matched by the matcher /// `inner`. @@ -61,29 +61,25 @@ use std::{fmt::Debug, marker::PhantomData}; /// # } /// # should_pass().unwrap(); /// ``` -pub fn each( - inner: MatcherT, -) -> impl Matcher -where - for<'a> &'a ActualT: IntoIterator, - MatcherT: Matcher, -{ - EachMatcher { inner, phantom: Default::default() } +pub fn each(inner: MatcherT) -> EachMatcher { + EachMatcher { inner } } -struct EachMatcher { +#[derive(MatcherExt)] +pub struct EachMatcher { inner: MatcherT, - phantom: PhantomData, } -impl Matcher for EachMatcher +impl<'c, ElementT: Debug, ActualT: Debug + ?Sized, MatcherT> Matcher<'c, ActualT> + for EachMatcher where for<'a> &'a ActualT: IntoIterator, - MatcherT: Matcher, + MatcherT: Matcher<'c, ElementT>, { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult + where + 'c: 'b, + { for element in actual { if self.inner.matches(element).is_no_match() { return MatcherResult::NoMatch; @@ -92,7 +88,10 @@ where MatcherResult::Match } - fn explain_match(&self, actual: &ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description + where + 'c: 'b, + { let mut non_matching_elements = Vec::new(); for (index, element) in actual.into_iter().enumerate() { if self.inner.matches(element).is_no_match() { diff --git a/googletest/src/matchers/elements_are_matcher.rs b/googletest/src/matchers/elements_are_matcher.rs index 3a4b5b24..9b1905ff 100644 --- a/googletest/src/matchers/elements_are_matcher.rs +++ b/googletest/src/matchers/elements_are_matcher.rs @@ -93,36 +93,34 @@ macro_rules! __elements_are { #[doc(hidden)] pub mod internal { use crate::description::Description; - use crate::matcher::{Matcher, MatcherResult}; + use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use crate::matcher_support::zipped_iterator::zip; - use std::{fmt::Debug, marker::PhantomData}; + use std::fmt::Debug; /// This struct is meant to be used only by the macro `elements_are!`. /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub struct ElementsAre<'a, ContainerT: ?Sized, T: Debug> { - elements: Vec + 'a>>, - phantom: PhantomData, + #[derive(MatcherExt)] + pub struct ElementsAre<'a, 'b, T: Debug> { + elements: Vec + 'a>>, } - impl<'a, ContainerT: ?Sized, T: Debug> ElementsAre<'a, ContainerT, T> { + impl<'a, 'b, T: Debug> ElementsAre<'a, 'b, T> { /// Factory only intended for use in the macro `elements_are!`. /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub fn new(elements: Vec + 'a>>) -> Self { - Self { elements, phantom: Default::default() } + pub fn new(elements: Vec + 'a>>) -> Self { + Self { elements } } } - impl<'a, T: Debug, ContainerT: Debug + ?Sized> Matcher for ElementsAre<'a, ContainerT, T> + impl<'a, 'b, T: Debug + 'b, ContainerT: Debug + ?Sized> Matcher<'b, ContainerT> for ElementsAre<'a, 'b, T> where - for<'b> &'b ContainerT: IntoIterator, + for<'c> &'c ContainerT: IntoIterator, { - type ActualT = ContainerT; - - fn matches(&self, actual: &ContainerT) -> MatcherResult { + fn matches<'c>(&self, actual: &'c ContainerT) -> MatcherResult where 'b: 'c { let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter()); for (a, e) in zipped_iterator.by_ref() { if e.matches(a).is_no_match() { @@ -136,7 +134,7 @@ pub mod internal { } } - fn explain_match(&self, actual: &ContainerT) -> Description { + fn explain_match<'c>(&self, actual: &'c ContainerT) -> Description where 'b: 'c{ let actual_iterator = actual.into_iter(); let mut zipped_iterator = zip(actual_iterator, self.elements.iter()); let mut mismatches = Vec::new(); diff --git a/googletest/src/matchers/empty_matcher.rs b/googletest/src/matchers/empty_matcher.rs index 11cb6758..e22f0b70 100644 --- a/googletest/src/matchers/empty_matcher.rs +++ b/googletest/src/matchers/empty_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches an empty container. /// @@ -50,24 +50,18 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_pass().unwrap(); /// ``` -pub fn empty() -> impl Matcher -where - for<'a> &'a T: IntoIterator, -{ - EmptyMatcher { phantom: Default::default() } +pub fn empty() -> EmptyMatcher { + EmptyMatcher } -struct EmptyMatcher { - phantom: PhantomData, -} +#[derive(MatcherExt)] +pub struct EmptyMatcher; -impl Matcher for EmptyMatcher +impl<'c, T: Debug + ?Sized> Matcher<'c, T> for EmptyMatcher where for<'a> &'a T: IntoIterator, { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'c: 'b { actual.into_iter().next().is_none().into() } diff --git a/googletest/src/matchers/eq_deref_of_matcher.rs b/googletest/src/matchers/eq_deref_of_matcher.rs index 3a6cfced..323c1778 100644 --- a/googletest/src/matchers/eq_deref_of_matcher.rs +++ b/googletest/src/matchers/eq_deref_of_matcher.rs @@ -14,10 +14,10 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, matcher_support::{edit_distance, summarize_diff::create_diff}, }; -use std::{fmt::Debug, marker::PhantomData, ops::Deref}; +use std::{fmt::Debug, ops::Deref}; /// Matches a value equal (in the sense of `==`) to the dereferenced value of /// `expected`. @@ -51,29 +51,25 @@ use std::{fmt::Debug, marker::PhantomData, ops::Deref}; /// ``` /// /// Otherwise, this has the same behaviour as [`eq`][crate::matchers::eq]. -pub fn eq_deref_of( - expected: ExpectedRefT, -) -> EqDerefOfMatcher { - EqDerefOfMatcher { expected, phantom: Default::default() } +pub fn eq_deref_of(expected: ExpectedRefT) -> EqDerefOfMatcher { + EqDerefOfMatcher { expected } } /// A matcher which matches a value equal to the derefenced value of `expected`. /// /// See [`eq_deref_of`]. -pub struct EqDerefOfMatcher { +#[derive(MatcherExt)] +pub struct EqDerefOfMatcher { pub(crate) expected: ExpectedRefT, - phantom: PhantomData, } -impl Matcher for EqDerefOfMatcher +impl<'a, ActualT, ExpectedRefT, ExpectedT> Matcher<'a, ActualT> for EqDerefOfMatcher where ActualT: Debug + ?Sized, ExpectedRefT: Deref + Debug, ExpectedT: PartialEq + Debug, { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ (self.expected.deref() == actual).into() } @@ -84,7 +80,7 @@ where } } - fn explain_match(&self, actual: &ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'a: 'b{ format!( "which {}{}", &self.describe(self.matches(actual)), diff --git a/googletest/src/matchers/eq_matcher.rs b/googletest/src/matchers/eq_matcher.rs index 26fea831..a42083eb 100644 --- a/googletest/src/matchers/eq_matcher.rs +++ b/googletest/src/matchers/eq_matcher.rs @@ -13,11 +13,11 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use crate::matcher_support::edit_distance; use crate::matcher_support::summarize_diff::create_diff; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a value equal (in the sense of `==`) to `expected`. /// @@ -71,22 +71,20 @@ use std::{fmt::Debug, marker::PhantomData}; /// options on how equality is checked through the /// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator] /// extension trait, which is implemented for this matcher. -pub fn eq(expected: T) -> EqMatcher { - EqMatcher { expected, phantom: Default::default() } +pub fn eq(expected: T) -> EqMatcher { + EqMatcher { expected } } /// A matcher which matches a value equal to `expected`. /// /// See [`eq`]. -pub struct EqMatcher { +#[derive(MatcherExt)] +pub struct EqMatcher { pub(crate) expected: T, - phantom: PhantomData, } -impl> Matcher for EqMatcher { - type ActualT = A; - - fn matches(&self, actual: &A) -> MatcherResult { +impl<'a, T: Debug, A: Debug + ?Sized + PartialEq> Matcher<'a, A> for EqMatcher { + fn matches<'b>(&self, actual: &'b A) -> MatcherResult where 'a: 'b{ (*actual == self.expected).into() } @@ -97,10 +95,10 @@ impl> Matcher for EqMatcher { } } - fn explain_match(&self, actual: &A) -> Description { + fn explain_match<'b>(&self, actual: &'b A) -> Description where 'a: 'b { let expected_debug = format!("{:#?}", self.expected); let actual_debug = format!("{:#?}", actual); - let description = self.describe(self.matches(actual)); + let description = Matcher::<'a, A>::describe(self, Matcher::<'a, A>::matches(self, actual)); let diff = if is_multiline_string_debug(&actual_debug) && is_multiline_string_debug(&expected_debug) diff --git a/googletest/src/matchers/err_matcher.rs b/googletest/src/matchers/err_matcher.rs index 3b10de4f..ddfd24b8 100644 --- a/googletest/src/matchers/err_matcher.rs +++ b/googletest/src/matchers/err_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a `Result` containing `Err` with a value matched by `inner`. /// @@ -38,28 +38,23 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_fail_1().unwrap_err(); /// # should_fail_2().unwrap_err(); /// ``` -pub fn err( - inner: impl Matcher, -) -> impl Matcher> { - ErrMatcher:: { inner, phantom_t: Default::default(), phantom_e: Default::default() } +pub fn err(inner: Inner) -> ErrMatcher { + ErrMatcher { inner } } -struct ErrMatcher { +#[derive(MatcherExt)] +pub struct ErrMatcher { inner: InnerMatcherT, - phantom_t: PhantomData, - phantom_e: PhantomData, } -impl> Matcher - for ErrMatcher +impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<'a, E>> Matcher<'a, std::result::Result> + for ErrMatcher { - type ActualT = std::result::Result; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b std::result::Result) -> MatcherResult where 'a: 'b{ actual.as_ref().err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch) } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b std::result::Result) -> Description where 'a: 'b{ match actual { Err(e) => { Description::new().text("which is an error").nested(self.inner.explain_match(e)) @@ -139,13 +134,9 @@ mod tests { #[test] fn err_describe_matches() -> Result<()> { - let matcher = super::ErrMatcher:: { - inner: eq(1), - phantom_t: Default::default(), - phantom_e: Default::default(), - }; + let matcher = err(eq(1)); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::>::describe(&matcher, MatcherResult::Match), displays_as(eq("is an error which is equal to 1")) ) } diff --git a/googletest/src/matchers/field_matcher.rs b/googletest/src/matchers/field_matcher.rs index fc37ce66..796cf902 100644 --- a/googletest/src/matchers/field_matcher.rs +++ b/googletest/src/matchers/field_matcher.rs @@ -143,7 +143,7 @@ macro_rules! field_internal { pub mod internal { use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; use std::fmt::Debug; @@ -152,26 +152,29 @@ pub mod internal { /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub fn field_matcher>( + pub fn field_matcher<'a, OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<'a, InnerT>>( field_accessor: fn(&OuterT) -> Option<&InnerT>, field_path: &'static str, inner: InnerMatcher, - ) -> impl Matcher { + ) -> FieldMatcher { FieldMatcher { field_accessor, field_path, inner } } - struct FieldMatcher { + pub struct FieldMatcher { field_accessor: fn(&OuterT) -> Option<&InnerT>, field_path: &'static str, inner: InnerMatcher, } - impl> Matcher + impl<'a, OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<'a, InnerT>> MatcherExt for FieldMatcher { - type ActualT = OuterT; + } - fn matches(&self, actual: &OuterT) -> MatcherResult { + impl<'a, OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<'a, InnerT>> Matcher<'a, OuterT> + for FieldMatcher + { + fn matches<'b>(&self, actual: &'b OuterT) -> MatcherResult where 'a: 'b { if let Some(value) = (self.field_accessor)(actual) { self.inner.matches(value) } else { @@ -179,7 +182,7 @@ pub mod internal { } } - fn explain_match(&self, actual: &OuterT) -> Description { + fn explain_match<'b>(&self, actual: &'b OuterT) -> Description where 'a: 'b{ if let Some(actual) = (self.field_accessor)(actual) { format!( "which has field `{}`, {}", diff --git a/googletest/src/matchers/ge_matcher.rs b/googletest/src/matchers/ge_matcher.rs index cb2a91ff..acb37bc8 100644 --- a/googletest/src/matchers/ge_matcher.rs +++ b/googletest/src/matchers/ge_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a value greater than or equal to (in the sense of `>=`) `expected`. /// @@ -74,23 +74,19 @@ use std::{fmt::Debug, marker::PhantomData}; /// /// You can find the standard library `PartialOrd` implementation in /// -pub fn ge, ExpectedT: Debug>( - expected: ExpectedT, -) -> impl Matcher { - GeMatcher:: { expected, phantom: Default::default() } +pub fn ge(expected: ExpectedT) -> GeMatcher { + GeMatcher { expected } } -struct GeMatcher { +#[derive(MatcherExt)] +pub struct GeMatcher { expected: ExpectedT, - phantom: PhantomData, } -impl, ExpectedT: Debug> Matcher - for GeMatcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> + for GeMatcher { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ (*actual >= self.expected).into() } diff --git a/googletest/src/matchers/gt_matcher.rs b/googletest/src/matchers/gt_matcher.rs index b52c6e3d..dbffffbe 100644 --- a/googletest/src/matchers/gt_matcher.rs +++ b/googletest/src/matchers/gt_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a value greater (in the sense of `>`) than `expected`. /// @@ -74,23 +74,19 @@ use std::{fmt::Debug, marker::PhantomData}; /// /// You can find the standard library `PartialOrd` implementation in /// -pub fn gt, ExpectedT: Debug>( - expected: ExpectedT, -) -> impl Matcher { - GtMatcher:: { expected, phantom: Default::default() } +pub fn gt(expected: ExpectedT) -> GtMatcher { + GtMatcher { expected } } -struct GtMatcher { +#[derive(MatcherExt)] +pub struct GtMatcher { expected: ExpectedT, - phantom: PhantomData, } -impl, ExpectedT: Debug> Matcher - for GtMatcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> + for GtMatcher { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b> (&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ (*actual > self.expected).into() } @@ -181,7 +177,7 @@ mod tests { #[test] fn gt_describe_matches() -> Result<()> { verify_that!( - gt::(232).describe(MatcherResult::Match), + Matcher::::describe(>(232), MatcherResult::Match), displays_as(eq("is greater than 232")) ) } @@ -189,7 +185,7 @@ mod tests { #[test] fn gt_describe_does_not_match() -> Result<()> { verify_that!( - gt::(232).describe(MatcherResult::NoMatch), + Matcher::::describe(>(232), MatcherResult::NoMatch), displays_as(eq("is less than or equal to 232")) ) } diff --git a/googletest/src/matchers/has_entry_matcher.rs b/googletest/src/matchers/has_entry_matcher.rs index 7f9d2add..bfd2f03c 100644 --- a/googletest/src/matchers/has_entry_matcher.rs +++ b/googletest/src/matchers/has_entry_matcher.rs @@ -13,11 +13,10 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use std::collections::HashMap; use std::fmt::Debug; use std::hash::Hash; -use std::marker::PhantomData; /// Matches a HashMap containing the given `key` whose value is matched by the /// matcher `inner`. @@ -62,25 +61,23 @@ use std::marker::PhantomData; /// However, `has_entry` will offer somewhat better diagnostic messages in the /// case of assertion failure. And it avoid the extra allocation hidden in the /// code above. -pub fn has_entry>( - key: KeyT, - inner: MatcherT, -) -> impl Matcher> { - HasEntryMatcher { key, inner, phantom: Default::default() } +pub fn has_entry(key: KeyT, inner: MatcherT) -> HasEntryMatcher { + HasEntryMatcher { key, inner } } -struct HasEntryMatcher { +#[derive(MatcherExt)] +pub struct HasEntryMatcher { key: KeyT, inner: MatcherT, - phantom: PhantomData, } -impl> Matcher - for HasEntryMatcher +impl<'a, KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<'a, ValueT>> + Matcher<'a, HashMap> for HasEntryMatcher { - type ActualT = HashMap; - - fn matches(&self, actual: &HashMap) -> MatcherResult { + fn matches<'b>(&self, actual: &'b HashMap) -> MatcherResult + where + 'a: 'b, + { if let Some(value) = actual.get(&self.key) { self.inner.matches(value) } else { @@ -88,7 +85,10 @@ impl } } - fn explain_match(&self, actual: &HashMap) -> Description { + fn explain_match<'b>(&self, actual: &'b HashMap) -> Description + where + 'a: 'b, + { if let Some(value) = actual.get(&self.key) { format!( "which contains key {:?}, but is mapped to value {:#?}, {}", diff --git a/googletest/src/matchers/is_encoded_string_matcher.rs b/googletest/src/matchers/is_encoded_string_matcher.rs index d2fb2596..4d256429 100644 --- a/googletest/src/matchers/is_encoded_string_matcher.rs +++ b/googletest/src/matchers/is_encoded_string_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a byte sequence which is a UTF-8 encoded string matched by `inner`. /// @@ -48,28 +48,24 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_fail_1().unwrap_err(); /// # should_fail_2().unwrap_err(); /// ``` -pub fn is_utf8_string<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT>( - inner: InnerMatcherT, -) -> impl Matcher +pub fn is_utf8_string(inner: InnerMatcherT) -> IsEncodedStringMatcher where - InnerMatcherT: Matcher, + InnerMatcherT: for<'a> Matcher<'a, String>, { - IsEncodedStringMatcher { inner, phantom: Default::default() } + IsEncodedStringMatcher { inner } } -struct IsEncodedStringMatcher { +#[derive(MatcherExt)] +pub struct IsEncodedStringMatcher { inner: InnerMatcherT, - phantom: PhantomData, } -impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher - for IsEncodedStringMatcher +impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher<'a, ActualT> + for IsEncodedStringMatcher where - InnerMatcherT: Matcher, + InnerMatcherT: for<'s> Matcher<'s, String>, { - type ActualT = ActualT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ String::from_utf8(actual.as_ref().to_vec()) .map(|s| self.inner.matches(&s)) .unwrap_or(MatcherResult::NoMatch) @@ -90,7 +86,7 @@ where } } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'a: 'b{ match String::from_utf8(actual.as_ref().to_vec()) { Ok(s) => { format!("which is a UTF-8 encoded string {}", self.inner.explain_match(&s)).into() @@ -132,20 +128,20 @@ mod tests { #[test] fn has_correct_description_in_matched_case() -> Result<()> { - let matcher = is_utf8_string::<&[u8], _>(eq("A string")); + let matcher = is_utf8_string(eq("A string")); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::<&[u8]>::describe(&matcher, MatcherResult::Match), displays_as(eq("is a UTF-8 encoded string which is equal to \"A string\"")) ) } #[test] fn has_correct_description_in_not_matched_case() -> Result<()> { - let matcher = is_utf8_string::<&[u8], _>(eq("A string")); + let matcher = is_utf8_string(eq("A string")); verify_that!( - matcher.describe(MatcherResult::NoMatch), + Matcher::<&[u8]>::describe(&matcher, MatcherResult::NoMatch), displays_as(eq("is not a UTF-8 encoded string which is equal to \"A string\"")) ) } diff --git a/googletest/src/matchers/is_matcher.rs b/googletest/src/matchers/is_matcher.rs index 336ce536..26d29c5d 100644 --- a/googletest/src/matchers/is_matcher.rs +++ b/googletest/src/matchers/is_matcher.rs @@ -16,34 +16,32 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches precisely values matched by `inner`. /// /// The returned matcher produces a description prefixed by the string /// `description`. This is useful in contexts where the test assertion failure /// output must include the additional description. -pub fn is<'a, ActualT: Debug + 'a, InnerMatcherT: Matcher + 'a>( - description: &'a str, - inner: InnerMatcherT, -) -> impl Matcher + 'a { - IsMatcher { description, inner, phantom: Default::default() } +pub fn is(description: &str, inner: InnerMatcherT) -> IsMatcher<'_, InnerMatcherT> { + IsMatcher { description, inner } } -struct IsMatcher<'a, ActualT, InnerMatcherT> { +#[derive(MatcherExt)] +pub struct IsMatcher<'a, InnerMatcherT> { description: &'a str, inner: InnerMatcherT, - phantom: PhantomData, } -impl<'a, ActualT: Debug, InnerMatcherT: Matcher> Matcher - for IsMatcher<'a, ActualT, InnerMatcherT> +impl<'a, 'b, ActualT: Debug, InnerMatcherT: Matcher<'b, ActualT>> Matcher<'b, ActualT> + for IsMatcher<'a, InnerMatcherT> { - type ActualT = ActualT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches<'c>(&self, actual: &'c ActualT) -> MatcherResult + where + 'b: 'c, + { self.inner.matches(actual) } @@ -64,7 +62,10 @@ impl<'a, ActualT: Debug, InnerMatcherT: Matcher> Matcher } } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'c>(&self, actual: &'c ActualT) -> Description + where + 'b: 'c, + { self.inner.explain_match(actual) } } diff --git a/googletest/src/matchers/is_nan_matcher.rs b/googletest/src/matchers/is_nan_matcher.rs index 0af4338d..c4bd6971 100644 --- a/googletest/src/matchers/is_nan_matcher.rs +++ b/googletest/src/matchers/is_nan_matcher.rs @@ -14,22 +14,21 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; use num_traits::float::Float; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a floating point value which is NaN. -pub fn is_nan() -> impl Matcher { - IsNanMatcher::(Default::default()) +pub fn is_nan() -> IsNanMatcher { + IsNanMatcher } -struct IsNanMatcher(PhantomData); +#[derive(MatcherExt)] +pub struct IsNanMatcher; -impl Matcher for IsNanMatcher { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { +impl<'a, T: Float + Debug> Matcher<'a, T> for IsNanMatcher { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ actual.is_nan().into() } diff --git a/googletest/src/matchers/le_matcher.rs b/googletest/src/matchers/le_matcher.rs index cfc57818..7596ce75 100644 --- a/googletest/src/matchers/le_matcher.rs +++ b/googletest/src/matchers/le_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a value less than or equal to (in the sense of `<=`) `expected`. /// @@ -74,23 +74,22 @@ use std::{fmt::Debug, marker::PhantomData}; /// /// You can find the standard library `PartialOrd` implementation in /// -pub fn le, ExpectedT: Debug>( - expected: ExpectedT, -) -> impl Matcher { - LeMatcher:: { expected, phantom: Default::default() } +pub fn le(expected: ExpectedT) -> LeMatcher { + LeMatcher { expected } } -struct LeMatcher { +#[derive(MatcherExt)] +pub struct LeMatcher { expected: ExpectedT, - phantom: PhantomData, } -impl, ExpectedT: Debug> Matcher - for LeMatcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> + for LeMatcher { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult + where + 'a: 'b, + { (*actual <= self.expected).into() } diff --git a/googletest/src/matchers/len_matcher.rs b/googletest/src/matchers/len_matcher.rs index be903c99..fb9ac127 100644 --- a/googletest/src/matchers/len_matcher.rs +++ b/googletest/src/matchers/len_matcher.rs @@ -13,9 +13,9 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use crate::matcher_support::count_elements::count_elements; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a container whose number of elements matches `expected`. /// @@ -49,25 +49,23 @@ use std::{fmt::Debug, marker::PhantomData}; /// # } /// # should_pass().unwrap(); /// ``` -pub fn len>(expected: E) -> impl Matcher -where - for<'a> &'a T: IntoIterator, -{ - LenMatcher { expected, phantom: Default::default() } +pub fn len(expected: E) -> LenMatcher { + LenMatcher { expected } } -struct LenMatcher { +#[derive(MatcherExt)] +pub struct LenMatcher { expected: E, - phantom: PhantomData, } -impl> Matcher for LenMatcher +impl<'c, T: Debug + ?Sized, E: for<'b> Matcher<'b, usize>> Matcher<'c, T> for LenMatcher where for<'a> &'a T: IntoIterator, { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult + where + 'c: 'b, + { self.expected.matches(&count_elements(actual)) } @@ -83,7 +81,10 @@ where } } - fn explain_match(&self, actual: &T) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description + where + 'c: 'b, + { let actual_size = count_elements(actual); format!("which has length {}, {}", actual_size, self.expected.explain_match(&actual_size)) .into() @@ -101,7 +102,6 @@ mod tests { BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque, }; use std::fmt::Debug; - use std::marker::PhantomData; #[test] fn len_matcher_match_vec() -> Result<()> { @@ -178,11 +178,10 @@ mod tests { #[test] fn len_matcher_explain_match() -> Result<()> { - struct TestMatcher(PhantomData); - impl Matcher for TestMatcher { - type ActualT = T; - - fn matches(&self, _: &T) -> MatcherResult { + #[derive(MatcherExt)] + struct TestMatcher; + impl<'a, T: Debug> Matcher<'a, T> for TestMatcher { + fn matches<'b>(&self, _: &'b T) -> MatcherResult where 'a: 'b{ false.into() } @@ -190,12 +189,12 @@ mod tests { "called described".into() } - fn explain_match(&self, _: &T) -> Description { + fn explain_match<'b>(&self, _: &'b T) -> Description where 'a: 'b{ "called explain_match".into() } } verify_that!( - len(TestMatcher(Default::default())).explain_match(&[1, 2, 3]), + len(TestMatcher).explain_match(&[1, 2, 3]), displays_as(eq("which has length 3, called explain_match")) ) } diff --git a/googletest/src/matchers/lt_matcher.rs b/googletest/src/matchers/lt_matcher.rs index 08bc563a..02238d0b 100644 --- a/googletest/src/matchers/lt_matcher.rs +++ b/googletest/src/matchers/lt_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a value less (in the sense of `<`) than `expected`. /// @@ -74,23 +74,19 @@ use std::{fmt::Debug, marker::PhantomData}; /// /// You can find the standard library `PartialOrd` implementation in /// -pub fn lt, ExpectedT: Debug>( - expected: ExpectedT, -) -> impl Matcher { - LtMatcher:: { expected, phantom: Default::default() } +pub fn lt(expected: ExpectedT) -> LtMatcher { + LtMatcher { expected } } -struct LtMatcher { +#[derive(MatcherExt)] +pub struct LtMatcher { expected: ExpectedT, - phantom: PhantomData, } -impl, ExpectedT: Debug> Matcher - for LtMatcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> + for LtMatcher { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b { (*actual < self.expected).into() } diff --git a/googletest/src/matchers/matches_regex_matcher.rs b/googletest/src/matchers/matches_regex_matcher.rs index 32b053b4..31110563 100644 --- a/googletest/src/matchers/matches_regex_matcher.rs +++ b/googletest/src/matchers/matches_regex_matcher.rs @@ -13,10 +13,9 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use regex::Regex; use std::fmt::Debug; -use std::marker::PhantomData; use std::ops::Deref; /// Matches a string the entirety of which which matches the given regular @@ -60,38 +59,31 @@ use std::ops::Deref; // compiler treats it as a Matcher only and the code // verify_that!("Some value".to_string(), matches_regex(".*value"))?; // doesn't compile. -pub fn matches_regex>( +pub fn matches_regex>( pattern: PatternT, -) -> MatchesRegexMatcher { +) -> MatchesRegexMatcher { let adjusted_pattern = format!("^{}$", pattern.deref()); let regex = Regex::new(adjusted_pattern.as_str()).unwrap(); - MatchesRegexMatcher { - regex, - pattern, - _adjusted_pattern: adjusted_pattern, - phantom: Default::default(), - } + MatchesRegexMatcher { regex, pattern, _adjusted_pattern: adjusted_pattern } } /// A matcher matching a string-like type matching a given regular expression. /// /// Intended only to be used from the function [`matches_regex`] only. /// Should not be referenced by code outside this library. -pub struct MatchesRegexMatcher> { +#[derive(MatcherExt)] +pub struct MatchesRegexMatcher> { regex: Regex, pattern: PatternT, _adjusted_pattern: String, - phantom: PhantomData, } -impl Matcher for MatchesRegexMatcher +impl<'a, PatternT, ActualT> Matcher<'a, ActualT> for MatchesRegexMatcher where PatternT: Deref, ActualT: AsRef + Debug + ?Sized, { - type ActualT = ActualT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ self.regex.is_match(actual.as_ref()).into() } @@ -109,7 +101,7 @@ where #[cfg(test)] mod tests { - use super::{matches_regex, MatchesRegexMatcher}; + use super::matches_regex; use crate::matcher::{Matcher, MatcherResult}; use crate::prelude::*; @@ -201,10 +193,10 @@ mod tests { #[test] fn matches_regex_displays_quoted_debug_of_pattern() -> Result<()> { - let matcher: MatchesRegexMatcher<&str, _> = matches_regex("\n"); + let matcher = matches_regex("\n"); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("matches the regular expression \"\\n\"")) ) } diff --git a/googletest/src/matchers/near_matcher.rs b/googletest/src/matchers/near_matcher.rs index ca7cbdf6..4873fe09 100644 --- a/googletest/src/matchers/near_matcher.rs +++ b/googletest/src/matchers/near_matcher.rs @@ -14,7 +14,7 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; use num_traits::{Float, FloatConst}; use std::fmt::Debug; @@ -139,6 +139,7 @@ pub fn approx_eq(expected: T) -> NearMatch /// A matcher which matches floating-point numbers approximately equal to its /// expected value. +#[derive(MatcherExt)] pub struct NearMatcher { expected: T, max_abs_error: T, @@ -166,10 +167,8 @@ impl NearMatcher { } } -impl Matcher for NearMatcher { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { +impl<'a, T: Debug + Float> Matcher<'a, T> for NearMatcher { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ if self.nans_are_equal && self.expected.is_nan() && actual.is_nan() { return MatcherResult::Match; } diff --git a/googletest/src/matchers/none_matcher.rs b/googletest/src/matchers/none_matcher.rs index af289327..c2c00379 100644 --- a/googletest/src/matchers/none_matcher.rs +++ b/googletest/src/matchers/none_matcher.rs @@ -13,9 +13,8 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use std::fmt::Debug; -use std::marker::PhantomData; /// Matches an `Option` containing `None`. /// @@ -32,18 +31,15 @@ use std::marker::PhantomData; /// # should_pass().unwrap(); /// # should_fail().unwrap_err(); /// ``` -pub fn none() -> impl Matcher> { - NoneMatcher:: { phantom: Default::default() } +pub fn none() -> NoneMatcher { + NoneMatcher } -struct NoneMatcher { - phantom: PhantomData, -} - -impl Matcher for NoneMatcher { - type ActualT = Option; +#[derive(MatcherExt)] +pub struct NoneMatcher; - fn matches(&self, actual: &Option) -> MatcherResult { +impl<'a, T: Debug> Matcher<'a, Option> for NoneMatcher { + fn matches<'b>(&self, actual: &'b Option) -> MatcherResult where 'a: 'b { (actual.is_none()).into() } @@ -63,9 +59,9 @@ mod tests { #[test] fn none_matches_option_with_none() -> Result<()> { - let matcher = none::(); + let matcher = none(); - let result = matcher.matches(&None); + let result = matcher.matches(&None::); verify_that!(result, eq(MatcherResult::Match)) } diff --git a/googletest/src/matchers/not_matcher.rs b/googletest/src/matchers/not_matcher.rs index f03d4cec..e6b39b30 100644 --- a/googletest/src/matchers/not_matcher.rs +++ b/googletest/src/matchers/not_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches the actual value exactly when the inner matcher does _not_ match. /// @@ -33,28 +33,30 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_pass().unwrap(); /// # should_fail().unwrap_err(); /// ``` -pub fn not>( - inner: InnerMatcherT, -) -> impl Matcher { - NotMatcher:: { inner, phantom: Default::default() } +pub fn not(inner: InnerMatcherT) -> NotMatcher { + NotMatcher { inner } } -struct NotMatcher { +#[derive(MatcherExt)] +pub struct NotMatcher { inner: InnerMatcherT, - phantom: PhantomData, } -impl> Matcher for NotMatcher { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { +impl<'a, T: Debug, InnerMatcherT: Matcher<'a, T>> Matcher<'a, T> for NotMatcher { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult + where + 'a: 'b, + { match self.inner.matches(actual) { MatcherResult::Match => MatcherResult::NoMatch, MatcherResult::NoMatch => MatcherResult::Match, } } - fn explain_match(&self, actual: &T) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description + where + 'a: 'b, + { self.inner.explain_match(actual) } diff --git a/googletest/src/matchers/ok_matcher.rs b/googletest/src/matchers/ok_matcher.rs index 8425b93a..4a90157f 100644 --- a/googletest/src/matchers/ok_matcher.rs +++ b/googletest/src/matchers/ok_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a `Result` containing `Ok` with a value matched by `inner`. /// @@ -38,28 +38,29 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_fail_1().unwrap_err(); /// # should_fail_2().unwrap_err(); /// ``` -pub fn ok( - inner: impl Matcher, -) -> impl Matcher> { - OkMatcher:: { inner, phantom_t: Default::default(), phantom_e: Default::default() } +pub fn ok(inner: InnerMatcherT) -> OkMatcher { + OkMatcher { inner } } -struct OkMatcher { +#[derive(MatcherExt)] +pub struct OkMatcher { inner: InnerMatcherT, - phantom_t: PhantomData, - phantom_e: PhantomData, } -impl> Matcher - for OkMatcher +impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<'a, T>> Matcher<'a, std::result::Result> + for OkMatcher { - type ActualT = std::result::Result; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b std::result::Result) -> MatcherResult + where + 'a: 'b, + { actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch) } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b std::result::Result) -> Description + where + 'a: 'b, + { match actual { Ok(o) => { Description::new().text("which is a success").nested(self.inner.explain_match(o)) @@ -141,13 +142,9 @@ mod tests { #[test] fn ok_describe_matches() -> Result<()> { - let matcher = super::OkMatcher:: { - inner: eq(1), - phantom_t: Default::default(), - phantom_e: Default::default(), - }; + let matcher = ok(eq(1)); verify_that!( - matcher.describe(MatcherResult::Match), + Matcher::>::describe(&matcher, MatcherResult::Match), displays_as(eq("is a success containing a value, which is equal to 1")) ) } diff --git a/googletest/src/matchers/points_to_matcher.rs b/googletest/src/matchers/points_to_matcher.rs index 2c516d0c..ded946f1 100644 --- a/googletest/src/matchers/points_to_matcher.rs +++ b/googletest/src/matchers/points_to_matcher.rs @@ -13,9 +13,8 @@ // limitations under the License. use crate::description::Description; -use crate::matcher::{Matcher, MatcherResult}; +use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use std::fmt::Debug; -use std::marker::PhantomData; use std::ops::Deref; /// Matches a (smart) pointer pointing to a value matched by the [`Matcher`] @@ -32,35 +31,26 @@ use std::ops::Deref; /// # } /// # should_pass().unwrap(); /// ``` -pub fn points_to( - expected: MatcherT, -) -> impl Matcher -where - ExpectedT: Debug, - MatcherT: Matcher, - ActualT: Deref + Debug + ?Sized, -{ - PointsToMatcher { expected, phantom: Default::default() } +pub fn points_to(expected: MatcherT) -> PointsToMatcher { + PointsToMatcher { expected } } -struct PointsToMatcher { +#[derive(MatcherExt)] +pub struct PointsToMatcher { expected: MatcherT, - phantom: PhantomData, } -impl Matcher for PointsToMatcher +impl<'a, ExpectedT, MatcherT, ActualT> Matcher<'a, ActualT> for PointsToMatcher where ExpectedT: Debug, - MatcherT: Matcher, + MatcherT: Matcher<'a, ExpectedT>, ActualT: Deref + Debug + ?Sized, { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ self.expected.matches(actual.deref()) } - fn explain_match(&self, actual: &ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'a: 'b{ self.expected.explain_match(actual.deref()) } diff --git a/googletest/src/matchers/pointwise_matcher.rs b/googletest/src/matchers/pointwise_matcher.rs index 01e70c0d..0d3cafe1 100644 --- a/googletest/src/matchers/pointwise_matcher.rs +++ b/googletest/src/matchers/pointwise_matcher.rs @@ -152,33 +152,34 @@ macro_rules! __pointwise { #[doc(hidden)] pub mod internal { use crate::description::Description; - use crate::matcher::{Matcher, MatcherResult}; + use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use crate::matcher_support::zipped_iterator::zip; - use std::{fmt::Debug, marker::PhantomData}; + use std::fmt::Debug; /// This struct is meant to be used only through the `pointwise` macro. /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub struct PointwiseMatcher { + #[derive(MatcherExt)] + pub struct PointwiseMatcher { matchers: Vec, - phantom: PhantomData, } - impl PointwiseMatcher { + impl PointwiseMatcher { pub fn new(matchers: Vec) -> Self { - Self { matchers, phantom: Default::default() } + Self { matchers } } } - impl, ContainerT: ?Sized + Debug> Matcher - for PointwiseMatcher + impl<'a, T: Debug, MatcherT: Matcher<'a, T>, ContainerT: ?Sized + Debug> Matcher<'a, ContainerT> + for PointwiseMatcher where for<'b> &'b ContainerT: IntoIterator, { - type ActualT = ContainerT; - - fn matches(&self, actual: &ContainerT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ContainerT) -> MatcherResult + where + 'a: 'b, + { 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() { @@ -192,7 +193,10 @@ pub mod internal { } } - fn explain_match(&self, actual: &ContainerT) -> Description { + fn explain_match<'b>(&self, actual: &'b ContainerT) -> Description + where + 'a: 'b, + { // TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider // extract as a separate library. (or implement pointwise! with // elements_are) diff --git a/googletest/src/matchers/predicate_matcher.rs b/googletest/src/matchers/predicate_matcher.rs index 5bc067d7..b56a0aed 100644 --- a/googletest/src/matchers/predicate_matcher.rs +++ b/googletest/src/matchers/predicate_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Creates a matcher based on the predicate provided. /// @@ -38,7 +38,7 @@ use std::{fmt::Debug, marker::PhantomData}; /// This is easily fixed by explicitly declaring the type of the argument pub fn predicate( predicate: P, -) -> PredicateMatcher +) -> PredicateMatcher where for<'a> P: Fn(&'a T) -> bool, { @@ -46,11 +46,10 @@ where predicate, positive_description: NoDescription, negative_description: NoDescription, - phantom: Default::default(), } } -impl PredicateMatcher { +impl

PredicateMatcher { /// Configures this instance to provide a more meaningful description. /// /// For example, to make sure the error message is more useful @@ -70,24 +69,19 @@ impl PredicateMatcher { self, positive_description: D1, negative_description: D2, - ) -> PredicateMatcher { - PredicateMatcher { - predicate: self.predicate, - positive_description, - negative_description, - phantom: Default::default(), - } + ) -> PredicateMatcher { + PredicateMatcher { predicate: self.predicate, positive_description, negative_description } } } /// A matcher which applies `predicate` on the value. /// /// See [`predicate`]. -pub struct PredicateMatcher { +#[derive(MatcherExt)] +pub struct PredicateMatcher { predicate: P, positive_description: D1, negative_description: D2, - phantom: PhantomData, } /// A trait to allow [`PredicateMatcher::with_description`] to accept multiple @@ -124,13 +118,11 @@ where #[doc(hidden)] pub struct NoDescription; -impl Matcher for PredicateMatcher +impl<'a, T: Debug, P> Matcher<'a, T> for PredicateMatcher where - for<'a> P: Fn(&'a T) -> bool, + for<'b> P: Fn(&'b T) -> bool, { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ (self.predicate)(actual).into() } @@ -142,14 +134,12 @@ where } } -impl Matcher - for PredicateMatcher +impl<'a, T: Debug, P, D1: PredicateDescription, D2: PredicateDescription> Matcher<'a, T> + for PredicateMatcher where - for<'a> P: Fn(&'a T) -> bool, + for<'b> P: Fn(&'b T) -> bool, { - type ActualT = T; - - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ (self.predicate)(actual).into() } @@ -168,7 +158,7 @@ mod tests { use crate::prelude::*; // Simple matcher with a description - fn is_odd() -> impl Matcher { + fn is_odd<'a>() -> impl Matcher<'a, i32> { predicate(|x| x % 2 == 1).with_description("is odd", "is even") } @@ -188,7 +178,7 @@ mod tests { } // Simple Matcher without description - fn is_even() -> impl Matcher { + fn is_even<'a>() -> impl Matcher<'a, i32> { predicate(|x| x % 2 == 0) } diff --git a/googletest/src/matchers/property_matcher.rs b/googletest/src/matchers/property_matcher.rs index 19b48627..78f4f9e8 100644 --- a/googletest/src/matchers/property_matcher.rs +++ b/googletest/src/matchers/property_matcher.rs @@ -136,37 +136,40 @@ macro_rules! property_internal { pub mod internal { use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; - use std::{fmt::Debug, marker::PhantomData}; + use std::fmt::Debug; /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub fn property_matcher>( - extractor: impl Fn(&OuterT) -> InnerT, + pub fn property_matcher<'a, + OuterT: Debug, + InnerT: Debug, + MatcherT: Matcher<'a, InnerT>, + ExtractorT: Fn(&OuterT) -> InnerT, + >( + extractor: ExtractorT, property_desc: &'static str, inner: MatcherT, - ) -> impl Matcher { - PropertyMatcher { extractor, property_desc, inner, phantom: Default::default() } + ) -> PropertyMatcher { + PropertyMatcher { extractor, property_desc, inner } } - struct PropertyMatcher { + #[derive(MatcherExt)] + pub struct PropertyMatcher { extractor: ExtractorT, property_desc: &'static str, inner: MatcherT, - phantom: PhantomData, } - impl Matcher for PropertyMatcher + impl<'a, InnerT, OuterT, ExtractorT, MatcherT> Matcher<'a, OuterT> for PropertyMatcher where InnerT: Debug, OuterT: Debug, ExtractorT: Fn(&OuterT) -> InnerT, - MatcherT: Matcher, + MatcherT: for<'b> Matcher<'b, InnerT>, { - type ActualT = OuterT; - - fn matches(&self, actual: &OuterT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b OuterT) -> MatcherResult where 'a: 'b { self.inner.matches(&(self.extractor)(actual)) } @@ -179,7 +182,7 @@ pub mod internal { .into() } - fn explain_match(&self, actual: &OuterT) -> Description { + fn explain_match<'b>(&self, actual: &'b OuterT) -> Description where 'a: 'b{ let actual_inner = (self.extractor)(actual); format!( "whose property `{}` is `{:#?}`, {}", @@ -193,31 +196,30 @@ pub mod internal { /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub fn property_ref_matcher( + pub fn property_ref_matcher<'a, OuterT, InnerT, MatcherT>( extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, - ) -> impl Matcher + ) -> PropertyRefMatcher where OuterT: Debug, InnerT: Debug + ?Sized, - MatcherT: Matcher, + MatcherT: Matcher<'a, InnerT>, { PropertyRefMatcher { extractor, property_desc, inner } } - struct PropertyRefMatcher { + #[derive(MatcherExt)] + pub struct PropertyRefMatcher { extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, } - impl> Matcher + impl<'a, InnerT: Debug + ?Sized, OuterT: Debug, MatcherT: Matcher<'a, InnerT>> Matcher<'a, OuterT> for PropertyRefMatcher { - type ActualT = OuterT; - - fn matches(&self, actual: &OuterT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b OuterT) -> MatcherResult where 'a: 'b{ self.inner.matches((self.extractor)(actual)) } @@ -230,7 +232,7 @@ pub mod internal { .into() } - fn explain_match(&self, actual: &OuterT) -> Description { + fn explain_match<'b>(&self, actual: &'b OuterT) -> Description where 'a: 'b{ let actual_inner = (self.extractor)(actual); format!( "whose property `{}` is `{:#?}`, {}", diff --git a/googletest/src/matchers/some_matcher.rs b/googletest/src/matchers/some_matcher.rs index 905aa17d..f41f6bf3 100644 --- a/googletest/src/matchers/some_matcher.rs +++ b/googletest/src/matchers/some_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches an `Option` containing a value matched by `inner`. /// @@ -38,23 +38,21 @@ use std::{fmt::Debug, marker::PhantomData}; /// # should_fail_1().unwrap_err(); /// # should_fail_2().unwrap_err(); /// ``` -pub fn some(inner: impl Matcher) -> impl Matcher> { - SomeMatcher { inner, phantom: Default::default() } +pub fn some(inner: Inner) -> SomeMatcher { + SomeMatcher { inner } } -struct SomeMatcher { +#[derive(MatcherExt)] +pub struct SomeMatcher { inner: InnerMatcherT, - phantom: PhantomData, } -impl> Matcher for SomeMatcher { - type ActualT = Option; - - fn matches(&self, actual: &Option) -> MatcherResult { +impl<'a, T: Debug, InnerMatcherT: Matcher<'a, T>> Matcher<'a, Option> for SomeMatcher { + fn matches<'b>(&self, actual: &'b Option) -> MatcherResult where 'a: 'b{ actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch) } - fn explain_match(&self, actual: &Option) -> Description { + fn explain_match<'b>(&self, actual: &'b Option) -> Description where 'a: 'b{ match (self.matches(actual), actual) { (_, Some(t)) => { Description::new().text("which has a value").nested(self.inner.explain_match(t)) @@ -104,9 +102,9 @@ mod tests { #[test] fn some_does_not_match_option_with_none() -> Result<()> { - let matcher = some(eq::(1)); + let matcher = some(eq(1)); - let result = matcher.matches(&None); + let result = matcher.matches(&None::); verify_that!(result, eq(MatcherResult::NoMatch)) } @@ -131,7 +129,7 @@ mod tests { #[test] fn some_describe_matches() -> Result<()> { verify_that!( - some(eq::(1)).describe(MatcherResult::Match), + Matcher::>::describe(&some(eq(1)), MatcherResult::Match), displays_as(eq("has a value which is equal to 1")) ) } @@ -139,14 +137,14 @@ mod tests { #[test] fn some_describe_does_not_match() -> Result<()> { verify_that!( - some(eq::(1)).describe(MatcherResult::NoMatch), + Matcher::>::describe(&some(eq(1)), MatcherResult::NoMatch), displays_as(eq("is None or has a value which isn't equal to 1")) ) } #[test] fn some_explain_match_with_none() -> Result<()> { - verify_that!(some(eq::(1)).explain_match(&None), displays_as(eq("which is None"))) + verify_that!(some(eq(1)).explain_match(&None::), displays_as(eq("which is None"))) } #[test] diff --git a/googletest/src/matchers/str_matcher.rs b/googletest/src/matchers/str_matcher.rs index f4f158e5..467bf867 100644 --- a/googletest/src/matchers/str_matcher.rs +++ b/googletest/src/matchers/str_matcher.rs @@ -14,7 +14,7 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, matcher_support::{ edit_distance, summarize_diff::{create_diff, create_diff_reversed}, @@ -23,7 +23,6 @@ use crate::{ }; use std::borrow::Cow; use std::fmt::Debug; -use std::marker::PhantomData; use std::ops::Deref; /// Matches a string containing a given substring. @@ -59,11 +58,10 @@ use std::ops::Deref; /// > and expected values when matching strings while /// > [`ignoring_ascii_case`][StrMatcherConfigurator::ignoring_ascii_case] is /// > set. -pub fn contains_substring(expected: T) -> StrMatcher { +pub fn contains_substring(expected: T) -> StrMatcher { StrMatcher { configuration: Configuration { mode: MatchMode::Contains, ..Default::default() }, expected, - phantom: Default::default(), } } @@ -99,11 +97,10 @@ pub fn contains_substring(expected: T) -> StrMatcher { /// /// See the [`StrMatcherConfigurator`] extension trait for more options on how /// the string is matched. -pub fn starts_with(expected: T) -> StrMatcher { +pub fn starts_with(expected: T) -> StrMatcher { StrMatcher { configuration: Configuration { mode: MatchMode::StartsWith, ..Default::default() }, expected, - phantom: Default::default(), } } @@ -139,11 +136,10 @@ pub fn starts_with(expected: T) -> StrMatcher { /// /// See the [`StrMatcherConfigurator`] extension trait for more options on how /// the string is matched. -pub fn ends_with(expected: T) -> StrMatcher { +pub fn ends_with(expected: T) -> StrMatcher { StrMatcher { configuration: Configuration { mode: MatchMode::EndsWith, ..Default::default() }, expected, - phantom: Default::default(), } } @@ -152,7 +148,7 @@ pub fn ends_with(expected: T) -> StrMatcher { /// Matchers which match against string values and, through configuration, /// specialise to [`StrMatcher`] implement this trait. That includes /// [`EqMatcher`] and [`StrMatcher`]. -pub trait StrMatcherConfigurator { +pub trait StrMatcherConfigurator { /// Configures the matcher to ignore any leading whitespace in either the /// actual or the expected value. /// @@ -171,7 +167,7 @@ pub trait StrMatcherConfigurator { /// When all other configuration options are left as the defaults, this is /// equivalent to invoking [`str::trim_start`] on both the expected and /// actual value. - fn ignoring_leading_whitespace(self) -> StrMatcher; + fn ignoring_leading_whitespace(self) -> StrMatcher; /// Configures the matcher to ignore any trailing whitespace in either the /// actual or the expected value. @@ -191,7 +187,7 @@ pub trait StrMatcherConfigurator { /// When all other configuration options are left as the defaults, this is /// equivalent to invoking [`str::trim_end`] on both the expected and /// actual value. - fn ignoring_trailing_whitespace(self) -> StrMatcher; + fn ignoring_trailing_whitespace(self) -> StrMatcher; /// Configures the matcher to ignore both leading and trailing whitespace in /// either the actual or the expected value. @@ -215,7 +211,7 @@ pub trait StrMatcherConfigurator { /// When all other configuration options are left as the defaults, this is /// equivalent to invoking [`str::trim`] on both the expected and actual /// value. - fn ignoring_outer_whitespace(self) -> StrMatcher; + fn ignoring_outer_whitespace(self) -> StrMatcher; /// Configures the matcher to ignore ASCII case when comparing values. /// @@ -237,7 +233,7 @@ pub trait StrMatcherConfigurator { /// /// This is **not guaranteed** to match strings with differing upper/lower /// case characters outside of the codepoints 0-127 covered by ASCII. - fn ignoring_ascii_case(self) -> StrMatcher; + fn ignoring_ascii_case(self) -> StrMatcher; /// Configures the matcher to match only strings which otherwise satisfy the /// conditions a number times matched by the matcher `times`. @@ -272,10 +268,7 @@ pub trait StrMatcherConfigurator { /// This is only meaningful when the matcher was constructed with /// [`contains_substring`]. This method will panic when it is used with any /// other matcher construction. - fn times( - self, - times: impl Matcher + 'static, - ) -> StrMatcher; + fn times(self, times: impl for<'a> Matcher<'a, usize> + 'static) -> StrMatcher; } /// A matcher which matches equality or containment of a string-like value in a @@ -287,20 +280,18 @@ pub trait StrMatcherConfigurator { /// * [`contains_substring`], /// * [`starts_with`], /// * [`ends_with`]. -pub struct StrMatcher { +#[derive(MatcherExt)] +pub struct StrMatcher { expected: ExpectedT, configuration: Configuration, - phantom: PhantomData, } -impl Matcher for StrMatcher +impl<'a, ExpectedT, ActualT> Matcher<'a, ActualT> for StrMatcher where ExpectedT: Deref + Debug, ActualT: AsRef + Debug + ?Sized, { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ self.configuration.do_strings_match(self.expected.deref(), actual.as_ref()).into() } @@ -308,15 +299,15 @@ where self.configuration.describe(matcher_result, self.expected.deref()) } - fn explain_match(&self, actual: &ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'a: 'b{ self.configuration.explain_match(self.expected.deref(), actual.as_ref()) } } -impl>> - StrMatcherConfigurator for MatcherT +impl>> StrMatcherConfigurator + for MatcherT { - fn ignoring_leading_whitespace(self) -> StrMatcher { + fn ignoring_leading_whitespace(self) -> StrMatcher { let existing = self.into(); StrMatcher { configuration: existing.configuration.ignoring_leading_whitespace(), @@ -324,7 +315,7 @@ impl>> } } - fn ignoring_trailing_whitespace(self) -> StrMatcher { + fn ignoring_trailing_whitespace(self) -> StrMatcher { let existing = self.into(); StrMatcher { configuration: existing.configuration.ignoring_trailing_whitespace(), @@ -332,20 +323,17 @@ impl>> } } - fn ignoring_outer_whitespace(self) -> StrMatcher { + fn ignoring_outer_whitespace(self) -> StrMatcher { let existing = self.into(); StrMatcher { configuration: existing.configuration.ignoring_outer_whitespace(), ..existing } } - fn ignoring_ascii_case(self) -> StrMatcher { + fn ignoring_ascii_case(self) -> StrMatcher { let existing = self.into(); StrMatcher { configuration: existing.configuration.ignoring_ascii_case(), ..existing } } - fn times( - self, - times: impl Matcher + 'static, - ) -> StrMatcher { + fn times(self, times: impl for<'a> Matcher<'a, usize> + 'static) -> StrMatcher { let existing = self.into(); if !matches!(existing.configuration.mode, MatchMode::Contains) { panic!("The times() configurator is only meaningful with contains_substring()."); @@ -354,25 +342,25 @@ impl>> } } -impl> From> for StrMatcher { - fn from(value: EqMatcher) -> Self { +impl> From> for StrMatcher { + fn from(value: EqMatcher) -> Self { Self::with_default_config(value.expected) } } -impl> From> for StrMatcher { - fn from(value: EqDerefOfMatcher) -> Self { +impl> From> for StrMatcher { + fn from(value: EqDerefOfMatcher) -> Self { Self::with_default_config(value.expected) } } -impl StrMatcher { +impl StrMatcher { /// Returns a [`StrMatcher`] with a default configuration to match against /// the given expected value. /// /// This default configuration is sensitive to whitespace and case. fn with_default_config(expected: T) -> Self { - Self { expected, configuration: Default::default(), phantom: Default::default() } + Self { expected, configuration: Default::default() } } } @@ -387,7 +375,7 @@ struct Configuration { ignore_leading_whitespace: bool, ignore_trailing_whitespace: bool, case_policy: CasePolicy, - times: Option>>, + times: Option Matcher<'a, usize>>>, } #[derive(Clone)] @@ -467,7 +455,7 @@ impl Configuration { } } - // StrMatcher::describe redirects immediately to this function. + // StrMatcher::::describe redirects immediately to this function. fn describe(&self, matcher_result: MatcherResult, expected: &str) -> Description { let mut addenda: Vec> = Vec::with_capacity(3); match (self.ignore_leading_whitespace, self.ignore_trailing_whitespace) { @@ -570,7 +558,7 @@ impl Configuration { Self { case_policy: CasePolicy::IgnoreAscii, ..self } } - fn times(self, times: impl Matcher + 'static) -> Self { + fn times(self, times: impl for<'a> Matcher<'a, usize> + 'static) -> Self { Self { times: Some(Box::new(times)), ..self } } } @@ -811,48 +799,45 @@ mod tests { #[test] fn describes_itself_for_matching_result() -> Result<()> { - let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string"); + let matcher = StrMatcher::with_default_config("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("is equal to \"A string\"")) ) } #[test] fn describes_itself_for_non_matching_result() -> Result<()> { - let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string"); + let matcher = StrMatcher::with_default_config("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::NoMatch), + Matcher::::describe(&matcher, MatcherResult::NoMatch), displays_as(eq("isn't equal to \"A string\"")) ) } #[test] fn describes_itself_for_matching_result_ignoring_leading_whitespace() -> Result<()> { - let matcher: StrMatcher<&str, _> = - StrMatcher::with_default_config("A string").ignoring_leading_whitespace(); + let matcher = StrMatcher::with_default_config("A string").ignoring_leading_whitespace(); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("is equal to \"A string\" (ignoring leading whitespace)")) ) } #[test] fn describes_itself_for_non_matching_result_ignoring_leading_whitespace() -> Result<()> { - let matcher: StrMatcher<&str, _> = - StrMatcher::with_default_config("A string").ignoring_leading_whitespace(); + let matcher = StrMatcher::with_default_config("A string").ignoring_leading_whitespace(); verify_that!( - Matcher::describe(&matcher, MatcherResult::NoMatch), + Matcher::::describe(&matcher, MatcherResult::NoMatch), displays_as(eq("isn't equal to \"A string\" (ignoring leading whitespace)")) ) } #[test] fn describes_itself_for_matching_result_ignoring_trailing_whitespace() -> Result<()> { - let matcher: StrMatcher<&str, _> = - StrMatcher::with_default_config("A string").ignoring_trailing_whitespace(); + let matcher = StrMatcher::with_default_config("A string").ignoring_trailing_whitespace(); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("is equal to \"A string\" (ignoring trailing whitespace)")) ) } @@ -860,20 +845,18 @@ mod tests { #[test] fn describes_itself_for_matching_result_ignoring_leading_and_trailing_whitespace() -> Result<()> { - let matcher: StrMatcher<&str, _> = - StrMatcher::with_default_config("A string").ignoring_outer_whitespace(); + let matcher = StrMatcher::with_default_config("A string").ignoring_outer_whitespace(); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("is equal to \"A string\" (ignoring leading and trailing whitespace)")) ) } #[test] fn describes_itself_for_matching_result_ignoring_ascii_case() -> Result<()> { - let matcher: StrMatcher<&str, _> = - StrMatcher::with_default_config("A string").ignoring_ascii_case(); + let matcher = StrMatcher::with_default_config("A string").ignoring_ascii_case(); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("is equal to \"A string\" (ignoring ASCII case)")) ) } @@ -881,11 +864,11 @@ mod tests { #[test] fn describes_itself_for_matching_result_ignoring_ascii_case_and_leading_whitespace() -> Result<()> { - let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string") + let matcher = StrMatcher::with_default_config("A string") .ignoring_leading_whitespace() .ignoring_ascii_case(); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq( "is equal to \"A string\" (ignoring leading whitespace, ignoring ASCII case)" )) @@ -894,63 +877,63 @@ mod tests { #[test] fn describes_itself_for_matching_result_in_contains_mode() -> Result<()> { - let matcher: StrMatcher<&str, _> = contains_substring("A string"); + let matcher = contains_substring("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("contains a substring \"A string\"")) ) } #[test] fn describes_itself_for_non_matching_result_in_contains_mode() -> Result<()> { - let matcher: StrMatcher<&str, _> = contains_substring("A string"); + let matcher = contains_substring("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::NoMatch), + Matcher::::describe(&matcher, MatcherResult::NoMatch), displays_as(eq("does not contain a substring \"A string\"")) ) } #[test] fn describes_itself_with_count_number() -> Result<()> { - let matcher: StrMatcher<&str, _> = contains_substring("A string").times(gt(2)); + let matcher = contains_substring("A string").times(gt(2)); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("contains a substring \"A string\" (count is greater than 2)")) ) } #[test] fn describes_itself_for_matching_result_in_starts_with_mode() -> Result<()> { - let matcher: StrMatcher<&str, _> = starts_with("A string"); + let matcher = starts_with("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("starts with prefix \"A string\"")) ) } #[test] fn describes_itself_for_non_matching_result_in_starts_with_mode() -> Result<()> { - let matcher: StrMatcher<&str, _> = starts_with("A string"); + let matcher = starts_with("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::NoMatch), + Matcher::::describe(&matcher, MatcherResult::NoMatch), displays_as(eq("does not start with \"A string\"")) ) } #[test] fn describes_itself_for_matching_result_in_ends_with_mode() -> Result<()> { - let matcher: StrMatcher<&str, _> = ends_with("A string"); + let matcher = ends_with("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::::describe(&matcher, MatcherResult::Match), displays_as(eq("ends with suffix \"A string\"")) ) } #[test] fn describes_itself_for_non_matching_result_in_ends_with_mode() -> Result<()> { - let matcher: StrMatcher<&str, _> = ends_with("A string"); + let matcher = ends_with("A string"); verify_that!( - Matcher::describe(&matcher, MatcherResult::NoMatch), + Matcher::::describe(&matcher, MatcherResult::NoMatch), displays_as(eq("does not end with \"A string\"")) ) } diff --git a/googletest/src/matchers/subset_of_matcher.rs b/googletest/src/matchers/subset_of_matcher.rs index 24c00d80..e14e8d3f 100644 --- a/googletest/src/matchers/subset_of_matcher.rs +++ b/googletest/src/matchers/subset_of_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a container all of whose items are in the given container /// `superset`. @@ -83,30 +83,22 @@ use std::{fmt::Debug, marker::PhantomData}; /// runtime proportional to the *product* of the sizes of the actual and /// expected containers as well as the time to check equality of each pair of /// items. It should not be used on especially large containers. -pub fn subset_of( - superset: ExpectedT, -) -> impl Matcher -where - for<'a> &'a ActualT: IntoIterator, - for<'a> &'a ExpectedT: IntoIterator, -{ - SubsetOfMatcher:: { superset, phantom: Default::default() } +pub fn subset_of(superset: ExpectedT) -> SubsetOfMatcher { + SubsetOfMatcher { superset } } -struct SubsetOfMatcher { +#[derive(MatcherExt)] +pub struct SubsetOfMatcher { superset: ExpectedT, - phantom: PhantomData, } -impl Matcher - for SubsetOfMatcher +impl<'c, ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher<'c, ActualT> + for SubsetOfMatcher where for<'a> &'a ActualT: IntoIterator, for<'a> &'a ExpectedT: IntoIterator, { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'c: 'b{ for actual_item in actual { if self.expected_is_missing(actual_item) { return MatcherResult::NoMatch; @@ -115,7 +107,7 @@ where MatcherResult::Match } - fn explain_match(&self, actual: &ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'c: 'b{ let unexpected_elements = actual .into_iter() .enumerate() @@ -138,7 +130,7 @@ where } } -impl SubsetOfMatcher +impl SubsetOfMatcher where for<'a> &'a ExpectedT: IntoIterator, { diff --git a/googletest/src/matchers/superset_of_matcher.rs b/googletest/src/matchers/superset_of_matcher.rs index d1e9d72a..ca37689e 100644 --- a/googletest/src/matchers/superset_of_matcher.rs +++ b/googletest/src/matchers/superset_of_matcher.rs @@ -14,9 +14,9 @@ use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; /// Matches a container containing all of the items in the given container /// `subset`. @@ -84,30 +84,22 @@ use std::{fmt::Debug, marker::PhantomData}; /// runtime proportional to the *product* of the sizes of the actual and /// expected containers as well as the time to check equality of each pair of /// items. It should not be used on especially large containers. -pub fn superset_of( - subset: ExpectedT, -) -> impl Matcher -where - for<'a> &'a ActualT: IntoIterator, - for<'a> &'a ExpectedT: IntoIterator, -{ - SupersetOfMatcher:: { subset, phantom: Default::default() } +pub fn superset_of(subset: ExpectedT) -> SupersetOfMatcher { + SupersetOfMatcher { subset } } -struct SupersetOfMatcher { +#[derive(MatcherExt)] +pub struct SupersetOfMatcher { subset: ExpectedT, - phantom: PhantomData, } -impl Matcher - for SupersetOfMatcher +impl<'c, ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher<'c, ActualT> + for SupersetOfMatcher where for<'a> &'a ActualT: IntoIterator, for<'a> &'a ExpectedT: IntoIterator, { - type ActualT = ActualT; - - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'c: 'b { for expected_item in &self.subset { if actual_is_missing(actual, expected_item) { return MatcherResult::NoMatch; @@ -116,7 +108,7 @@ where MatcherResult::Match } - fn explain_match(&self, actual: &ActualT) -> Description { + fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'c: 'b{ let missing_items: Vec<_> = self .subset .into_iter() diff --git a/googletest/src/matchers/tuple_matcher.rs b/googletest/src/matchers/tuple_matcher.rs index af55cbf6..38ac5159 100644 --- a/googletest/src/matchers/tuple_matcher.rs +++ b/googletest/src/matchers/tuple_matcher.rs @@ -23,16 +23,19 @@ pub mod internal { use crate::{ description::Description, - matcher::{Matcher, MatcherResult}, + matcher::{Matcher, MatcherExt, MatcherResult}, }; use std::fmt::Debug; + impl MatcherExt for () {} + // This implementation is provided for completeness, but is completely trivial. // The only actual value which can be supplied is (), which must match. - impl Matcher for () { - type ActualT = (); - - fn matches(&self, _: &Self::ActualT) -> MatcherResult { + impl<'a> Matcher<'a, ()> for () { + fn matches<'b>(&self, _: &'b ()) -> MatcherResult + where + 'a: 'b, + { MatcherResult::Match } @@ -50,12 +53,12 @@ pub mod internal { #[doc(hidden)] macro_rules! tuple_matcher_n { ($([$field_number:tt, $matcher_type:ident, $field_type:ident]),*) => { - impl<$($field_type: Debug, $matcher_type: Matcher),*> - Matcher for ($($matcher_type,)*) - { - type ActualT = ($($field_type,)*); + impl<$($matcher_type),*> MatcherExt for ($($matcher_type,)*){} - fn matches(&self, actual: &($($field_type,)*)) -> MatcherResult { + impl<'a, $($field_type: Debug, $matcher_type: Matcher<'a, $field_type>),*> + Matcher<'a, ($($field_type,)*)> for ($($matcher_type,)*) + { + fn matches<'b>(&self, actual: &'b ($($field_type,)*)) -> MatcherResult where 'a: 'b{ $(match self.$field_number.matches(&actual.$field_number) { MatcherResult::Match => {}, MatcherResult::NoMatch => { @@ -65,7 +68,7 @@ pub mod internal { MatcherResult::Match } - fn explain_match(&self, actual: &($($field_type,)*)) -> Description { + fn explain_match<'b>(&self, actual: &'b ($($field_type,)*)) -> Description where 'a: 'b{ let mut explanation = Description::new().text("which").nested(self.describe(self.matches(actual))); $(match self.$field_number.matches(&actual.$field_number) { MatcherResult::Match => {}, diff --git a/googletest/src/matchers/unordered_elements_are_matcher.rs b/googletest/src/matchers/unordered_elements_are_matcher.rs index f4585a43..efe005e4 100644 --- a/googletest/src/matchers/unordered_elements_are_matcher.rs +++ b/googletest/src/matchers/unordered_elements_are_matcher.rs @@ -366,31 +366,28 @@ macro_rules! __is_contained_in { #[doc(hidden)] pub mod internal { use crate::description::Description; - use crate::matcher::{Matcher, MatcherResult}; + use crate::matcher::{Matcher, MatcherExt, MatcherResult}; use crate::matcher_support::count_elements::count_elements; use std::collections::HashSet; use std::fmt::{Debug, Display}; - use std::marker::PhantomData; /// This struct is meant to be used only through the /// `unordered_elements_are![...]` macro. /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub struct UnorderedElementsAreMatcher<'a, ContainerT: ?Sized, T: Debug, const N: usize> { - elements: [Box + 'a>; N], + #[derive(MatcherExt)] + pub struct UnorderedElementsAreMatcher<'a, 'b, T: Debug, const N: usize> { + elements: [Box + 'a>; N], requirements: Requirements, - phantom: PhantomData, } - impl<'a, ContainerT: ?Sized, T: Debug, const N: usize> - UnorderedElementsAreMatcher<'a, ContainerT, T, N> - { + impl<'a, 'b, T: Debug, const N: usize> UnorderedElementsAreMatcher<'a, 'b, T, N> { pub fn new( - elements: [Box + 'a>; N], + elements: [Box + 'a>; N], requirements: Requirements, ) -> Self { - Self { elements, requirements, phantom: Default::default() } + Self { elements, requirements } } } @@ -403,19 +400,23 @@ pub mod internal { // least one expected element and vice versa. // 3. `UnorderedElementsAreMatcher` verifies that a perfect matching exists // using Ford-Fulkerson. - impl<'a, T: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher - for UnorderedElementsAreMatcher<'a, ContainerT, T, N> + impl<'a, 'b, T: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher<'b, ContainerT> + for UnorderedElementsAreMatcher<'a, 'b, T, N> where - for<'b> &'b ContainerT: IntoIterator, + for<'c> &'c ContainerT: IntoIterator, { - type ActualT = ContainerT; - - fn matches(&self, actual: &ContainerT) -> MatcherResult { + fn matches<'c>(&self, actual: &'c ContainerT) -> MatcherResult + where + 'b: 'c, + { let match_matrix = MatchMatrix::generate(actual, &self.elements); match_matrix.is_match_for(self.requirements).into() } - fn explain_match(&self, actual: &ContainerT) -> Description { + fn explain_match<'c>(&self, actual: &'c ContainerT) -> Description + where + 'b: 'c, + { if let Some(size_mismatch_explanation) = self.requirements.explain_size_mismatch(actual, N) { @@ -450,49 +451,50 @@ pub mod internal { } } - type KeyValueMatcher<'a, KeyT, ValueT> = - (Box + 'a>, Box + 'a>); + type KeyValueMatcher<'a, 'b, KeyT, ValueT> = + (Box + 'a>, Box + 'a>); /// This is the analogue to [UnorderedElementsAreMatcher] for maps and /// map-like collections. /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub struct UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, const N: usize> + pub struct UnorderedElementsOfMapAreMatcher<'a, 'b, KeyT, ValueT, const N: usize> where - ContainerT: ?Sized, KeyT: Debug, ValueT: Debug, { - elements: [KeyValueMatcher<'a, KeyT, ValueT>; N], + elements: [KeyValueMatcher<'a, 'b, KeyT, ValueT>; N], requirements: Requirements, - phantom: PhantomData, } - impl<'a, ContainerT, KeyT: Debug, ValueT: Debug, const N: usize> - UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, N> + impl<'a, 'b, KeyT: Debug, ValueT: Debug, const N: usize> + UnorderedElementsOfMapAreMatcher<'a, 'b, KeyT, ValueT, N> { pub fn new( - elements: [KeyValueMatcher<'a, KeyT, ValueT>; N], + elements: [KeyValueMatcher<'a, 'b, KeyT, ValueT>; N], requirements: Requirements, ) -> Self { - Self { elements, requirements, phantom: Default::default() } + Self { elements, requirements } } } - impl<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher - for UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, N> - where - for<'b> &'b ContainerT: IntoIterator, + impl<'a, 'b, KeyT: Debug, ValueT: Debug, const N: usize> MatcherExt + for UnorderedElementsOfMapAreMatcher<'a, 'b, KeyT, ValueT, N> { - type ActualT = ContainerT; + } - fn matches(&self, actual: &ContainerT) -> MatcherResult { + impl<'a, 'b, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize> + Matcher<'b, ContainerT> for UnorderedElementsOfMapAreMatcher<'a, 'b, KeyT, ValueT, N> + where + for<'c> &'c ContainerT: IntoIterator, + { + fn matches<'c>(&self, actual: &'c ContainerT) -> MatcherResult where 'b: 'c { let match_matrix = MatchMatrix::generate_for_map(actual, &self.elements); match_matrix.is_match_for(self.requirements).into() } - fn explain_match(&self, actual: &ContainerT) -> Description { + fn explain_match<'c>(&self, actual: &'c ContainerT) -> Description where 'b: 'c{ if let Some(size_mismatch_explanation) = self.requirements.explain_size_mismatch(actual, N) { @@ -601,12 +603,12 @@ pub mod internal { struct MatchMatrix(Vec<[MatcherResult; N]>); impl MatchMatrix { - fn generate<'a, T: Debug + 'a, ContainerT: Debug + ?Sized>( + fn generate<'a, 'b, T: Debug + 'a, ContainerT: Debug + ?Sized>( actual: &ContainerT, - expected: &[Box + 'a>; N], + expected: &[Box + 'a>; N], ) -> Self where - for<'b> &'b ContainerT: IntoIterator, + for<'c> &'c ContainerT: IntoIterator, { let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]); for (actual_idx, actual) in actual.into_iter().enumerate() { @@ -617,12 +619,12 @@ pub mod internal { matrix } - fn generate_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>( + fn generate_for_map<'a, 'b, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>( actual: &ContainerT, - expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N], + expected: &[KeyValueMatcher<'a, 'b, KeyT, ValueT>; N], ) -> Self where - for<'b> &'b ContainerT: IntoIterator, + for<'c> &'c ContainerT: IntoIterator, { let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]); for (actual_idx, (actual_key, actual_value)) in actual.into_iter().enumerate() { @@ -962,7 +964,7 @@ pub mod internal { fn get_explanation<'a, T: Debug, ContainerT: Debug + ?Sized>( &self, actual: &ContainerT, - expected: &[Box + 'a>; N], + expected: &[Box + 'a>; N], requirements: Requirements, ) -> Option where @@ -1006,14 +1008,14 @@ pub mod internal { ).into()) } - fn get_explanation_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>( + fn get_explanation_for_map<'a, 'b, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>( &self, actual: &ContainerT, - expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N], + expected: &[KeyValueMatcher<'a, 'b, KeyT, ValueT>; N], requirements: Requirements, ) -> Option where - for<'b> &'b ContainerT: IntoIterator, + for<'c> &'c ContainerT: IntoIterator, { let actual: Vec<_> = actual.into_iter().collect(); if self.is_full_match() { @@ -1068,7 +1070,6 @@ pub mod internal { #[cfg(test)] mod tests { - use super::internal::UnorderedElementsOfMapAreMatcher; use crate::matcher::{Matcher, MatcherResult}; use crate::prelude::*; use indoc::indoc; @@ -1082,13 +1083,13 @@ mod tests { // we must create the constitute matchers separately so that they // aren't dropped too early. let matchers = ((eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))); - let matcher: UnorderedElementsOfMapAreMatcher, _, _, 3> = unordered_elements_are![ + let matcher = unordered_elements_are![ (matchers.0.0, matchers.0.1), (matchers.1.0, matchers.1.1), (matchers.2.0, matchers.2.1) ]; verify_that!( - Matcher::describe(&matcher, MatcherResult::Match), + Matcher::>::describe(&matcher, MatcherResult::Match), displays_as(eq(indoc!( " contains elements matching in any order: @@ -1107,7 +1108,7 @@ mod tests { // we must create the constitute matchers separately so that they // aren't dropped too early. let matchers = ((anything(), eq(1)), (anything(), eq(2)), (anything(), eq(2))); - let matcher: UnorderedElementsOfMapAreMatcher, _, _, 3> = unordered_elements_are![ + let matcher = unordered_elements_are![ (matchers.0.0, matchers.0.1), (matchers.1.0, matchers.1.1), (matchers.2.0, matchers.2.1), @@ -1115,13 +1116,17 @@ mod tests { let value: HashMap = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]); verify_that!( matcher.explain_match(&value), - displays_as(contains_regex( - "Actual element 2 => 2 at index [0-2] matched expected element `is anything` => `is equal to 2` at index [0-2]." - )).and(displays_as(contains_regex( - "Actual element [0-1] => [0-1] at index [0-2] did not match any remaining expected element." - ))).and(displays_as(contains_substring( - "Expected element `is anything` => `is equal to 2` at index 2 did not match any remaining actual element." - ))) + all![ + displays_as(contains_regex( + "Actual element 2 => 2 at index [0-2] matched expected element `is anything` => `is equal to 2` at index [0-2]." + )), + displays_as(contains_regex( + "Actual element [0-1] => [0-1] at index [0-2] did not match any remaining expected element." + )), + displays_as(contains_substring( + "Expected element `is anything` => `is equal to 2` at index 2 did not match any remaining actual element." + )) + ] ) } } diff --git a/googletest/tests/elements_are_matcher_test.rs b/googletest/tests/elements_are_matcher_test.rs index 4de23144..510c08ee 100644 --- a/googletest/tests/elements_are_matcher_test.rs +++ b/googletest/tests/elements_are_matcher_test.rs @@ -109,7 +109,7 @@ fn elements_are_explain_match_wrong_size() -> Result<()> { ) } -fn create_matcher() -> impl Matcher> { +fn create_matcher<'a>() -> impl Matcher<'a, Vec> { elements_are![eq(1)] } diff --git a/googletest/tests/tuple_matcher_test.rs b/googletest/tests/tuple_matcher_test.rs index a93ffee4..a79b9703 100644 --- a/googletest/tests/tuple_matcher_test.rs +++ b/googletest/tests/tuple_matcher_test.rs @@ -176,7 +176,7 @@ fn tuple_matcher_with_trailing_comma_matches_matching_12_tuple() -> Result<()> { #[test] fn tuple_matcher_1_has_correct_description_for_match() -> Result<()> { verify_that!( - (eq::(1),).describe(MatcherResult::Match), + Matcher::<(i32,)>::describe(&(eq(1),), MatcherResult::Match), displays_as(eq(indoc!( " is a tuple whose values respectively match: @@ -188,7 +188,7 @@ fn tuple_matcher_1_has_correct_description_for_match() -> Result<()> { #[test] fn tuple_matcher_1_has_correct_description_for_mismatch() -> Result<()> { verify_that!( - (eq::(1),).describe(MatcherResult::NoMatch), + Matcher::<(i32,)>::describe(&(eq(1),), MatcherResult::NoMatch), displays_as(eq(indoc!( " is a tuple whose values do not respectively match: @@ -200,7 +200,7 @@ fn tuple_matcher_1_has_correct_description_for_mismatch() -> Result<()> { #[test] fn tuple_matcher_2_has_correct_description_for_match() -> Result<()> { verify_that!( - (eq::(1), eq::(2)).describe(MatcherResult::Match), + Matcher::<(i32, i32)>::describe(&(eq(1), eq(2)), MatcherResult::Match), displays_as(eq(indoc!( " is a tuple whose values respectively match: @@ -213,7 +213,7 @@ fn tuple_matcher_2_has_correct_description_for_match() -> Result<()> { #[test] fn tuple_matcher_2_has_correct_description_for_mismatch() -> Result<()> { verify_that!( - (eq::(1), eq::(2)).describe(MatcherResult::NoMatch), + Matcher::<(i32, i32)>::describe(&(eq(1), eq(2)), MatcherResult::NoMatch), displays_as(eq(indoc!( " is a tuple whose values do not respectively match: diff --git a/googletest/tests/unordered_elements_are_matcher_test.rs b/googletest/tests/unordered_elements_are_matcher_test.rs index bd614175..e5599ad8 100644 --- a/googletest/tests/unordered_elements_are_matcher_test.rs +++ b/googletest/tests/unordered_elements_are_matcher_test.rs @@ -208,7 +208,7 @@ fn unordered_elements_are_unmatchable_actual_description_mismatch() -> Result<() ) } -fn create_matcher() -> impl Matcher> { +fn create_matcher<'a>() -> impl Matcher<'a, Vec> { unordered_elements_are![eq(1)] } @@ -217,7 +217,7 @@ fn unordered_elements_are_works_when_matcher_is_created_in_subroutine() -> Resul verify_that!(vec![1], create_matcher()) } -fn create_matcher_for_map() -> impl Matcher> { +fn create_matcher_for_map<'a>() -> impl Matcher<'a, HashMap> { unordered_elements_are![(eq(1), eq(1))] } @@ -352,7 +352,7 @@ fn is_contained_in_matches_hash_map_with_trailing_comma() -> Result<()> { #[test] fn is_contained_in_matches_when_container_is_empty() -> Result<()> { - verify_that!(vec![], is_contained_in!(eq::(2), eq(3), eq(4))) + verify_that!(vec![1; 0], is_contained_in!(eq(2), eq(3), eq(4))) } #[test] diff --git a/googletest_macro/src/lib.rs b/googletest_macro/src/lib.rs index 1f9a2587..02ff64e9 100644 --- a/googletest_macro/src/lib.rs +++ b/googletest_macro/src/lib.rs @@ -13,7 +13,7 @@ // limitations under the License. use quote::quote; -use syn::{parse_macro_input, Attribute, ItemFn, ReturnType}; +use syn::{parse_macro_input, Attribute, DeriveInput, ItemFn, ReturnType}; /// Marks a test to be run by the Google Rust test runner. /// @@ -163,3 +163,15 @@ fn is_test_attribute(attr: &Attribute) -> bool { && last_segment.ident == "rstest" && attr.path().segments.len() <= 2) } + +#[proc_macro_derive(MatcherExt)] +pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + // eprintln!("{ast:#?}"); + let DeriveInput { ident, generics, .. } = ast; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote! { + impl #impl_generics MatcherExt for #ident #ty_generics #where_clause {} + } + .into() +}