Skip to main content

googletest/matchers/
display_matcher.rs

1// Copyright 2022 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
15use crate::description::Description;
16use crate::matcher::{Matcher, MatcherBase, MatcherResult};
17use std::fmt::{Debug, Display};
18
19/// Matches the string representation of types that implement `Display`.
20///
21/// ```ignore
22/// let result: impl Display = ...;
23/// verify_that!(result, displays_as(eq(format!("{}", result))))?;
24/// ```
25pub fn displays_as<InnerMatcher: for<'a> Matcher<&'a str>>(
26    inner: InnerMatcher,
27) -> DisplayMatcher<InnerMatcher> {
28    DisplayMatcher { inner }
29}
30
31#[derive(MatcherBase)]
32pub struct DisplayMatcher<InnerMatcher> {
33    inner: InnerMatcher,
34}
35
36impl<T: Debug + Display + Copy, InnerMatcher: for<'a> Matcher<&'a str>> Matcher<T>
37    for DisplayMatcher<InnerMatcher>
38{
39    fn matches(&self, actual: T) -> MatcherResult {
40        self.inner.matches(&format!("{actual}"))
41    }
42
43    fn explain_match(&self, actual: T) -> Description {
44        format!(
45            "which displays as {:?} {}",
46            actual.to_string(),
47            self.inner.explain_match(&format!("{actual}"))
48        )
49        .into()
50    }
51
52    fn describe(&self, matcher_result: MatcherResult) -> Description {
53        match matcher_result {
54            MatcherResult::Match => {
55                format!("displays as a string which {}", self.inner.describe(MatcherResult::Match))
56                    .into()
57            }
58            MatcherResult::NoMatch => format!(
59                "doesn't display as a string which {}",
60                self.inner.describe(MatcherResult::Match)
61            )
62            .into(),
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use crate::prelude::*;
70    use crate::Result;
71    use indoc::indoc;
72    use std::fmt::{Debug, Display, Error, Formatter};
73
74    #[test]
75    fn display_matches_i32() -> Result<()> {
76        let value = 32;
77        verify_that!(value, displays_as(eq("32")))?;
78        Ok(())
79    }
80
81    #[test]
82    fn display_matches_str() -> Result<()> {
83        let value = "32";
84        verify_that!(value, displays_as(eq("32")))?;
85        Ok(())
86    }
87
88    #[test]
89    fn display_matches_struct() -> Result<()> {
90        #[allow(dead_code)]
91        #[derive(Debug)]
92        struct Struct {
93            a: i32,
94            b: i64,
95        }
96        impl Display for Struct {
97            fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> {
98                write!(f, "{self:?}")
99            }
100        }
101        verify_that!(Struct { a: 123, b: 321 }, displays_as(eq("Struct { a: 123, b: 321 }")))?;
102        Ok(())
103    }
104
105    #[test]
106    fn display_displays_error_message_with_explanation_from_inner_matcher() -> Result<()> {
107        let result = verify_that!("123\n234", displays_as(eq("123\n345")));
108
109        verify_that!(
110            result,
111            err(displays_as(contains_substring(indoc!(
112                "
113                  Actual: \"123\\n234\",
114                    which displays as \"123\\n234\" which isn't equal to \"123\\n345\"
115                    
116                    Difference(-actual / +expected):
117                     123
118                    -234
119                    +345
120                "
121            ))))
122        )
123    }
124}