Skip to content

Commit e141282

Browse files
committed
feat: mapping a subject to its debug or display string representation
1 parent cc1205a commit e141282

5 files changed

Lines changed: 191 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,13 +368,15 @@ for types that implement `core::fmt::Debug`:
368368
|----------------------------|----------------------------------------------------------------------------|
369369
| has_debug_string | verify that a type formatted for debug is equal to the expected string |
370370
| does_not_have_debug_string | verify that a type formatted for debug is not equal to the expected string |
371+
| debug_string | map the subject to its debug string representation |
371372

372373
for types that implement `core::fmt::Display`:
373374

374375
| assertion | description |
375376
|------------------------------|------------------------------------------------------------------------------|
376377
| has_display_string | verify that a type formatted for display is equal to the expected string |
377378
| does_not_have_display_string | verify that a type formatted for display is not equal to the expected string |
379+
| display_string | map the subject to its display string representation |
378380

379381
### Emptiness
380382

src/assertions.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,6 +2332,47 @@ pub trait AssertHasDebugString<E> {
23322332
fn does_not_have_debug_string(self, expected: E) -> Self;
23332333
}
23342334

2335+
/// Mapping the subject into its debug string representation to do assertions on
2336+
/// its debug string.
2337+
///
2338+
/// # Examples
2339+
///
2340+
/// ```
2341+
/// use asserting::prelude::*;
2342+
///
2343+
/// #[derive(Debug)]
2344+
/// struct Foo {
2345+
/// hello: String,
2346+
/// }
2347+
///
2348+
/// let subject = Foo { hello: "World".into() };
2349+
///
2350+
/// assert_that!(&subject).debug_string().contains("World");
2351+
/// assert_that!(&subject).debug_string().does_not_start_with("Bar");
2352+
/// ```
2353+
pub trait AssertDebugString<'a, R> {
2354+
/// Maps the subject into its debug string representation to do assertions
2355+
/// on its debug string.
2356+
///
2357+
/// # Examples
2358+
///
2359+
/// ```
2360+
/// use asserting::prelude::*;
2361+
///
2362+
/// #[derive(Debug)]
2363+
/// struct Foo {
2364+
/// hello: String,
2365+
/// }
2366+
///
2367+
/// let subject = Foo { hello: "World".into() };
2368+
///
2369+
/// assert_that!(&subject).debug_string().contains("World");
2370+
/// assert_that!(&subject).debug_string().does_not_start_with("Bar");
2371+
/// ```
2372+
#[track_caller]
2373+
fn debug_string(self) -> Spec<'a, String, R>;
2374+
}
2375+
23352376
/// Assert a type formatted into a display string.
23362377
///
23372378
/// The subject's type must implement `Display` and the expected type must
@@ -2409,6 +2450,57 @@ pub trait AssertHasDisplayString<E> {
24092450
fn does_not_have_display_string(self, expected: E) -> Self;
24102451
}
24112452

2453+
/// Mapping the subject into its display string representation to do assertions
2454+
/// on its display string.
2455+
///
2456+
/// # Examples
2457+
///
2458+
/// ```
2459+
/// use core::fmt::{self, Display};
2460+
/// use asserting::prelude::*;
2461+
///
2462+
/// struct Foo {
2463+
/// hello: String,
2464+
/// }
2465+
///
2466+
/// impl Display for Foo {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2467+
/// write!(f, "Hello, {}", self.hello)
2468+
/// }
2469+
/// }
2470+
///
2471+
/// let subject = Foo { hello: "World".into() };
2472+
///
2473+
/// assert_that!(&subject).display_string().is_equal_to("Hello, World");
2474+
/// assert_that!(&subject).display_string().does_not_end_with('!');
2475+
/// ```
2476+
pub trait AssertDisplayString<'a, R> {
2477+
/// Maps the subject into its display string representation to do assertions
2478+
/// on its display string.
2479+
///
2480+
/// # Examples
2481+
///
2482+
/// ```
2483+
/// use core::fmt::{self, Display};
2484+
/// use asserting::prelude::*;
2485+
///
2486+
/// struct Foo {
2487+
/// hello: String,
2488+
/// }
2489+
///
2490+
/// impl Display for Foo {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2491+
/// write!(f, "Hello, {}", self.hello)
2492+
/// }
2493+
/// }
2494+
///
2495+
/// let subject = Foo { hello: "World".into() };
2496+
///
2497+
/// assert_that!(&subject).display_string().is_equal_to("Hello, World");
2498+
/// assert_that!(&subject).display_string().does_not_end_with('!');
2499+
/// ```
2500+
#[track_caller]
2501+
fn display_string(self) -> Spec<'a, String, R>;
2502+
}
2503+
24122504
/// Assert that a string contains a substring or character.
24132505
///
24142506
/// # Examples

src/error/tests.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,70 @@ mod colored {
359359
);
360360
}
361361
}
362+
363+
#[cfg(feature = "regex")]
364+
mod with_regex {
365+
use super::*;
366+
367+
#[test]
368+
fn error_has_debug_string_matching_regex() {
369+
let error = SuperError {
370+
source: SourceError::Bar,
371+
};
372+
373+
assert_that(error).debug_string().matches(r"SuperError.*");
374+
}
375+
376+
#[test]
377+
fn verify_error_has_debug_string_matching_regex_fails() {
378+
let error = SuperError {
379+
source: SourceError::Bar,
380+
};
381+
382+
let failures = verify_that(error)
383+
.debug_string()
384+
.matches(r"SuperError \{ (source|target): Foo \}")
385+
.display_failures();
386+
387+
assert_eq!(
388+
failures,
389+
&[
390+
r"expected subject's debug string to match the regex SuperError \{ (source|target): Foo \}
391+
but was: SuperError { source: Bar }
392+
does not match regex: SuperError \{ (source|target): Foo \}
393+
"
394+
]
395+
);
396+
}
397+
398+
#[test]
399+
fn error_has_display_string_matching_regex() {
400+
let error = SuperError {
401+
source: SourceError::Bar,
402+
};
403+
404+
assert_that(error).display_string().matches(r".*-error .*");
405+
}
406+
407+
#[test]
408+
fn verify_error_has_display_string_matching_regex_fails() {
409+
let error = SuperError {
410+
source: SourceError::Bar,
411+
};
412+
413+
let failures = verify_that(error)
414+
.display_string()
415+
.matches(r".*-error-caused.*")
416+
.display_failures();
417+
418+
assert_eq!(
419+
failures,
420+
&[
421+
r"expected subject's display string to match the regex .*-error-caused.*
422+
but was: super-error caused by bar error
423+
does not match regex: .*-error-caused.*
424+
"
425+
]
426+
);
427+
}
428+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ mod integer;
839839
mod iterator;
840840
mod length;
841841
mod map;
842+
mod mapping;
842843
#[cfg(feature = "num-bigint")]
843844
mod num_bigint;
844845
mod number;

src/mapping.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::assertions::{AssertDebugString, AssertDisplayString};
2+
use crate::spec::{FailingStrategy, Spec};
3+
use crate::std::fmt::{Debug, Display};
4+
use crate::std::format;
5+
use crate::std::string::{String, ToString};
6+
7+
impl<'a, S, R> AssertDebugString<'a, R> for Spec<'a, S, R>
8+
where
9+
S: Debug,
10+
R: FailingStrategy,
11+
{
12+
fn debug_string(self) -> Spec<'a, String, R> {
13+
let expression_debug_string = format!("{}'s debug string", self.expression());
14+
self.mapping(|subject| format!("{subject:?}"))
15+
.named(expression_debug_string)
16+
}
17+
}
18+
19+
impl<'a, S, R> AssertDisplayString<'a, R> for Spec<'a, S, R>
20+
where
21+
S: Display,
22+
R: FailingStrategy,
23+
{
24+
fn display_string(self) -> Spec<'a, String, R> {
25+
let expression_display_string = format!("{}'s display string", self.expression());
26+
self.mapping(|subject| subject.to_string())
27+
.named(expression_display_string)
28+
}
29+
}

0 commit comments

Comments
 (0)