From 4b67c374d49b00860a8f285aae2e064cc58a0dc7 Mon Sep 17 00:00:00 2001 From: Bastien Jacot-Guillarmod Date: Fri, 12 Jan 2024 14:22:46 +0100 Subject: [PATCH 1/2] Make `ActualT` in `Matcher` a generic type parameter. --- README.md | 6 +- googletest/crate_docs.md | 36 +++-- googletest/src/assertions.rs | 2 +- googletest/src/lib.rs | 2 +- googletest/src/matcher.rs | 42 +++--- googletest/src/matchers/all_matcher.rs | 12 +- googletest/src/matchers/any_matcher.rs | 10 +- googletest/src/matchers/anything_matcher.rs | 15 +- googletest/src/matchers/char_count_matcher.rs | 27 ++-- .../src/matchers/conjunction_matcher.rs | 14 +- .../src/matchers/container_eq_matcher.rs | 46 +++--- googletest/src/matchers/contains_matcher.rs | 46 +++--- .../src/matchers/contains_regex_matcher.rs | 26 ++-- .../src/matchers/disjunction_matcher.rs | 14 +- googletest/src/matchers/display_matcher.rs | 19 ++- googletest/src/matchers/each_matcher.rs | 24 ++-- .../src/matchers/elements_are_matcher.rs | 20 ++- googletest/src/matchers/empty_matcher.rs | 20 +-- .../src/matchers/eq_deref_of_matcher.rs | 18 +-- googletest/src/matchers/eq_matcher.rs | 18 ++- googletest/src/matchers/err_matcher.rs | 33 ++--- googletest/src/matchers/field_matcher.rs | 15 +- googletest/src/matchers/ge_matcher.rs | 20 ++- googletest/src/matchers/gt_matcher.rs | 24 ++-- googletest/src/matchers/has_entry_matcher.rs | 20 +-- .../src/matchers/is_encoded_string_matcher.rs | 36 +++-- googletest/src/matchers/is_matcher.rs | 25 ++-- googletest/src/matchers/is_nan_matcher.rs | 15 +- googletest/src/matchers/le_matcher.rs | 20 ++- googletest/src/matchers/len_matcher.rs | 29 ++-- googletest/src/matchers/lt_matcher.rs | 20 ++- .../src/matchers/matches_regex_matcher.rs | 30 ++-- googletest/src/matchers/near_matcher.rs | 7 +- googletest/src/matchers/none_matcher.rs | 20 ++- googletest/src/matchers/not_matcher.rs | 18 +-- googletest/src/matchers/ok_matcher.rs | 33 ++--- googletest/src/matchers/points_to_matcher.rs | 24 +--- googletest/src/matchers/pointwise_matcher.rs | 18 ++- googletest/src/matchers/predicate_matcher.rs | 36 ++--- googletest/src/matchers/property_matcher.rs | 38 ++--- googletest/src/matchers/some_matcher.rs | 26 ++-- googletest/src/matchers/str_matcher.rs | 135 ++++++++---------- googletest/src/matchers/subset_of_matcher.rs | 26 ++-- .../src/matchers/superset_of_matcher.rs | 24 ++-- googletest/src/matchers/tuple_matcher.rs | 22 +-- .../unordered_elements_are_matcher.rs | 78 +++++----- googletest/tests/elements_are_matcher_test.rs | 2 +- googletest/tests/tuple_matcher_test.rs | 8 +- .../unordered_elements_are_matcher_test.rs | 6 +- googletest_macro/src/lib.rs | 14 +- 50 files changed, 539 insertions(+), 700 deletions(-) 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..d63fac92 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 Matcher for MyEqMatcher { + fn matches(&self, actual: &T) -> MatcherResult { 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 Matcher for MyEqMatcher { + # fn matches(&self, actual: &T) -> MatcherResult { # 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(expected: T) -> impl Matcher { 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 Matcher for MyEqMatcher { +# fn matches(&self, actual: &T) -> MatcherResult { # 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(expected: T) -> impl Matcher { # 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..c589bbfd 100644 --- a/googletest/src/assertions.rs +++ b/googletest/src/assertions.rs @@ -499,7 +499,7 @@ pub mod internal { #[must_use = "The assertion result must be evaluated to affect the test result."] pub fn check_matcher( actual: &T, - expected: impl Matcher, + expected: impl Matcher, 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..cd9f994b 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 { /// 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(&self, actual: &ActualT) -> MatcherResult; /// 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(&self, actual: &ActualT) -> Description { 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, { @@ -215,7 +219,7 @@ 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, + matcher: &impl Matcher, actual: &T, actual_expr: &'static str, source_location: SourceLocation, @@ -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> Matcher for &M { + fn matches(&self, actual: &T) -> MatcherResult { (*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(&self, actual: &T) -> Description { (*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..5265bff3 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,15 +32,14 @@ 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); - -impl Matcher for Anything { - type ActualT = T; +#[derive(MatcherExt)] +pub struct Anything; +impl Matcher for Anything { fn matches(&self, _: &T) -> MatcherResult { MatcherResult::Match } diff --git a/googletest/src/matchers/char_count_matcher.rs b/googletest/src/matchers/char_count_matcher.rs index 70977d74..1b0df967 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,20 +56,16 @@ 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>(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; - +impl, E: Matcher> Matcher for CharLenMatcher { fn matches(&self, actual: &T) -> MatcherResult { self.expected.matches(&actual.as_ref().chars().count()) } @@ -108,7 +104,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,10 +125,10 @@ mod tests { #[test] fn char_count_explains_match() -> Result<()> { - struct TestMatcher(PhantomData); - impl Matcher for TestMatcher { - type ActualT = T; + #[derive(MatcherExt)] + struct TestMatcher; + impl Matcher for TestMatcher { fn matches(&self, _: &T) -> MatcherResult { false.into() } @@ -147,7 +142,7 @@ mod tests { } } 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..a8674e9b 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,15 @@ impl ConjunctionMatcher { } } -impl> Matcher for ConjunctionMatcher -where - M1::ActualT: Debug, -{ - type ActualT = M1::ActualT; - - fn matches(&self, actual: &M1::ActualT) -> MatcherResult { +impl, M2: Matcher> Matcher for ConjunctionMatcher { + fn matches(&self, actual: &T) -> MatcherResult { 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(&self, actual: &T) -> Description { 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..787aec2e 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 + Matcher for ContainerEqMatcher where ActualElementT: PartialEq + Debug + ?Sized, ActualContainerT: PartialEq + Debug + ?Sized, @@ -114,8 +112,6 @@ where for<'a> &'a ActualContainerT: IntoIterator, for<'a> &'a ExpectedContainerT: IntoIterator, { - type ActualT = ActualContainerT; - fn matches(&self, actual: &ActualContainerT) -> MatcherResult { (*actual == self.expected).into() } @@ -132,19 +128,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..d9b8ebf7 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>>, } -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 Matcher + '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, ContainerT: Debug> Matcher + for ContainsMatcher where for<'a> &'a ContainerT: IntoIterator, { - type ActualT = ContainerT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches(&self, actual: &ContainerT) -> MatcherResult { 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(&self, actual: &ContainerT) -> Description { 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 { +impl ContainsMatcher { fn count_matches(&self, actual: &ContainerT) -> usize where for<'b> &'b ContainerT: IntoIterator, - InnerMatcherT: Matcher, + InnerMatcherT: Matcher, { 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..272cdf1f 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,14 +60,12 @@ 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; - +impl + Debug + ?Sized> Matcher for ContainsRegexMatcher { fn matches(&self, actual: &ActualT) -> MatcherResult { 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..6c6f559b 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,15 @@ impl DisjunctionMatcher { } } -impl> Matcher for DisjunctionMatcher -where - M1::ActualT: Debug, -{ - type ActualT = M1::ActualT; - - fn matches(&self, actual: &M1::ActualT) -> MatcherResult { +impl, M2: Matcher> Matcher for DisjunctionMatcher { + fn matches(&self, actual: &T) -> MatcherResult { 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(&self, actual: &T) -> Description { 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..c4f7bbe1 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,22 +22,20 @@ use std::marker::PhantomData; /// let result: impl Display = ...; /// verify_that!(result, displays_as(eq(format!("{}", result))))?; /// ``` -pub fn displays_as>( +pub fn displays_as>( 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> Matcher + for DisplayMatcher { - type ActualT = T; - fn matches(&self, actual: &T) -> MatcherResult { self.inner.matches(&format!("{actual}")) } diff --git a/googletest/src/matchers/each_matcher.rs b/googletest/src/matchers/each_matcher.rs index 08a07426..f6c39fb6 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,28 +61,20 @@ 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 Matcher for EachMatcher where for<'a> &'a ActualT: IntoIterator, - MatcherT: Matcher, + MatcherT: Matcher, { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { for element in actual { 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..3e71934c 100644 --- a/googletest/src/matchers/elements_are_matcher.rs +++ b/googletest/src/matchers/elements_are_matcher.rs @@ -93,35 +93,33 @@ 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, T: Debug> { + elements: Vec + 'a>>, } - impl<'a, ContainerT: ?Sized, T: Debug> ElementsAre<'a, ContainerT, T> { + impl<'a, T: Debug> ElementsAre<'a, 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, T: Debug, ContainerT: Debug + ?Sized> Matcher for ElementsAre<'a, T> where for<'b> &'b ContainerT: IntoIterator, { - type ActualT = ContainerT; - fn matches(&self, actual: &ContainerT) -> MatcherResult { let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter()); for (a, e) in zipped_iterator.by_ref() { diff --git a/googletest/src/matchers/empty_matcher.rs b/googletest/src/matchers/empty_matcher.rs index 11cb6758..99c9544a 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,23 +50,17 @@ 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 Matcher for EmptyMatcher where for<'a> &'a T: IntoIterator, { - type ActualT = T; - fn matches(&self, actual: &T) -> MatcherResult { 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..293f844d 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,28 +51,24 @@ 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 Matcher for EqDerefOfMatcher where ActualT: Debug + ?Sized, ExpectedRefT: Deref + Debug, ExpectedT: PartialEq + Debug, { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { (self.expected.deref() == actual).into() } diff --git a/googletest/src/matchers/eq_matcher.rs b/googletest/src/matchers/eq_matcher.rs index 26fea831..3ff535ad 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,21 +71,19 @@ 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; - +impl> Matcher for EqMatcher { fn matches(&self, actual: &A) -> MatcherResult { (*actual == self.expected).into() } @@ -100,7 +98,7 @@ impl> Matcher for EqMatcher { fn explain_match(&self, actual: &A) -> Description { let expected_debug = format!("{:#?}", self.expected); let actual_debug = format!("{:#?}", actual); - let description = self.describe(self.matches(actual)); + let description = Matcher::::describe(self, self.matches(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..016b4427 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> Matcher> + for ErrMatcher { - type ActualT = std::result::Result; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches(&self, actual: &std::result::Result) -> MatcherResult { 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(&self, actual: &std::result::Result) -> Description { 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..570cc102 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,25 +152,28 @@ pub mod internal { /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub fn field_matcher>( + pub fn field_matcher>( 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> MatcherExt for FieldMatcher { - type ActualT = OuterT; + } + impl> Matcher + for FieldMatcher + { fn matches(&self, actual: &OuterT) -> MatcherResult { if let Some(value) = (self.field_accessor)(actual) { self.inner.matches(value) diff --git a/googletest/src/matchers/ge_matcher.rs b/googletest/src/matchers/ge_matcher.rs index cb2a91ff..5a435b0c 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,22 +74,18 @@ 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, ExpectedT: Debug> Matcher + for GeMatcher { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { (*actual >= self.expected).into() } diff --git a/googletest/src/matchers/gt_matcher.rs b/googletest/src/matchers/gt_matcher.rs index b52c6e3d..debb6b37 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,22 +74,18 @@ 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, ExpectedT: Debug> Matcher + for GtMatcher { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { (*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..28219876 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,24 +61,19 @@ 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> + Matcher> for HasEntryMatcher { - type ActualT = HashMap; - fn matches(&self, actual: &HashMap) -> MatcherResult { if let Some(value) = actual.get(&self.key) { self.inner.matches(value) diff --git a/googletest/src/matchers/is_encoded_string_matcher.rs b/googletest/src/matchers/is_encoded_string_matcher.rs index d2fb2596..f2f37f2a 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: Matcher, { - 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 + for IsEncodedStringMatcher where - InnerMatcherT: Matcher, + InnerMatcherT: Matcher, { - type ActualT = ActualT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches(&self, actual: &ActualT) -> MatcherResult { 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(&self, actual: &ActualT) -> Description { 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..edc46e33 100644 --- a/googletest/src/matchers/is_matcher.rs +++ b/googletest/src/matchers/is_matcher.rs @@ -16,34 +16,29 @@ 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, ActualT: Debug, InnerMatcherT: Matcher> Matcher + for IsMatcher<'a, InnerMatcherT> { - type ActualT = ActualT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches(&self, actual: &ActualT) -> MatcherResult { self.inner.matches(actual) } @@ -64,7 +59,7 @@ impl<'a, ActualT: Debug, InnerMatcherT: Matcher> Matcher } } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match(&self, actual: &ActualT) -> Description { 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..ec5aa7f8 100644 --- a/googletest/src/matchers/is_nan_matcher.rs +++ b/googletest/src/matchers/is_nan_matcher.rs @@ -14,21 +14,20 @@ 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); - -impl Matcher for IsNanMatcher { - type ActualT = T; +#[derive(MatcherExt)] +pub struct IsNanMatcher; +impl Matcher for IsNanMatcher { fn matches(&self, actual: &T) -> MatcherResult { actual.is_nan().into() } diff --git a/googletest/src/matchers/le_matcher.rs b/googletest/src/matchers/le_matcher.rs index cfc57818..c6503817 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,22 +74,18 @@ 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, ExpectedT: Debug> Matcher + for LeMatcher { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { (*actual <= self.expected).into() } diff --git a/googletest/src/matchers/len_matcher.rs b/googletest/src/matchers/len_matcher.rs index be903c99..26a809da 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,24 +49,19 @@ 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> Matcher for LenMatcher where for<'a> &'a T: IntoIterator, { - type ActualT = T; - fn matches(&self, actual: &T) -> MatcherResult { self.expected.matches(&count_elements(actual)) } @@ -101,7 +96,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,10 +172,9 @@ mod tests { #[test] fn len_matcher_explain_match() -> Result<()> { - struct TestMatcher(PhantomData); - impl Matcher for TestMatcher { - type ActualT = T; - + #[derive(MatcherExt)] + struct TestMatcher; + impl Matcher for TestMatcher { fn matches(&self, _: &T) -> MatcherResult { false.into() } @@ -195,7 +188,7 @@ mod tests { } } 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..4cce2ff2 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,22 +74,18 @@ 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, ExpectedT: Debug> Matcher + for LtMatcher { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { (*actual < self.expected).into() } diff --git a/googletest/src/matchers/matches_regex_matcher.rs b/googletest/src/matchers/matches_regex_matcher.rs index 32b053b4..c57d7fa0 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 Matcher for MatchesRegexMatcher where PatternT: Deref, ActualT: AsRef + Debug + ?Sized, { - type ActualT = ActualT; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches(&self, actual: &ActualT) -> MatcherResult { 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..bffd672e 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,9 +167,7 @@ impl NearMatcher { } } -impl Matcher for NearMatcher { - type ActualT = T; - +impl Matcher for NearMatcher { fn matches(&self, actual: &T) -> MatcherResult { 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..336dcbef 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,17 +31,14 @@ 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; +impl Matcher> for NoneMatcher { fn matches(&self, actual: &Option) -> MatcherResult { (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..f5b79b88 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,20 +33,16 @@ 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; - +impl> Matcher for NotMatcher { fn matches(&self, actual: &T) -> MatcherResult { match self.inner.matches(actual) { MatcherResult::Match => MatcherResult::NoMatch, diff --git a/googletest/src/matchers/ok_matcher.rs b/googletest/src/matchers/ok_matcher.rs index 8425b93a..95a6d54c 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,23 @@ 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> Matcher> + for OkMatcher { - type ActualT = std::result::Result; - - fn matches(&self, actual: &Self::ActualT) -> MatcherResult { + fn matches(&self, actual: &std::result::Result) -> MatcherResult { actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch) } - fn explain_match(&self, actual: &Self::ActualT) -> Description { + fn explain_match(&self, actual: &std::result::Result) -> Description { match actual { Ok(o) => { Description::new().text("which is a success").nested(self.inner.explain_match(o)) @@ -141,13 +136,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..b990b681 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,30 +31,21 @@ 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 Matcher for PointsToMatcher where ExpectedT: Debug, - MatcherT: Matcher, + MatcherT: Matcher, ActualT: Deref + Debug + ?Sized, { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { self.expected.matches(actual.deref()) } diff --git a/googletest/src/matchers/pointwise_matcher.rs b/googletest/src/matchers/pointwise_matcher.rs index 01e70c0d..b74f1d22 100644 --- a/googletest/src/matchers/pointwise_matcher.rs +++ b/googletest/src/matchers/pointwise_matcher.rs @@ -152,32 +152,30 @@ 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, ContainerT: ?Sized + Debug> Matcher + for PointwiseMatcher where for<'b> &'b ContainerT: IntoIterator, { - type ActualT = ContainerT; - fn matches(&self, actual: &ContainerT) -> MatcherResult { let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter()); for (element, matcher) in zipped_iterator.by_ref() { diff --git a/googletest/src/matchers/predicate_matcher.rs b/googletest/src/matchers/predicate_matcher.rs index 5bc067d7..bfffd1eb 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,12 +118,10 @@ where #[doc(hidden)] pub struct NoDescription; -impl Matcher for PredicateMatcher +impl Matcher for PredicateMatcher where for<'a> P: Fn(&'a T) -> bool, { - type ActualT = T; - fn matches(&self, actual: &T) -> MatcherResult { (self.predicate)(actual).into() } @@ -142,13 +134,11 @@ where } } -impl Matcher - for PredicateMatcher +impl Matcher + for PredicateMatcher where for<'a> P: Fn(&'a T) -> bool, { - type ActualT = T; - fn matches(&self, actual: &T) -> MatcherResult { (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() -> impl Matcher { 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() -> impl Matcher { predicate(|x| x % 2 == 0) } diff --git a/googletest/src/matchers/property_matcher.rs b/googletest/src/matchers/property_matcher.rs index 19b48627..b39a1b08 100644 --- a/googletest/src/matchers/property_matcher.rs +++ b/googletest/src/matchers/property_matcher.rs @@ -136,36 +136,39 @@ 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< + OuterT: Debug, + InnerT: Debug, + MatcherT: Matcher, + 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 Matcher for PropertyMatcher where InnerT: Debug, OuterT: Debug, ExtractorT: Fn(&OuterT) -> InnerT, - MatcherT: Matcher, + MatcherT: Matcher, { - type ActualT = OuterT; - fn matches(&self, actual: &OuterT) -> MatcherResult { self.inner.matches(&(self.extractor)(actual)) } @@ -197,26 +200,25 @@ pub mod internal { extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, - ) -> impl Matcher + ) -> PropertyRefMatcher where OuterT: Debug, InnerT: Debug + ?Sized, - MatcherT: Matcher, + MatcherT: Matcher, { 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> Matcher for PropertyRefMatcher { - type ActualT = OuterT; - fn matches(&self, actual: &OuterT) -> MatcherResult { self.inner.matches((self.extractor)(actual)) } diff --git a/googletest/src/matchers/some_matcher.rs b/googletest/src/matchers/some_matcher.rs index 905aa17d..5d1008cb 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,18 +38,16 @@ 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; - +impl> Matcher> for SomeMatcher { fn matches(&self, actual: &Option) -> MatcherResult { actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch) } @@ -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..03d09d26 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 Matcher + 'static) -> StrMatcher; } /// A matcher which matches equality or containment of a string-like value in a @@ -287,19 +280,17 @@ 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 Matcher for StrMatcher where ExpectedT: Deref + Debug, ActualT: AsRef + Debug + ?Sized, { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { self.configuration.do_strings_match(self.expected.deref(), actual.as_ref()).into() } @@ -313,10 +304,10 @@ where } } -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 Matcher + '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>>, } #[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 Matcher + '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..464de486 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,29 +83,21 @@ 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 Matcher + for SubsetOfMatcher where for<'a> &'a ActualT: IntoIterator, for<'a> &'a ExpectedT: IntoIterator, { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { for actual_item in actual { if self.expected_is_missing(actual_item) { @@ -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..bb1f99fa 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,29 +84,21 @@ 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 Matcher + for SupersetOfMatcher where for<'a> &'a ActualT: IntoIterator, for<'a> &'a ExpectedT: IntoIterator, { - type ActualT = ActualT; - fn matches(&self, actual: &ActualT) -> MatcherResult { for expected_item in &self.subset { if actual_is_missing(actual, expected_item) { diff --git a/googletest/src/matchers/tuple_matcher.rs b/googletest/src/matchers/tuple_matcher.rs index af55cbf6..531795b3 100644 --- a/googletest/src/matchers/tuple_matcher.rs +++ b/googletest/src/matchers/tuple_matcher.rs @@ -23,16 +23,16 @@ 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 Matcher<()> for () { + fn matches(&self, _: &()) -> MatcherResult { MatcherResult::Match } @@ -50,12 +50,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<$($field_type: Debug, $matcher_type: Matcher<$field_type>),*> + Matcher<($($field_type,)*)> for ($($matcher_type,)*) + { + fn matches(&self, actual: & ($($field_type,)*)) -> MatcherResult { $(match self.$field_number.matches(&actual.$field_number) { MatcherResult::Match => {}, MatcherResult::NoMatch => { @@ -65,7 +65,7 @@ pub mod internal { MatcherResult::Match } - fn explain_match(&self, actual: &($($field_type,)*)) -> Description { + fn explain_match(&self, actual: & ($($field_type,)*)) -> Description { 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..d77162c6 100644 --- a/googletest/src/matchers/unordered_elements_are_matcher.rs +++ b/googletest/src/matchers/unordered_elements_are_matcher.rs @@ -366,31 +366,25 @@ 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, 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> - { - pub fn new( - elements: [Box + 'a>; N], - requirements: Requirements, - ) -> Self { - Self { elements, requirements, phantom: Default::default() } + impl<'a, T: Debug, const N: usize> UnorderedElementsAreMatcher<'a, T, N> { + pub fn new(elements: [Box + 'a>; N], requirements: Requirements) -> Self { + Self { elements, requirements } } } @@ -403,13 +397,11 @@ 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, T: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher + for UnorderedElementsAreMatcher<'a, T, N> where for<'b> &'b ContainerT: IntoIterator, { - type ActualT = ContainerT; - fn matches(&self, actual: &ContainerT) -> MatcherResult { let match_matrix = MatchMatrix::generate(actual, &self.elements); match_matrix.is_match_for(self.requirements).into() @@ -451,42 +443,43 @@ pub mod internal { } type KeyValueMatcher<'a, KeyT, ValueT> = - (Box + 'a>, Box + 'a>); + (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, KeyT, ValueT, const N: usize> where - ContainerT: ?Sized, KeyT: Debug, ValueT: Debug, { elements: [KeyValueMatcher<'a, 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, KeyT: Debug, ValueT: Debug, const N: usize> + UnorderedElementsOfMapAreMatcher<'a, KeyT, ValueT, N> { pub fn new( elements: [KeyValueMatcher<'a, 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> + impl<'a, KeyT: Debug, ValueT: Debug, const N: usize> MatcherExt + for UnorderedElementsOfMapAreMatcher<'a, KeyT, ValueT, N> + { + } + + impl<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize> + Matcher for UnorderedElementsOfMapAreMatcher<'a, KeyT, ValueT, N> where for<'b> &'b ContainerT: IntoIterator, { - type ActualT = ContainerT; - fn matches(&self, actual: &ContainerT) -> MatcherResult { let match_matrix = MatchMatrix::generate_for_map(actual, &self.elements); match_matrix.is_match_for(self.requirements).into() @@ -603,7 +596,7 @@ pub mod internal { impl MatchMatrix { fn generate<'a, T: Debug + 'a, ContainerT: Debug + ?Sized>( actual: &ContainerT, - expected: &[Box + 'a>; N], + expected: &[Box + 'a>; N], ) -> Self where for<'b> &'b ContainerT: IntoIterator, @@ -962,7 +955,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 @@ -1068,7 +1061,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 +1074,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 +1099,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 +1107,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..8dc7e3f1 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() -> impl Matcher> { 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..05dd3c82 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() -> impl Matcher> { 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() -> impl Matcher> { 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() +} From 867b85f85b0e8cddf6b48ee35a924892fcc578ff Mon Sep 17 00:00:00 2001 From: Bastien Jacot-Guillarmod Date: Fri, 23 Feb 2024 18:17:34 +0100 Subject: [PATCH 2/2] draft --- googletest/crate_docs.md | 16 ++-- googletest/src/assertions.rs | 6 +- googletest/src/matcher.rs | 18 ++--- googletest/src/matchers/anything_matcher.rs | 7 +- googletest/src/matchers/char_count_matcher.rs | 28 +++++-- .../src/matchers/conjunction_matcher.rs | 14 +++- .../src/matchers/container_eq_matcher.rs | 14 +++- googletest/src/matchers/contains_matcher.rs | 16 ++-- .../src/matchers/contains_regex_matcher.rs | 4 +- .../src/matchers/disjunction_matcher.rs | 14 +++- googletest/src/matchers/display_matcher.rs | 10 +-- googletest/src/matchers/each_matcher.rs | 15 +++- .../src/matchers/elements_are_matcher.rs | 16 ++-- googletest/src/matchers/empty_matcher.rs | 4 +- .../src/matchers/eq_deref_of_matcher.rs | 6 +- googletest/src/matchers/eq_matcher.rs | 8 +- googletest/src/matchers/err_matcher.rs | 6 +- googletest/src/matchers/field_matcher.rs | 10 +-- googletest/src/matchers/ge_matcher.rs | 4 +- googletest/src/matchers/gt_matcher.rs | 4 +- googletest/src/matchers/has_entry_matcher.rs | 14 +++- .../src/matchers/is_encoded_string_matcher.rs | 10 +-- googletest/src/matchers/is_matcher.rs | 12 ++- googletest/src/matchers/is_nan_matcher.rs | 4 +- googletest/src/matchers/le_matcher.rs | 7 +- googletest/src/matchers/len_matcher.rs | 18 +++-- googletest/src/matchers/lt_matcher.rs | 4 +- .../src/matchers/matches_regex_matcher.rs | 4 +- googletest/src/matchers/near_matcher.rs | 4 +- googletest/src/matchers/none_matcher.rs | 4 +- googletest/src/matchers/not_matcher.rs | 12 ++- googletest/src/matchers/ok_matcher.rs | 12 ++- googletest/src/matchers/points_to_matcher.rs | 8 +- googletest/src/matchers/pointwise_matcher.rs | 12 ++- googletest/src/matchers/predicate_matcher.rs | 16 ++-- googletest/src/matchers/property_matcher.rs | 22 +++--- googletest/src/matchers/some_matcher.rs | 6 +- googletest/src/matchers/str_matcher.rs | 14 ++-- googletest/src/matchers/subset_of_matcher.rs | 6 +- .../src/matchers/superset_of_matcher.rs | 6 +- googletest/src/matchers/tuple_matcher.rs | 15 ++-- .../unordered_elements_are_matcher.rs | 73 +++++++++++-------- googletest/tests/elements_are_matcher_test.rs | 2 +- .../unordered_elements_are_matcher_test.rs | 4 +- 44 files changed, 303 insertions(+), 206 deletions(-) diff --git a/googletest/crate_docs.md b/googletest/crate_docs.md index d63fac92..7acde881 100644 --- a/googletest/crate_docs.md +++ b/googletest/crate_docs.md @@ -217,8 +217,8 @@ struct MyEqMatcher { expected: T, } -impl Matcher for MyEqMatcher { - fn matches(&self, actual: &T) -> 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 { @@ -249,8 +249,8 @@ impl Matcher for MyEqMatcher { # expected: T, # } # - # impl Matcher for MyEqMatcher { - # fn matches(&self, actual: &T) -> 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 { @@ -270,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 } } ``` @@ -286,8 +286,8 @@ impl Matcher for MyEqMatcher { # expected: T, # } # -# impl Matcher for MyEqMatcher { -# fn matches(&self, actual: &T) -> 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 { @@ -307,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. diff --git a/googletest/src/assertions.rs b/googletest/src/assertions.rs index c589bbfd..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/matcher.rs b/googletest/src/matcher.rs index cd9f994b..f9ba7041 100644 --- a/googletest/src/matcher.rs +++ b/googletest/src/matcher.rs @@ -27,14 +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 { +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: &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`. @@ -135,7 +135,7 @@ pub trait Matcher { /// .nested(self.expected.explain_match(actual.deref())) /// } /// ``` - 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))).into() } } @@ -218,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 { @@ -279,8 +279,8 @@ impl MatcherResult { impl MatcherExt for &M {} -impl> Matcher for &M { - fn matches(&self, actual: &T) -> 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) } @@ -288,7 +288,7 @@ impl> Matcher for &M { (*self).describe(matcher_result) } - fn explain_match(&self, actual: &T) -> Description { + fn explain_match<'b>(&self, actual: &'b T) -> Description where 'a: 'b{ (*self).explain_match(actual) } } diff --git a/googletest/src/matchers/anything_matcher.rs b/googletest/src/matchers/anything_matcher.rs index 5265bff3..51a970e8 100644 --- a/googletest/src/matchers/anything_matcher.rs +++ b/googletest/src/matchers/anything_matcher.rs @@ -39,8 +39,11 @@ pub fn anything() -> Anything { #[derive(MatcherExt)] pub struct Anything; -impl Matcher for Anything { - 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 1b0df967..b27f728c 100644 --- a/googletest/src/matchers/char_count_matcher.rs +++ b/googletest/src/matchers/char_count_matcher.rs @@ -56,7 +56,7 @@ use std::fmt::Debug; /// # } /// # should_pass().unwrap(); /// ``` -pub fn char_count>(expected: E) -> CharLenMatcher { +pub fn char_count Matcher<'a, usize>>(expected: E) -> CharLenMatcher { CharLenMatcher { expected } } @@ -65,8 +65,13 @@ pub struct CharLenMatcher { expected: E, } -impl, E: Matcher> Matcher for CharLenMatcher { - 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()) } @@ -85,7 +90,10 @@ impl, E: Matcher> Matcher for CharLenMa } } - 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 {}, {}", @@ -128,8 +136,11 @@ mod tests { #[derive(MatcherExt)] struct TestMatcher; - impl Matcher for TestMatcher { - fn matches(&self, _: &T) -> MatcherResult { + impl<'a, T: Debug> Matcher<'a, T> for TestMatcher { + fn matches<'b>(&self, _: &'b T) -> MatcherResult + where + 'a: 'b, + { false.into() } @@ -137,7 +148,10 @@ 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() } } diff --git a/googletest/src/matchers/conjunction_matcher.rs b/googletest/src/matchers/conjunction_matcher.rs index a8674e9b..e0a1e2cf 100644 --- a/googletest/src/matchers/conjunction_matcher.rs +++ b/googletest/src/matchers/conjunction_matcher.rs @@ -53,15 +53,23 @@ impl ConjunctionMatcher { } } -impl, M2: Matcher> Matcher for ConjunctionMatcher { - fn matches(&self, actual: &T) -> MatcherResult { +impl<'a, T: Debug + ?Sized, M1: Matcher<'a, T>, M2: Matcher<'a, T>> Matcher<'a, T> + for ConjunctionMatcher +{ + 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: &T) -> 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 787aec2e..934594db 100644 --- a/googletest/src/matchers/container_eq_matcher.rs +++ b/googletest/src/matchers/container_eq_matcher.rs @@ -102,8 +102,8 @@ pub struct ContainerEqMatcher { expected: ExpectedContainerT, } -impl - Matcher for ContainerEqMatcher +impl<'z, ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT> + Matcher<'z, ActualContainerT> for ContainerEqMatcher where ActualElementT: PartialEq + Debug + ?Sized, ActualContainerT: PartialEq + Debug + ?Sized, @@ -112,11 +112,17 @@ where for<'a> &'a ActualContainerT: IntoIterator, for<'a> &'a ExpectedContainerT: IntoIterator, { - 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() } diff --git a/googletest/src/matchers/contains_matcher.rs b/googletest/src/matchers/contains_matcher.rs index d9b8ebf7..abb9243a 100644 --- a/googletest/src/matchers/contains_matcher.rs +++ b/googletest/src/matchers/contains_matcher.rs @@ -52,7 +52,7 @@ pub fn contains(inner: InnerMatcherT) -> ContainsMatcher { inner: InnerMatcherT, - count: Option>>, + count: Option Matcher<'a, usize>>>, } impl ContainsMatcher { @@ -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,12 +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 +impl<'c, T: Debug + 'c, InnerMatcherT: Matcher<'c, T>, ContainerT: Debug + 'c> Matcher<'c, ContainerT> for ContainsMatcher where for<'a> &'a ContainerT: IntoIterator, { - fn matches(&self, actual: &ContainerT) -> 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 { @@ -102,7 +102,7 @@ where } } - fn explain_match(&self, actual: &ContainerT) -> 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(), @@ -139,10 +139,10 @@ where } impl ContainsMatcher { - fn count_matches(&self, actual: &ContainerT) -> usize + 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() { diff --git a/googletest/src/matchers/contains_regex_matcher.rs b/googletest/src/matchers/contains_regex_matcher.rs index 272cdf1f..16ae9036 100644 --- a/googletest/src/matchers/contains_regex_matcher.rs +++ b/googletest/src/matchers/contains_regex_matcher.rs @@ -65,8 +65,8 @@ pub struct ContainsRegexMatcher { regex: Regex, } -impl + Debug + ?Sized> Matcher for ContainsRegexMatcher { - 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() } diff --git a/googletest/src/matchers/disjunction_matcher.rs b/googletest/src/matchers/disjunction_matcher.rs index 6c6f559b..951b41c4 100644 --- a/googletest/src/matchers/disjunction_matcher.rs +++ b/googletest/src/matchers/disjunction_matcher.rs @@ -47,15 +47,23 @@ impl DisjunctionMatcher { } } -impl, M2: Matcher> Matcher for DisjunctionMatcher { - fn matches(&self, actual: &T) -> MatcherResult { +impl<'a, T: Debug + ?Sized, M1: Matcher<'a, T>, M2: Matcher<'a, T>> Matcher<'a, T> + for DisjunctionMatcher +{ + 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: &T) -> 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 c4f7bbe1..87268e0e 100644 --- a/googletest/src/matchers/display_matcher.rs +++ b/googletest/src/matchers/display_matcher.rs @@ -22,25 +22,25 @@ use std::fmt::{Debug, Display}; /// 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, ) -> DisplayMatcher { DisplayMatcher { inner } } #[derive(MatcherExt)] -pub struct DisplayMatcher> { +pub struct DisplayMatcher { inner: InnerMatcher, } -impl> Matcher +impl<'a, 's, T: Debug + Display, InnerMatcher: Matcher<'s, String>> Matcher<'a, T> for DisplayMatcher { - 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 f6c39fb6..4ab16779 100644 --- a/googletest/src/matchers/each_matcher.rs +++ b/googletest/src/matchers/each_matcher.rs @@ -70,12 +70,16 @@ pub struct EachMatcher { inner: MatcherT, } -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>, { - 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; @@ -84,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 3e71934c..9b1905ff 100644 --- a/googletest/src/matchers/elements_are_matcher.rs +++ b/googletest/src/matchers/elements_are_matcher.rs @@ -102,25 +102,25 @@ pub mod internal { /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] #[derive(MatcherExt)] - pub struct ElementsAre<'a, T: Debug> { - elements: Vec + 'a>>, + pub struct ElementsAre<'a, 'b, T: Debug> { + elements: Vec + 'a>>, } - impl<'a, T: Debug> ElementsAre<'a, 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 { + pub fn new(elements: Vec + 'a>>) -> Self { Self { elements } } } - impl<'a, T: Debug, ContainerT: Debug + ?Sized> Matcher for ElementsAre<'a, 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, { - 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() { @@ -134,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 99c9544a..e22f0b70 100644 --- a/googletest/src/matchers/empty_matcher.rs +++ b/googletest/src/matchers/empty_matcher.rs @@ -57,11 +57,11 @@ pub fn empty() -> EmptyMatcher { #[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, { - 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 293f844d..323c1778 100644 --- a/googletest/src/matchers/eq_deref_of_matcher.rs +++ b/googletest/src/matchers/eq_deref_of_matcher.rs @@ -63,13 +63,13 @@ pub struct EqDerefOfMatcher { pub(crate) expected: ExpectedRefT, } -impl Matcher for EqDerefOfMatcher +impl<'a, ActualT, ExpectedRefT, ExpectedT> Matcher<'a, ActualT> for EqDerefOfMatcher where ActualT: Debug + ?Sized, ExpectedRefT: Deref + Debug, ExpectedT: PartialEq + Debug, { - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ (self.expected.deref() == actual).into() } @@ -80,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 3ff535ad..a42083eb 100644 --- a/googletest/src/matchers/eq_matcher.rs +++ b/googletest/src/matchers/eq_matcher.rs @@ -83,8 +83,8 @@ pub struct EqMatcher { pub(crate) expected: T, } -impl> Matcher for EqMatcher { - 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() } @@ -95,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 = Matcher::::describe(self, 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 016b4427..ddfd24b8 100644 --- a/googletest/src/matchers/err_matcher.rs +++ b/googletest/src/matchers/err_matcher.rs @@ -47,14 +47,14 @@ pub struct ErrMatcher { inner: InnerMatcherT, } -impl> Matcher> +impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<'a, E>> Matcher<'a, std::result::Result> for ErrMatcher { - fn matches(&self, actual: &std::result::Result) -> 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: &std::result::Result) -> 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)) diff --git a/googletest/src/matchers/field_matcher.rs b/googletest/src/matchers/field_matcher.rs index 570cc102..796cf902 100644 --- a/googletest/src/matchers/field_matcher.rs +++ b/googletest/src/matchers/field_matcher.rs @@ -152,7 +152,7 @@ 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, @@ -166,15 +166,15 @@ pub mod internal { inner: InnerMatcher, } - impl> MatcherExt + impl<'a, OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<'a, InnerT>> MatcherExt for FieldMatcher { } - impl> Matcher + impl<'a, OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<'a, InnerT>> Matcher<'a, OuterT> for FieldMatcher { - fn matches(&self, actual: &OuterT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b OuterT) -> MatcherResult where 'a: 'b { if let Some(value) = (self.field_accessor)(actual) { self.inner.matches(value) } else { @@ -182,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 5a435b0c..acb37bc8 100644 --- a/googletest/src/matchers/ge_matcher.rs +++ b/googletest/src/matchers/ge_matcher.rs @@ -83,10 +83,10 @@ pub struct GeMatcher { expected: ExpectedT, } -impl, ExpectedT: Debug> Matcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> for GeMatcher { - 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 debb6b37..dbffffbe 100644 --- a/googletest/src/matchers/gt_matcher.rs +++ b/googletest/src/matchers/gt_matcher.rs @@ -83,10 +83,10 @@ pub struct GtMatcher { expected: ExpectedT, } -impl, ExpectedT: Debug> Matcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> for GtMatcher { - 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/has_entry_matcher.rs b/googletest/src/matchers/has_entry_matcher.rs index 28219876..bfd2f03c 100644 --- a/googletest/src/matchers/has_entry_matcher.rs +++ b/googletest/src/matchers/has_entry_matcher.rs @@ -71,10 +71,13 @@ pub struct HasEntryMatcher { inner: MatcherT, } -impl> - Matcher> for HasEntryMatcher +impl<'a, KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<'a, ValueT>> + Matcher<'a, HashMap> for HasEntryMatcher { - 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 { @@ -82,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 f2f37f2a..4d256429 100644 --- a/googletest/src/matchers/is_encoded_string_matcher.rs +++ b/googletest/src/matchers/is_encoded_string_matcher.rs @@ -50,7 +50,7 @@ use std::fmt::Debug; /// ``` pub fn is_utf8_string(inner: InnerMatcherT) -> IsEncodedStringMatcher where - InnerMatcherT: Matcher, + InnerMatcherT: for<'a> Matcher<'a, String>, { IsEncodedStringMatcher { inner } } @@ -60,12 +60,12 @@ pub struct IsEncodedStringMatcher { inner: InnerMatcherT, } -impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher +impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher<'a, ActualT> for IsEncodedStringMatcher where - InnerMatcherT: Matcher, + InnerMatcherT: for<'s> Matcher<'s, String>, { - fn matches(&self, actual: &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) @@ -86,7 +86,7 @@ where } } - fn explain_match(&self, actual: &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() diff --git a/googletest/src/matchers/is_matcher.rs b/googletest/src/matchers/is_matcher.rs index edc46e33..26d29c5d 100644 --- a/googletest/src/matchers/is_matcher.rs +++ b/googletest/src/matchers/is_matcher.rs @@ -35,10 +35,13 @@ pub struct IsMatcher<'a, InnerMatcherT> { inner: InnerMatcherT, } -impl<'a, ActualT: Debug, InnerMatcherT: Matcher> Matcher +impl<'a, 'b, ActualT: Debug, InnerMatcherT: Matcher<'b, ActualT>> Matcher<'b, ActualT> for IsMatcher<'a, InnerMatcherT> { - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'c>(&self, actual: &'c ActualT) -> MatcherResult + where + 'b: 'c, + { self.inner.matches(actual) } @@ -59,7 +62,10 @@ impl<'a, ActualT: Debug, InnerMatcherT: Matcher> Matcher } } - fn explain_match(&self, actual: &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 ec5aa7f8..c4bd6971 100644 --- a/googletest/src/matchers/is_nan_matcher.rs +++ b/googletest/src/matchers/is_nan_matcher.rs @@ -27,8 +27,8 @@ pub fn is_nan() -> IsNanMatcher { #[derive(MatcherExt)] pub struct IsNanMatcher; -impl Matcher for IsNanMatcher { - 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 c6503817..7596ce75 100644 --- a/googletest/src/matchers/le_matcher.rs +++ b/googletest/src/matchers/le_matcher.rs @@ -83,10 +83,13 @@ pub struct LeMatcher { expected: ExpectedT, } -impl, ExpectedT: Debug> Matcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> for LeMatcher { - 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 26a809da..fb9ac127 100644 --- a/googletest/src/matchers/len_matcher.rs +++ b/googletest/src/matchers/len_matcher.rs @@ -58,11 +58,14 @@ pub struct LenMatcher { expected: E, } -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, { - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult + where + 'c: 'b, + { self.expected.matches(&count_elements(actual)) } @@ -78,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() @@ -174,8 +180,8 @@ mod tests { fn len_matcher_explain_match() -> Result<()> { #[derive(MatcherExt)] struct TestMatcher; - impl Matcher for TestMatcher { - fn matches(&self, _: &T) -> MatcherResult { + impl<'a, T: Debug> Matcher<'a, T> for TestMatcher { + fn matches<'b>(&self, _: &'b T) -> MatcherResult where 'a: 'b{ false.into() } @@ -183,7 +189,7 @@ 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() } } diff --git a/googletest/src/matchers/lt_matcher.rs b/googletest/src/matchers/lt_matcher.rs index 4cce2ff2..02238d0b 100644 --- a/googletest/src/matchers/lt_matcher.rs +++ b/googletest/src/matchers/lt_matcher.rs @@ -83,10 +83,10 @@ pub struct LtMatcher { expected: ExpectedT, } -impl, ExpectedT: Debug> Matcher +impl<'a, ActualT: Debug + PartialOrd, ExpectedT: Debug> Matcher<'a, ActualT> for LtMatcher { - 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 c57d7fa0..31110563 100644 --- a/googletest/src/matchers/matches_regex_matcher.rs +++ b/googletest/src/matchers/matches_regex_matcher.rs @@ -78,12 +78,12 @@ pub struct MatchesRegexMatcher> { _adjusted_pattern: String, } -impl Matcher for MatchesRegexMatcher +impl<'a, PatternT, ActualT> Matcher<'a, ActualT> for MatchesRegexMatcher where PatternT: Deref, ActualT: AsRef + Debug + ?Sized, { - fn matches(&self, actual: &ActualT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b{ self.regex.is_match(actual.as_ref()).into() } diff --git a/googletest/src/matchers/near_matcher.rs b/googletest/src/matchers/near_matcher.rs index bffd672e..4873fe09 100644 --- a/googletest/src/matchers/near_matcher.rs +++ b/googletest/src/matchers/near_matcher.rs @@ -167,8 +167,8 @@ impl NearMatcher { } } -impl Matcher for NearMatcher { - 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 336dcbef..c2c00379 100644 --- a/googletest/src/matchers/none_matcher.rs +++ b/googletest/src/matchers/none_matcher.rs @@ -38,8 +38,8 @@ pub fn none() -> NoneMatcher { #[derive(MatcherExt)] pub struct NoneMatcher; -impl Matcher> for 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() } diff --git a/googletest/src/matchers/not_matcher.rs b/googletest/src/matchers/not_matcher.rs index f5b79b88..e6b39b30 100644 --- a/googletest/src/matchers/not_matcher.rs +++ b/googletest/src/matchers/not_matcher.rs @@ -42,15 +42,21 @@ pub struct NotMatcher { inner: InnerMatcherT, } -impl> Matcher for NotMatcher { - 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 95a6d54c..4a90157f 100644 --- a/googletest/src/matchers/ok_matcher.rs +++ b/googletest/src/matchers/ok_matcher.rs @@ -47,14 +47,20 @@ pub struct OkMatcher { inner: InnerMatcherT, } -impl> Matcher> +impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<'a, T>> Matcher<'a, std::result::Result> for OkMatcher { - fn matches(&self, actual: &std::result::Result) -> 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: &std::result::Result) -> 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)) diff --git a/googletest/src/matchers/points_to_matcher.rs b/googletest/src/matchers/points_to_matcher.rs index b990b681..ded946f1 100644 --- a/googletest/src/matchers/points_to_matcher.rs +++ b/googletest/src/matchers/points_to_matcher.rs @@ -40,17 +40,17 @@ pub struct PointsToMatcher { expected: MatcherT, } -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, { - 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 b74f1d22..0d3cafe1 100644 --- a/googletest/src/matchers/pointwise_matcher.rs +++ b/googletest/src/matchers/pointwise_matcher.rs @@ -171,12 +171,15 @@ pub mod internal { } } - impl, ContainerT: ?Sized + Debug> Matcher + impl<'a, T: Debug, MatcherT: Matcher<'a, T>, ContainerT: ?Sized + Debug> Matcher<'a, ContainerT> for PointwiseMatcher where for<'b> &'b ContainerT: IntoIterator, { - 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() { @@ -190,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 bfffd1eb..b56a0aed 100644 --- a/googletest/src/matchers/predicate_matcher.rs +++ b/googletest/src/matchers/predicate_matcher.rs @@ -118,11 +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, { - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ (self.predicate)(actual).into() } @@ -134,12 +134,12 @@ where } } -impl Matcher +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, { - fn matches(&self, actual: &T) -> MatcherResult { + fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{ (self.predicate)(actual).into() } @@ -158,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") } @@ -178,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 b39a1b08..78f4f9e8 100644 --- a/googletest/src/matchers/property_matcher.rs +++ b/googletest/src/matchers/property_matcher.rs @@ -142,10 +142,10 @@ pub mod internal { /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] - pub fn property_matcher< + pub fn property_matcher<'a, OuterT: Debug, InnerT: Debug, - MatcherT: Matcher, + MatcherT: Matcher<'a, InnerT>, ExtractorT: Fn(&OuterT) -> InnerT, >( extractor: ExtractorT, @@ -162,14 +162,14 @@ pub mod internal { inner: MatcherT, } - 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>, { - fn matches(&self, actual: &OuterT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b OuterT) -> MatcherResult where 'a: 'b { self.inner.matches(&(self.extractor)(actual)) } @@ -182,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 `{:#?}`, {}", @@ -196,7 +196,7 @@ 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, @@ -204,7 +204,7 @@ pub mod internal { where OuterT: Debug, InnerT: Debug + ?Sized, - MatcherT: Matcher, + MatcherT: Matcher<'a, InnerT>, { PropertyRefMatcher { extractor, property_desc, inner } } @@ -216,10 +216,10 @@ pub mod internal { inner: MatcherT, } - impl> Matcher + impl<'a, InnerT: Debug + ?Sized, OuterT: Debug, MatcherT: Matcher<'a, InnerT>> Matcher<'a, OuterT> for PropertyRefMatcher { - fn matches(&self, actual: &OuterT) -> MatcherResult { + fn matches<'b>(&self, actual: &'b OuterT) -> MatcherResult where 'a: 'b{ self.inner.matches((self.extractor)(actual)) } @@ -232,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 5d1008cb..f41f6bf3 100644 --- a/googletest/src/matchers/some_matcher.rs +++ b/googletest/src/matchers/some_matcher.rs @@ -47,12 +47,12 @@ pub struct SomeMatcher { inner: InnerMatcherT, } -impl> Matcher> for SomeMatcher { - 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)) diff --git a/googletest/src/matchers/str_matcher.rs b/googletest/src/matchers/str_matcher.rs index 03d09d26..467bf867 100644 --- a/googletest/src/matchers/str_matcher.rs +++ b/googletest/src/matchers/str_matcher.rs @@ -268,7 +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 @@ -286,12 +286,12 @@ pub struct StrMatcher { configuration: Configuration, } -impl Matcher for StrMatcher +impl<'a, ExpectedT, ActualT> Matcher<'a, ActualT> for StrMatcher where ExpectedT: Deref + Debug, ActualT: AsRef + Debug + ?Sized, { - 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() } @@ -299,7 +299,7 @@ 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()) } } @@ -333,7 +333,7 @@ impl>> StrMatcherConfigurator + '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()."); @@ -375,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)] @@ -558,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 } } } diff --git a/googletest/src/matchers/subset_of_matcher.rs b/googletest/src/matchers/subset_of_matcher.rs index 464de486..e14e8d3f 100644 --- a/googletest/src/matchers/subset_of_matcher.rs +++ b/googletest/src/matchers/subset_of_matcher.rs @@ -92,13 +92,13 @@ pub struct SubsetOfMatcher { superset: ExpectedT, } -impl Matcher +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, { - 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; @@ -107,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() diff --git a/googletest/src/matchers/superset_of_matcher.rs b/googletest/src/matchers/superset_of_matcher.rs index bb1f99fa..ca37689e 100644 --- a/googletest/src/matchers/superset_of_matcher.rs +++ b/googletest/src/matchers/superset_of_matcher.rs @@ -93,13 +93,13 @@ pub struct SupersetOfMatcher { subset: ExpectedT, } -impl Matcher +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, { - 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; @@ -108,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 531795b3..38ac5159 100644 --- a/googletest/src/matchers/tuple_matcher.rs +++ b/googletest/src/matchers/tuple_matcher.rs @@ -31,8 +31,11 @@ pub mod internal { // 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 () { - fn matches(&self, _: &()) -> MatcherResult { + impl<'a> Matcher<'a, ()> for () { + fn matches<'b>(&self, _: &'b ()) -> MatcherResult + where + 'a: 'b, + { MatcherResult::Match } @@ -52,10 +55,10 @@ pub mod internal { ($([$field_number:tt, $matcher_type:ident, $field_type:ident]),*) => { impl<$($matcher_type),*> MatcherExt for ($($matcher_type,)*){} - impl<$($field_type: Debug, $matcher_type: Matcher<$field_type>),*> - Matcher<($($field_type,)*)> for ($($matcher_type,)*) + impl<'a, $($field_type: Debug, $matcher_type: Matcher<'a, $field_type>),*> + Matcher<'a, ($($field_type,)*)> for ($($matcher_type,)*) { - fn matches(&self, actual: & ($($field_type,)*)) -> MatcherResult { + 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 d77162c6..efe005e4 100644 --- a/googletest/src/matchers/unordered_elements_are_matcher.rs +++ b/googletest/src/matchers/unordered_elements_are_matcher.rs @@ -377,13 +377,16 @@ pub mod internal { /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] #[derive(MatcherExt)] - pub struct UnorderedElementsAreMatcher<'a, T: Debug, const N: usize> { - elements: [Box + 'a>; N], + pub struct UnorderedElementsAreMatcher<'a, 'b, T: Debug, const N: usize> { + elements: [Box + 'a>; N], requirements: Requirements, } - impl<'a, T: Debug, const N: usize> UnorderedElementsAreMatcher<'a, T, N> { - pub fn new(elements: [Box + 'a>; N], requirements: Requirements) -> Self { + impl<'a, 'b, T: Debug, const N: usize> UnorderedElementsAreMatcher<'a, 'b, T, N> { + pub fn new( + elements: [Box + 'a>; N], + requirements: Requirements, + ) -> Self { Self { elements, requirements } } } @@ -397,17 +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, 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, { - 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) { @@ -442,50 +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, KeyT, ValueT, const N: usize> + pub struct UnorderedElementsOfMapAreMatcher<'a, 'b, KeyT, ValueT, const N: usize> where KeyT: Debug, ValueT: Debug, { - elements: [KeyValueMatcher<'a, KeyT, ValueT>; N], + elements: [KeyValueMatcher<'a, 'b, KeyT, ValueT>; N], requirements: Requirements, } - impl<'a, KeyT: Debug, ValueT: Debug, const N: usize> - UnorderedElementsOfMapAreMatcher<'a, 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 } } } - impl<'a, KeyT: Debug, ValueT: Debug, const N: usize> MatcherExt - for UnorderedElementsOfMapAreMatcher<'a, KeyT, ValueT, N> + impl<'a, 'b, KeyT: Debug, ValueT: Debug, const N: usize> MatcherExt + for UnorderedElementsOfMapAreMatcher<'a, 'b, KeyT, ValueT, N> { } - impl<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize> - Matcher for UnorderedElementsOfMapAreMatcher<'a, KeyT, ValueT, N> + 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<'b> &'b ContainerT: IntoIterator, + for<'c> &'c ContainerT: IntoIterator, { - fn matches(&self, actual: &ContainerT) -> MatcherResult { + 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) { @@ -594,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() { @@ -610,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() { @@ -999,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() { diff --git a/googletest/tests/elements_are_matcher_test.rs b/googletest/tests/elements_are_matcher_test.rs index 8dc7e3f1..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/unordered_elements_are_matcher_test.rs b/googletest/tests/unordered_elements_are_matcher_test.rs index 05dd3c82..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))] }