Skip to content

Commit a108544

Browse files
authored
Merge pull request #88 from x3ro/add-str-eq
Add `assert_str_eq` macro for comparing "raw" strings
2 parents 88cd1be + 5271543 commit a108544

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

pretty_assertions/src/lib.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,76 @@ where
141141
}
142142
}
143143

144+
/// A comparison of two strings.
145+
///
146+
/// In contrast to [`Comparison`], which uses the [`core::fmt::Debug`] representation,
147+
/// `StrComparison` uses the string values directly, resulting in multi-line output for multiline strings.
148+
///
149+
/// ```
150+
/// use pretty_assertions::StrComparison;
151+
///
152+
/// print!("{}", StrComparison::new("foo\nbar", "foo\nbaz"));
153+
/// ```
154+
///
155+
/// ## Value type bounds
156+
///
157+
/// Any value that can be referenced as a [`str`] via [`AsRef`] may be used:
158+
///
159+
/// ```
160+
/// use pretty_assertions::StrComparison;
161+
///
162+
/// #[derive(PartialEq)]
163+
/// struct MyString(String);
164+
///
165+
/// impl AsRef<str> for MyString {
166+
/// fn as_ref(&self) -> &str {
167+
/// &self.0
168+
/// }
169+
/// }
170+
///
171+
/// print!(
172+
/// "{}",
173+
/// StrComparison::new(
174+
/// &MyString("foo\nbar".to_owned()),
175+
/// &MyString("foo\nbaz".to_owned()),
176+
/// ),
177+
/// );
178+
/// ```
179+
///
180+
/// The values may have different types, although in practice they are usually the same.
181+
pub struct StrComparison<'a, TLeft, TRight>
182+
where
183+
TLeft: ?Sized,
184+
TRight: ?Sized,
185+
{
186+
left: &'a TLeft,
187+
right: &'a TRight,
188+
}
189+
190+
impl<'a, TLeft, TRight> StrComparison<'a, TLeft, TRight>
191+
where
192+
TLeft: AsRef<str> + ?Sized,
193+
TRight: AsRef<str> + ?Sized,
194+
{
195+
/// Store two values to be compared in future.
196+
///
197+
/// Expensive diffing is deferred until calling `Debug::fmt`.
198+
pub fn new(left: &'a TLeft, right: &'a TRight) -> StrComparison<'a, TLeft, TRight> {
199+
StrComparison { left, right }
200+
}
201+
}
202+
203+
impl<'a, TLeft, TRight> Display for StrComparison<'a, TLeft, TRight>
204+
where
205+
TLeft: AsRef<str> + ?Sized,
206+
TRight: AsRef<str> + ?Sized,
207+
{
208+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209+
printer::write_header(f)?;
210+
printer::write_lines(f, self.left.as_ref(), self.right.as_ref())
211+
}
212+
}
213+
144214
/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
145215
///
146216
/// On panic, this macro will print a diff derived from [`Debug`] representation of
@@ -186,6 +256,51 @@ macro_rules! assert_eq {
186256
});
187257
}
188258

259+
/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
260+
///
261+
/// On panic, this macro will print a diff derived from each value's [`str`] representation.
262+
/// See [`StrComparison`] for further details.
263+
///
264+
/// This is a drop in replacement for [`core::assert_eq!`].
265+
/// You can provide a custom panic message if desired.
266+
///
267+
/// # Examples
268+
///
269+
/// ```
270+
/// use pretty_assertions::assert_str_eq;
271+
///
272+
/// let a = "foo\nbar";
273+
/// let b = ["foo", "bar"].join("\n");
274+
/// assert_str_eq!(a, b);
275+
///
276+
/// assert_str_eq!(a, b, "we are testing concatenation with {} and {}", a, b);
277+
/// ```
278+
#[macro_export]
279+
macro_rules! assert_str_eq {
280+
($left:expr, $right:expr$(,)?) => ({
281+
$crate::assert_str_eq!(@ $left, $right, "", "");
282+
});
283+
($left:expr, $right:expr, $($arg:tt)*) => ({
284+
$crate::assert_str_eq!(@ $left, $right, ": ", $($arg)+);
285+
});
286+
(@ $left:expr, $right:expr, $maybe_semicolon:expr, $($arg:tt)*) => ({
287+
match (&($left), &($right)) {
288+
(left_val, right_val) => {
289+
if !(*left_val == *right_val) {
290+
::core::panic!("assertion failed: `(left == right)`{}{}\
291+
\n\
292+
\n{}\
293+
\n",
294+
$maybe_semicolon,
295+
format_args!($($arg)*),
296+
$crate::StrComparison::new(left_val, right_val)
297+
)
298+
}
299+
}
300+
}
301+
});
302+
}
303+
189304
/// Asserts that two expressions are not equal to each other (using [`PartialEq`]).
190305
///
191306
/// On panic, this macro will print the values of the expressions with their

pretty_assertions/tests/macros.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,70 @@
44
#[cfg(feature = "alloc")]
55
extern crate alloc;
66

7+
#[allow(clippy::eq_op)]
8+
mod assert_str_eq {
9+
use ::core::{cmp::PartialEq, convert::AsRef};
10+
11+
#[cfg(feature = "alloc")]
12+
use ::alloc::string::{String, ToString};
13+
#[cfg(feature = "std")]
14+
use ::std::string::{String, ToString};
15+
16+
#[test]
17+
fn passes_str() {
18+
let a = "some value";
19+
::pretty_assertions::assert_str_eq!(a, a);
20+
}
21+
22+
#[test]
23+
fn passes_string() {
24+
let a: String = "some value".to_string();
25+
::pretty_assertions::assert_str_eq!(a, a);
26+
}
27+
28+
#[test]
29+
fn passes_comparable_types() {
30+
let s0: &'static str = "foo";
31+
let s1: String = "foo".to_string();
32+
::pretty_assertions::assert_str_eq!(s0, s1);
33+
}
34+
35+
#[test]
36+
fn passes_as_ref_types() {
37+
#[derive(PartialEq)]
38+
struct MyString(String);
39+
40+
impl AsRef<str> for MyString {
41+
fn as_ref(&self) -> &str {
42+
&self.0
43+
}
44+
}
45+
46+
impl PartialEq<String> for MyString {
47+
fn eq(&self, other: &String) -> bool {
48+
&self.0 == other
49+
}
50+
}
51+
52+
let s0 = MyString("foo".to_string());
53+
let s1 = "foo".to_string();
54+
::pretty_assertions::assert_str_eq!(s0, s1);
55+
}
56+
57+
#[test]
58+
#[should_panic(expected = r#"assertion failed: `(left == right)`
59+
60+
Diff < left / right > :
61+
foo
62+
<bar
63+
>baz
64+
65+
"#)]
66+
fn fails_foo() {
67+
::pretty_assertions::assert_str_eq!("foo\nbar", "foo\nbaz");
68+
}
69+
}
70+
771
#[allow(clippy::eq_op)]
872
mod assert_eq {
973
#[cfg(feature = "alloc")]

0 commit comments

Comments
 (0)