1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::{
16     description::Description,
17     matcher::{Matcher, MatcherResult},
18     matcher_support::{edit_distance, summarize_diff::create_diff},
19 };
20 use std::{fmt::Debug, marker::PhantomData, ops::Deref};
21 
22 /// Matches a value equal (in the sense of `==`) to the dereferenced value of
23 /// `expected`.
24 ///
25 /// This is similar to [`eq`][crate::matchers::eq] but takes a reference or
26 /// smart pointer to the expected value rather than consuming it. This is useful
27 /// when:
28 ///
29 ///  * one has only a reference to the expected value, and
30 ///  * the expected value cannot or should not be copied or cloned to create an
31 ///    owned value from it.
32 ///
33 /// ```
34 /// # use googletest::{matchers::eq_deref_of, verify_that};
35 /// #[derive(Debug, PartialEq)]
36 /// struct NonCloneableStruct(i32);
37 /// let expected = NonCloneableStruct(123);
38 /// verify_that!(NonCloneableStruct(123), eq_deref_of(&expected))
39 /// #    .unwrap()
40 /// ```
41 ///
42 /// **Note**: while one can use `eq_deref_of` with the configuration methods of
43 /// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
44 /// to configure string equality, it is not possible to do so when the input is
45 /// a smart pointer to a string.
46 ///
47 /// ```compile_fail
48 /// # use googletest::{matchers::{eq_deref_of, str_matcher::StrMatcherConfigurator}, verify_that};
49 /// verify_that!("A string", eq_deref_of(Box::new("A STRING")).ignoring_ascii_case()) // Does not compile
50 /// #    .unwrap()
51 /// ```
52 ///
53 /// Otherwise, this has the same behaviour as [`eq`][crate::matchers::eq].
eq_deref_of<ActualT: ?Sized, ExpectedRefT>( expected: ExpectedRefT, ) -> EqDerefOfMatcher<ActualT, ExpectedRefT>54 pub fn eq_deref_of<ActualT: ?Sized, ExpectedRefT>(
55     expected: ExpectedRefT,
56 ) -> EqDerefOfMatcher<ActualT, ExpectedRefT> {
57     EqDerefOfMatcher { expected, phantom: Default::default() }
58 }
59 
60 /// A matcher which matches a value equal to the derefenced value of `expected`.
61 ///
62 /// See [`eq_deref_of`].
63 pub struct EqDerefOfMatcher<ActualT: ?Sized, ExpectedRefT> {
64     pub(crate) expected: ExpectedRefT,
65     phantom: PhantomData<ActualT>,
66 }
67 
68 impl<ActualT, ExpectedRefT, ExpectedT> Matcher for EqDerefOfMatcher<ActualT, ExpectedRefT>
69 where
70     ActualT: Debug + ?Sized,
71     ExpectedRefT: Deref<Target = ExpectedT> + Debug,
72     ExpectedT: PartialEq<ActualT> + Debug,
73 {
74     type ActualT = ActualT;
75 
matches(&self, actual: &ActualT) -> MatcherResult76     fn matches(&self, actual: &ActualT) -> MatcherResult {
77         (self.expected.deref() == actual).into()
78     }
79 
describe(&self, matcher_result: MatcherResult) -> Description80     fn describe(&self, matcher_result: MatcherResult) -> Description {
81         match matcher_result {
82             MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
83             MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
84         }
85     }
86 
explain_match(&self, actual: &ActualT) -> Description87     fn explain_match(&self, actual: &ActualT) -> Description {
88         format!(
89             "which {}{}",
90             &self.describe(self.matches(actual)),
91             create_diff(
92                 &format!("{:#?}", actual),
93                 &format!("{:#?}", self.expected.deref()),
94                 edit_distance::Mode::Exact,
95             )
96         )
97         .into()
98     }
99 }
100 
101 #[cfg(test)]
102 mod tests {
103     use super::eq_deref_of;
104     use crate::prelude::*;
105     use indoc::indoc;
106 
107     #[derive(Debug, PartialEq)]
108     struct NonCloneNonCopyStruct(i32);
109 
110     #[test]
matches_value_with_ref_to_equal_value() -> Result<()>111     fn matches_value_with_ref_to_equal_value() -> Result<()> {
112         verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(&NonCloneNonCopyStruct(123)))
113     }
114 
115     #[test]
matches_value_with_box_of_equal_value() -> Result<()>116     fn matches_value_with_box_of_equal_value() -> Result<()> {
117         verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(Box::new(NonCloneNonCopyStruct(123))))
118     }
119 
120     #[test]
does_not_match_value_with_non_equal_value() -> Result<()>121     fn does_not_match_value_with_non_equal_value() -> Result<()> {
122         verify_that!(NonCloneNonCopyStruct(123), not(eq_deref_of(&NonCloneNonCopyStruct(234))))
123     }
124 
125     #[test]
shows_structured_diff() -> Result<()>126     fn shows_structured_diff() -> Result<()> {
127         #[derive(Debug, PartialEq)]
128         struct Strukt {
129             int: i32,
130             string: String,
131         }
132 
133         let result = verify_that!(
134             Strukt { int: 123, string: "something".into() },
135             eq_deref_of(Box::new(Strukt { int: 321, string: "someone".into() }))
136         );
137         verify_that!(
138             result,
139             err(displays_as(contains_substring(indoc! {
140             "
141             Actual: Strukt { int: 123, string: \"something\" },
142               which isn't equal to Strukt { int: 321, string: \"someone\" }
143               Difference(-actual / +expected):
144                Strukt {
145               -    int: 123,
146               +    int: 321,
147               -    string: \"something\",
148               +    string: \"someone\",
149                }
150             "})))
151         )
152     }
153 }
154