asserting/spec/
mod.rs

1//! This is the core of the `asserting` crate.
2
3use crate::expectations::Predicate;
4use crate::std::error::Error as StdError;
5use crate::std::fmt::{self, Debug, Display};
6use crate::std::ops::Deref;
7#[cfg(not(feature = "std"))]
8use alloc::{
9    borrow::ToOwned,
10    string::{String, ToString},
11    vec,
12    vec::Vec,
13};
14
15/// Starts an assertion for the given subject or expression in the
16/// [`PanicOnFail`] mode.
17///
18/// It wraps the subject into a [`Spec`] and sets the name of the expression and
19/// the code location of the assertion in the [`Spec`]. On the [`Spec`] any
20/// assertion method that is implemented for the subject's type can be called.
21///
22/// Assertions started with `assert_that!` will panic on the first failing
23/// assertion.
24///
25/// # Example
26///
27/// ```
28/// use asserting::prelude::*;
29///
30/// assert_that!(7 * 6).is_equal_to(42);
31/// ```
32///
33/// This call of the macro expands to:
34///
35/// ```
36/// # use asserting::prelude::*;
37/// assert_that(7 * 6)
38///     .named("7 * 6")
39///     .located_at(Location { file: file!(), line: line!(), column: column!() })
40///     .is_equal_to(42);
41/// ```
42#[macro_export]
43macro_rules! assert_that {
44    ($subject:expr) => {
45        $crate::prelude::assert_that($subject)
46            .named(&stringify!($subject).replace("\n", " "))
47            .located_at($crate::prelude::Location {
48                file: file!(),
49                line: line!(),
50                column: column!(),
51            })
52    };
53}
54
55/// Starts an assertion for the given subject or expression in the
56/// [`CollectFailures`] mode.
57///
58/// It wraps the subject into a [`Spec`] and sets the name of the expression and
59/// the code location of the assertion in the [`Spec`]. On the [`Spec`] any
60/// assertion method that is implemented for the subject's type can be called.
61///
62/// Assertions started with `verify_that!` will collect [`AssertFailure`]s for
63/// all failing assertions. The collected failures can be queried by calling one
64/// of the methods [`failures`](Spec::failures) or
65/// [`display_failures`](Spec::display_failures) on the [`Spec`].
66///
67/// # Example
68///
69/// ```
70/// use asserting::prelude::*;
71/// let some_text = "vel tempor augue delenit".to_string();
72/// let failures = verify_that!(some_text)
73///     .starts_with("nibh")
74///     .ends_with("magna")
75///     .failures();
76///
77/// assert_that!(failures).has_length(2);
78/// ```
79///
80/// This call of the macro expands to:
81///
82/// ```
83/// # use asserting::prelude::*;
84/// # let some_text = "vel tempor augue delenit".to_string();
85/// let failures = verify_that(some_text)
86///     .named("some_text")
87///     .located_at(Location { file: file!(), line: line!(), column: column!() })
88///     .starts_with("nibh")
89///     .ends_with("magna")
90///     .failures();
91/// ```
92#[macro_export]
93macro_rules! verify_that {
94    ($subject:expr) => {
95        $crate::prelude::verify_that($subject)
96            .named(&stringify!($subject).replace("\n", " "))
97            .located_at($crate::prelude::Location {
98                file: file!(),
99                line: line!(),
100                column: column!(),
101            })
102    };
103}
104
105/// Starts an assertion for some piece of code in the [`PanicOnFail`] mode.
106///
107/// It takes a closure and wraps it into a [`Spec`]. On the [`Spec`] any
108/// assertion method that is implemented for closures can be called.
109///
110/// Assertions started with `assert_that_code!` will panic on the first failing
111/// assertion.
112///
113/// # Examples
114///
115/// ```
116/// use asserting::prelude::*;
117///
118/// fn divide(a: i32, b: i32) -> i32 {
119///     a / b
120/// }
121///
122/// assert_that_code!(|| { divide(7, 0); }).panics();
123///
124/// assert_that_code!(|| { divide(7, 0); })
125///     .panics_with_message("attempt to divide by zero");
126///
127/// assert_that_code!(|| { divide(7, 3); }).does_not_panic();
128/// ```
129#[cfg(feature = "panic")]
130#[cfg_attr(feature = "panic", macro_export)]
131macro_rules! assert_that_code {
132    ($subject:expr) => {
133        $crate::prelude::assert_that_code($subject)
134            .named(&stringify!($subject).replace("\n", " "))
135            .located_at($crate::prelude::Location {
136                file: file!(),
137                line: line!(),
138                column: column!(),
139            })
140    };
141}
142
143/// Starts an assertion for some piece of code in the [`CollectFailures`] mode.
144///
145/// It takes a closure and wraps it into a [`Spec`]. On the [`Spec`] any
146/// assertion method that is implemented for closures can be called.
147///
148/// Assertions started with `verify_that_code!` will collect [`AssertFailure`]s
149/// for all failing assertions. The collected failures can be queried by calling
150/// one of the methods [`failures`](Spec::failures) or
151/// [`display_failures`](Spec::display_failures) on the [`Spec`].
152///
153/// # Examples
154///
155/// ```
156/// use asserting::prelude::*;
157///
158/// fn divide(a: i32, b: i32) -> i32 {
159///     a / b
160/// }
161///
162/// let failures = verify_that_code!(|| { divide(7, 3); })
163///     .does_not_panic()
164///     .failures();
165///
166/// assert_that!(failures).is_empty();
167///
168/// let failures = verify_that_code!(|| { divide(7, 0); })
169///     .does_not_panic()
170///     .display_failures();
171///
172/// assert_that!(failures).contains_exactly([
173///     r#"assertion failed: expected || { divide(7, 0); } to not panic, but did panic
174///   with message: "attempt to divide by zero"
175/// "#
176/// ]);
177///
178/// let failures = verify_that_code!(|| { divide(7, 0); })
179///     .panics_with_message("division by zero")
180///     .display_failures();
181///
182/// assert_that!(failures).contains_exactly([
183///     r#"assertion failed: expected || { divide(7, 0); } to panic with message "division by zero"
184///    but was: "attempt to divide by zero"
185///   expected: "division by zero"
186/// "#
187/// ]);
188/// ```
189#[cfg(feature = "panic")]
190#[cfg_attr(feature = "panic", macro_export)]
191macro_rules! verify_that_code {
192    ($subject:expr) => {
193        $crate::prelude::verify_that_code($subject)
194            .named(&stringify!($subject).replace("\n", " "))
195            .located_at($crate::prelude::Location {
196                file: file!(),
197                line: line!(),
198                column: column!(),
199            })
200    };
201}
202
203/// Starts an assertion for the given subject or expression in the
204/// [`PanicOnFail`] mode.
205///
206/// It wraps the subject into a [`Spec`]. On the [`Spec`] any
207/// assertion method that is implemented for the subject's type can be called.
208///
209/// Assertions started with `assert_that()` will panic on the first failing
210/// assertion.
211///
212/// In comparison to using the macro [`assert_that!`](crate::assert_that)
213/// calling this function does not set a name for the expression and does not
214/// set the code location of the assertion. In failure messages the generic word
215/// "subject" is used. To set a specific text for the expression the method
216/// [`named`](Spec::named) must be called explicitly.
217///
218/// Note: It is not necessary to set the code location explicitly as this
219/// function is annotated with `#[track_caller]`.
220///
221/// # Examples
222///
223/// ```
224/// use asserting::prelude::*;
225///
226/// assert_that(7 * 6).is_equal_to(42);
227/// ```
228///
229/// or with setting a name for the expression:
230///
231/// ```
232/// use asserting::prelude::*;
233///
234/// assert_that(7 * 6)
235///     .named("7 * 6")
236///     .is_equal_to(42);
237/// ```
238#[track_caller]
239pub const fn assert_that<'a, S>(subject: S) -> Spec<'a, S, PanicOnFail> {
240    Spec::new(subject, PanicOnFail)
241}
242
243/// Starts an assertion for the given subject or expression in the
244/// [`CollectFailures`] mode.
245///
246/// It wraps the subject into a [`Spec`]. On the [`Spec`] any
247/// assertion method that is implemented for the subject's type can be called.
248///
249/// Assertions started with `verify_that()` will collect [`AssertFailure`]s
250/// for all failing assertions. The collected failures can be queried by calling
251/// one of the methods [`failures`](Spec::failures) or the
252/// [`display_failures`](Spec::display_failures) on the [`Spec`].
253///
254/// In comparison to using the macro [`verify_that!`](crate::verify_that) calling
255/// this function does not set a name for the expression and does not set the
256/// code location of the assertion. In failure messages the generic word
257/// "subject" is used. To set a specific text for the expression the method
258/// [`named`](Spec::named) must be called explicitly.
259///
260/// # Examples
261///
262/// ```
263/// use asserting::prelude::*;
264///
265/// let some_text = "vel tempor augue delenit".to_string();
266///
267/// let failures = verify_that(some_text).named("my_thing")
268///     .starts_with("nibh")
269///     .ends_with("magna")
270///     .failures();
271///
272/// assert_that!(failures).has_length(2);
273/// ```
274///
275/// or with querying the failures as formated text:
276///
277/// ```
278/// use asserting::prelude::*;
279///
280/// let some_text = "vel tempor augue delenit".to_string();
281///
282/// let failures = verify_that(some_text).named("my_thing")
283///     .starts_with("nibh")
284///     .ends_with("magna")
285///     .display_failures();
286///
287/// assert_that!(failures).contains_exactly([
288///     r#"assertion failed: expected my_thing to start with "nibh"
289///    but was: "vel tempor augue delenit"
290///   expected: "nibh"
291/// "#,
292///     r#"assertion failed: expected my_thing to end with "magna"
293///    but was: "vel tempor augue delenit"
294///   expected: "magna"
295/// "#,
296/// ]);
297/// ```
298#[track_caller]
299pub const fn verify_that<'a, S>(subject: S) -> Spec<'a, S, CollectFailures> {
300    Spec::new(subject, CollectFailures)
301}
302
303/// Starts an assertion for some piece of code in the [`PanicOnFail`] mode.
304///
305/// It takes a closure and wraps it into a [`Spec`]. On the [`Spec`] any
306/// assertion method that is implemented for closures can be called.
307///
308/// Assertions started with `assert_that_code()` will panic on the first failing
309/// assertion.
310///
311/// In comparison to using the macro [`assert_that_code!`](crate::assert_that_code)
312/// calling this function does not set a name for the expression and does not
313/// set the code location of the assertion. In failure messages the generic word
314/// "the closure" is used. To set a specific text for the expression the method
315/// [`named`](Spec::named) must be called explicitly.
316///
317/// # Examples
318///
319/// ```
320/// use asserting::prelude::*;
321///
322/// fn divide(a: i32, b: i32) -> i32 {
323///     a / b
324/// }
325///
326/// assert_that_code(|| { divide(7, 0); })
327///     .panics_with_message("attempt to divide by zero");
328///
329/// assert_that_code(|| { divide(7, 3); }).does_not_panic();
330/// ```
331#[cfg(feature = "panic")]
332pub fn assert_that_code<'a, S>(code: S) -> Spec<'a, Code<S>, PanicOnFail>
333where
334    S: FnOnce(),
335{
336    Spec::new(Code::from(code), PanicOnFail).named("the closure")
337}
338
339/// Starts an assertion for some piece of code in the [`CollectFailures`] mode.
340///
341/// It takes a closure and wraps it into a [`Spec`]. On the [`Spec`] any
342/// assertion method that is implemented for closures can be called.
343///
344/// Assertions started with `verify_that_code()` will collect [`AssertFailure`]s
345/// for all failing assertions. The collected failures can be queried by calling
346/// one of the methods [`failures`](Spec::failures) or
347/// [`display_failures`](Spec::display_failures) on the [`Spec`].
348///
349/// In comparison to using the macro [`verify_that_code!`](crate::verify_that_code)
350/// calling this function does not set a name for the expression and does not
351/// set the code location of the assertion. In failure messages the generic word
352/// "the closure" is used. To set a specific text for the expression the method
353/// [`named`](Spec::named) must be called explicitly.
354///
355/// # Examples
356///
357/// ```
358/// use asserting::prelude::*;
359///
360/// fn divide(a: i32, b: i32) -> i32 {
361///     a / b
362/// }
363///
364/// let failures = verify_that_code(|| { divide(7, 3); })
365///     .does_not_panic()
366///     .failures();
367///
368/// assert_that!(failures).is_empty();
369///
370/// let failures = verify_that_code(|| { divide(7, 0); })
371///     .does_not_panic()
372///     .display_failures();
373///
374/// assert_that!(failures).contains_exactly([
375///     r#"assertion failed: expected the closure to not panic, but did panic
376///   with message: "attempt to divide by zero"
377/// "#
378/// ]);
379///
380/// let failures = verify_that_code(|| { divide(7, 0); })
381///     .panics_with_message("division by zero")
382///     .display_failures();
383///
384/// assert_that!(failures).contains_exactly([
385///     r#"assertion failed: expected the closure to panic with message "division by zero"
386///    but was: "attempt to divide by zero"
387///   expected: "division by zero"
388/// "#
389/// ]);
390/// ```
391#[cfg(feature = "panic")]
392pub fn verify_that_code<'a, S>(code: S) -> Spec<'a, Code<S>, CollectFailures>
393where
394    S: FnOnce(),
395{
396    Spec::new(Code::from(code), CollectFailures).named("the closure")
397}
398
399/// An expectation defines a test for a property of the asserted subject.
400///
401/// It requires two methods: a `test()` method and a `message()` method.
402/// The `test()` method is called to verify whether an actual subject meets the
403/// expected property. In case the test of the expectation fails the `message()`
404/// method is called to form an expectation specific failure message.
405pub trait Expectation<S: ?Sized> {
406    /// Verifies whether the actual subject fulfills the expected property.
407    fn test(&mut self, subject: &S) -> bool;
408
409    /// Forms a failure message for this expectation.
410    fn message(&self, expression: Expression<'_>, actual: &S) -> String;
411}
412
413/// A textual representation of the expression or subject that is being
414/// asserted.
415#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
416pub struct Expression<'a>(pub &'a str);
417
418impl Default for Expression<'_> {
419    fn default() -> Self {
420        Self("subject")
421    }
422}
423
424impl Display for Expression<'_> {
425    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426        write!(f, "{}", self.0)
427    }
428}
429
430impl Deref for Expression<'_> {
431    type Target = str;
432
433    fn deref(&self) -> &Self::Target {
434        self.0
435    }
436}
437
438/// The location of an assertion in the source code respectively test code.
439///
440/// # Related
441/// - [`core::panic::Location`]
442#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
443pub struct Location<'a> {
444    /// The file path of the source file where the assertion is located.
445    pub file: &'a str,
446
447    /// The line number within the source file where the assertion is located.
448    pub line: u32,
449
450    /// The column number on the line within the source file where the assertion
451    /// is located.
452    pub column: u32,
453}
454
455impl Display for Location<'_> {
456    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457        #[cfg(not(test))]
458        let file = self.file;
459        #[cfg(test)]
460        let file = self.file.replace('\\', "/");
461        write!(f, "{file}:{}:{}", self.line, self.column)
462    }
463}
464
465impl<'a> Location<'a> {
466    /// Constructs a new `Location` with the given file, line and column.
467    #[must_use]
468    pub const fn new(file: &'a str, line: u32, column: u32) -> Self {
469        Self { file, line, column }
470    }
471}
472
473impl Location<'_> {
474    /// Returns the file path of this location.
475    #[must_use]
476    pub const fn file(&self) -> &str {
477        self.file
478    }
479
480    /// Returns the line number of this location.
481    #[must_use]
482    pub const fn line(&self) -> u32 {
483        self.line
484    }
485
486    /// Returns the column number of this location.
487    #[must_use]
488    pub const fn column(&self) -> u32 {
489        self.column
490    }
491}
492
493/// An owned location in the source code respectively test code.
494///
495/// It is basically the same as [`Location`] but uses owned types instead of
496/// borrowed types for its fields.
497#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
498pub struct OwnedLocation {
499    /// The file path of the source file where the assertion is located.
500    pub file: String,
501
502    /// The line number within the source file where the assertion is located.
503    pub line: u32,
504
505    /// The column number on the line within the source file where the assertion
506    /// is located.
507    pub column: u32,
508}
509
510impl Display for OwnedLocation {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        #[cfg(not(test))]
513        let file = self.file.clone();
514        #[cfg(test)]
515        let file = self.file.replace('\\', "/");
516        write!(f, "{file}:{}:{}", self.line, self.column)
517    }
518}
519
520impl OwnedLocation {
521    /// Constructs a new `OwnedLocation` with the given file, line and column.
522    #[must_use]
523    pub fn new(file: impl Into<String>, line: u32, column: u32) -> Self {
524        Self {
525            file: file.into(),
526            line,
527            column,
528        }
529    }
530}
531
532impl From<Location<'_>> for OwnedLocation {
533    fn from(value: Location<'_>) -> Self {
534        Self {
535            file: value.file.into(),
536            line: value.line,
537            column: value.column,
538        }
539    }
540}
541
542impl OwnedLocation {
543    /// Returns the file path of this location.
544    #[must_use]
545    pub fn file(&self) -> &str {
546        &self.file
547    }
548
549    #[must_use]
550    /// Returns the line number of this location.
551    pub const fn line(&self) -> u32 {
552        self.line
553    }
554
555    #[must_use]
556    /// Returns the column number of this location.
557    pub const fn column(&self) -> u32 {
558        self.column
559    }
560}
561
562/// Data of an actual assertion.
563///
564/// It holds the data needed to execute an assertion such as the subject,
565/// the name of the subject or expression, an optional description of the
566/// current assertion and the location of the assertion in the source code
567/// respectively test code.
568///
569/// It also holds the concrete [`FailingStrategy`] on how to behave in case
570/// an assertion fails.
571///
572/// In case of the [`CollectFailures`] failing strategy the [`AssertFailure`]s
573/// are collected in this struct.
574pub struct Spec<'a, S, R> {
575    subject: S,
576    expression: Option<Expression<'a>>,
577    description: Option<&'a str>,
578    location: Option<Location<'a>>,
579    failures: Vec<AssertFailure>,
580    failing_strategy: R,
581}
582
583impl<S, R> Spec<'_, S, R> {
584    /// Constructs a new `Spec` for the given subject and with the specified
585    /// failing strategy.
586    #[must_use = "a spec does nothing unless an assertion method is called"]
587    pub const fn new(subject: S, failing_strategy: R) -> Self {
588        Self {
589            subject,
590            expression: None,
591            description: None,
592            location: None,
593            failures: vec![],
594            failing_strategy,
595        }
596    }
597
598    /// Returns the subject.
599    pub const fn subject(&self) -> &S {
600        &self.subject
601    }
602
603    /// Returns the expression (or subject name) if one has been set.
604    pub const fn expression(&self) -> Option<Expression<'_>> {
605        self.expression
606    }
607
608    /// Returns the location in source code or test code if it has been set.
609    pub const fn location(&self) -> Option<Location<'_>> {
610        self.location
611    }
612
613    /// Returns the description or the assertion if it has been set.
614    pub const fn description(&self) -> Option<&str> {
615        self.description
616    }
617
618    /// Returns the failing strategy that is used in case an assertion fails.
619    pub const fn failing_strategy(&self) -> &R {
620        &self.failing_strategy
621    }
622
623    /// Returns the assertion failures that have been collected so far.
624    pub fn failures(&self) -> Vec<AssertFailure> {
625        self.failures.clone()
626    }
627
628    /// Returns the assertion failures collected so far as formatted text.
629    pub fn display_failures(&self) -> Vec<String> {
630        self.failures.iter().map(ToString::to_string).collect()
631    }
632}
633
634impl<'a, S, R> Spec<'a, S, R> {
635    /// Sets the subject name or expression for this assertion.
636    #[must_use = "a spec does nothing unless an assertion method is called"]
637    pub const fn named(mut self, subject_name: &'a str) -> Self {
638        self.expression = Some(Expression(subject_name));
639        self
640    }
641
642    /// Sets a custom description about what is being asserted.
643    #[must_use = "a spec does nothing unless an assertion method is called"]
644    pub const fn described_as(mut self, description: &'a str) -> Self {
645        self.description = Some(description);
646        self
647    }
648
649    /// Sets the location of the assertion in the source code respectively test
650    /// code.
651    #[must_use = "a spec does nothing unless an assertion method is called"]
652    pub const fn located_at(mut self, location: Location<'a>) -> Self {
653        self.location = Some(location);
654        self
655    }
656
657    /// Maps the current subject to some other value.
658    ///
659    /// It takes a closure that maps the current subject to a new subject and
660    /// returns a new `Spec` with the value returned by the closure as the new
661    /// subject. The new subject may have a different type than the original
662    /// subject. All other data like expression, description and location are
663    /// taken over from this `Spec` into the returned `Spec`.
664    ///
665    /// This function is useful when having a custom type and some specific
666    /// property of this type shall be asserted only.
667    ///
668    /// This is an alias function to the [`mapping()`](Spec::mapping) function.
669    /// Both functions do exactly the same. The idea is to provide different
670    /// names to be able to express the intent more clearly when used in
671    /// assertions.
672    ///
673    /// # Example
674    ///
675    /// ```
676    /// use asserting::prelude::*;
677    ///
678    /// struct MyStruct {
679    ///     important_property: String,
680    ///     other_property: f64,
681    /// }
682    ///
683    /// let some_thing = MyStruct {
684    ///     important_property: "imperdiet aliqua zzril eiusmod".into(),
685    ///     other_property: 99.9,
686    /// };
687    ///
688    /// assert_that!(some_thing).extracting(|s| s.important_property)
689    ///     .is_equal_to("imperdiet aliqua zzril eiusmod");
690    ///
691    /// ```
692    pub fn extracting<F, U>(self, extractor: F) -> Spec<'a, U, R>
693    where
694        F: FnOnce(S) -> U,
695    {
696        self.mapping(extractor)
697    }
698
699    /// Maps the current subject to some other value.
700    ///
701    /// It takes a closure that maps the current subject to a new subject and
702    /// returns a new `Spec` with the value returned by the closure as the new
703    /// subject. The new subject may have a different type than the original
704    /// subject. All other data like expression, description and location are
705    /// taken over from this `Spec` into the returned `Spec`.
706    ///
707    /// This function is useful if some type does not implement a trait that is
708    /// required for an assertion.
709    ///
710    /// `Spec` also provides the [`extracting()`](Spec::extracting) function,
711    /// which is an alias to this function. Both functions do exactly the same.
712    /// Choose that function of which its name expresses the intent more
713    /// clearly.
714    ///
715    /// # Example
716    ///
717    /// ```
718    /// use asserting::prelude::*;
719    ///
720    /// struct Point {
721    ///     x: i64,
722    ///     y: i64,
723    /// }
724    ///
725    /// let target = Point { x: 12, y: -64 };
726    ///
727    /// assert_that!(target).mapping(|s| (s.x, s.y)).is_equal_to((12, -64));
728    /// ```
729    ///
730    /// The custom type `Point` does not implement the `PartialEq` trait nor
731    /// the `Debug` trait, which are both required for an `is_equal_to`
732    /// assertion. So we map the subject of the type `Point` to a tuple of its
733    /// fields.
734    pub fn mapping<F, U>(self, mapper: F) -> Spec<'a, U, R>
735    where
736        F: FnOnce(S) -> U,
737    {
738        Spec {
739            subject: mapper(self.subject),
740            expression: self.expression,
741            description: self.description,
742            location: self.location,
743            failures: self.failures,
744            failing_strategy: self.failing_strategy,
745        }
746    }
747}
748
749impl<S, R> Spec<'_, S, R>
750where
751    R: FailingStrategy,
752{
753    /// Asserts the given expectation.
754    ///
755    /// In case the expectation is not meet it does fail according the current
756    /// failing strategy of this `Spec`.
757    ///
758    /// This method is called from the implementations of the assertion traits
759    /// defined in the [`assertions`](crate::assertions) module. Implementations
760    /// of custom assertions will call this method with a proper expectation.
761    ///
762    /// # Examples
763    ///
764    /// ```
765    /// use asserting::expectations::{IsEmpty, IsEqualTo};
766    /// use asserting::prelude::*;
767    ///
768    /// assert_that!(7 * 6).expecting(IsEqualTo {expected: 42 });
769    ///
770    /// assert_that!("").expecting(IsEmpty);
771    /// ```
772    #[allow(clippy::needless_pass_by_value, clippy::return_self_not_must_use)]
773    #[track_caller]
774    pub fn expecting(mut self, mut expectation: impl Expectation<S>) -> Self {
775        if !expectation.test(&self.subject) {
776            let expression = self.expression.unwrap_or_default();
777            let message = expectation.message(expression, &self.subject);
778            self.do_fail_with_message(message);
779        }
780        self
781    }
782
783    /// Asserts whether the given predicate is meet.
784    ///
785    /// This method takes a predicate function and calls it as an expectation.
786    /// In case the predicate function returns false, it does fail with a
787    /// generic failure message and according to the current failing strategy of
788    /// this `Spec`.
789    ///
790    /// This method can be used to do simple custom assertions without
791    /// implementing an [`Expectation`] and an assertion trait.
792    ///
793    /// # Examples
794    ///
795    /// ```
796    /// use asserting::prelude::*;
797    ///
798    /// fn is_odd(value: &i32) -> bool {
799    ///     value & 1 == 1
800    /// }
801    ///
802    /// assert_that!(37).satisfies(is_odd);
803    ///
804    /// let failures = verify_that!(22).satisfies(is_odd).display_failures();
805    ///
806    /// assert_that!(failures).contains_exactly([
807    ///     "assertion failed: expected 22 to satisfy the given predicate, but returned false\n"
808    /// ]);
809    /// ```
810    ///
811    /// To assert a predicate with a custom failure message instead of the
812    /// generic one use the method
813    /// [`satisfies_with_message`](Spec::satisfies_with_message).
814    #[allow(clippy::return_self_not_must_use)]
815    #[track_caller]
816    pub fn satisfies<P>(self, predicate: P) -> Self
817    where
818        P: Fn(&S) -> bool,
819    {
820        self.expecting(Predicate {
821            predicate,
822            message: None,
823        })
824    }
825
826    #[allow(clippy::return_self_not_must_use)]
827    #[track_caller]
828    /// Asserts whether the given predicate is meet.
829    ///
830    /// This method takes a predicate function and calls it as an expectation.
831    /// In case the predicate function returns false, it does fail with the
832    /// provided failure message and according to the current failing strategy
833    /// of this `Spec`.
834    ///
835    /// This method can be used to do simple custom assertions without
836    /// implementing an [`Expectation`] and an assertion trait.
837    ///
838    /// # Examples
839    ///
840    /// ```
841    /// use asserting::prelude::*;
842    ///
843    /// fn is_odd(value: &i32) -> bool {
844    ///     value & 1 == 1
845    /// }
846    ///
847    /// assert_that!(37).satisfies_with_message("expected my number to be odd", is_odd);
848    ///
849    /// let failures = verify_that!(22)
850    ///         .satisfies_with_message("expected my number to be odd", is_odd)
851    ///         .display_failures();
852    ///
853    /// assert_that!(failures).contains_exactly([
854    ///     "assertion failed: expected my number to be odd\n"
855    /// ]);
856    /// ```
857    ///
858    /// To assert a predicate with a generic failure message instead of
859    /// providing one use the method
860    /// [`satisfies`](Spec::satisfies).
861    pub fn satisfies_with_message<P>(self, message: impl Into<String>, predicate: P) -> Self
862    where
863        P: Fn(&S) -> bool,
864    {
865        self.expecting(Predicate {
866            predicate,
867            message: Some(message.into()),
868        })
869    }
870
871    /// Fails the assertion according the current failing strategy of this
872    /// `Spec`.
873    #[track_caller]
874    fn do_fail_with_message(&mut self, message: impl Into<String>) {
875        let message = message.into();
876        let failure = AssertFailure {
877            description: self.description.map(ToOwned::to_owned),
878            message,
879            location: self.location.map(OwnedLocation::from),
880        };
881        self.failures.push(failure);
882        self.failing_strategy.do_fail_with(&self.failures);
883    }
884}
885
886/// An error describing a failed assertion.
887///
888/// This struct implements the [`std::error::Error`] trait.
889#[derive(Debug, Clone, PartialEq, Eq)]
890pub struct AssertFailure {
891    description: Option<String>,
892    message: String,
893    location: Option<OwnedLocation>,
894}
895
896impl Display for AssertFailure {
897    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
898        match &self.description {
899            None => {
900                writeln!(f, "assertion failed: {}", &self.message)?;
901            },
902            Some(description) => {
903                writeln!(f, "assertion failed: {description}\n{}", &self.message)?;
904            },
905        }
906        Ok(())
907    }
908}
909
910impl StdError for AssertFailure {}
911
912#[allow(clippy::must_use_candidate)]
913impl AssertFailure {
914    /// Returns the description of the assertion that failed.
915    pub const fn description(&self) -> Option<&String> {
916        self.description.as_ref()
917    }
918
919    /// Returns the failure message of the assertion that failed.
920    pub fn message(&self) -> &str {
921        &self.message
922    }
923
924    /// Returns the location of the assertion in the source code / test code if
925    /// it has been set in the [`Spec`].
926    pub const fn location(&self) -> Option<&OwnedLocation> {
927        self.location.as_ref()
928    }
929}
930
931/// Defines the behavior when an assertion fails.
932///
933/// This crate provides two implementations:
934///
935/// * [`PanicOnFail`] - panics when an assertion fails
936/// * [`CollectFailures`] - collects [`AssertFailure`]s of assertions that have failed.
937pub trait FailingStrategy {
938    /// Reacts to an assertion that has failed with the [`AssertFailure`]s given
939    /// as argument.
940    fn do_fail_with(&self, failures: &[AssertFailure]);
941}
942
943/// [`FailingStrategy`] that panics when an assertion fails.
944#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
945pub struct PanicOnFail;
946
947impl FailingStrategy for PanicOnFail {
948    #[track_caller]
949    fn do_fail_with(&self, failures: &[AssertFailure]) {
950        let message = failures
951            .iter()
952            .map(ToString::to_string)
953            .collect::<Vec<_>>()
954            .join("\n");
955        panic!("{}", message);
956    }
957}
958
959/// [`FailingStrategy`] that collects the failures from failing assertions.
960#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
961pub struct CollectFailures;
962
963impl FailingStrategy for CollectFailures {
964    fn do_fail_with(&self, _failures: &[AssertFailure]) {
965        // do nothing by design
966    }
967}
968
969/// Used with generic types in expectations where the concrete type is not
970/// relevant for the failure message.
971///
972/// This type implements the std format trait [`std::fmt::Debug`] and
973/// [`std::fmt::Display`] which both format the value as "_".
974///
975/// ```
976/// # use asserting::prelude::*;
977/// # use asserting::spec::Unknown;
978/// assert_that!(format!("{:?}", Unknown)).is_equal_to("_");
979/// assert_that!(format!("{}", Unknown)).is_equal_to("_");
980/// ```
981///
982/// # Examples
983///
984/// This type is used to implement the expectations
985///
986/// * [`IsSome`](crate::expectations::IsSome)
987/// * [`IsNone`](crate::expectations::IsNone)
988/// * [`IsOk`](crate::expectations::IsOk)
989/// * [`IsErr`](crate::expectations::IsErr)
990///
991/// For example for implementing the function [`Expectation::message()`] for the
992/// [`IsOk`](crate::expectations::IsOk) expectation for `Result<T, E>` the
993/// concrete types for `T` and `E` are not relevant. The implementation of the
994/// trait looks like this:
995///
996/// ```no_run
997/// # use std::fmt::Debug;
998/// # use asserting::spec::Expectation;
999/// # use asserting::spec::Expression;
1000/// # use asserting::spec::Unknown;
1001/// # struct IsOk;
1002/// impl<T, E> Expectation<Result<T, E>> for IsOk
1003/// where
1004///     T: Debug,
1005///     E: Debug,
1006/// {
1007///     fn test(&mut self, subject: &Result<T, E>) -> bool {
1008///         subject.is_ok()
1009///     }
1010///
1011///     fn message(&self, expression: Expression<'_>, actual: &Result<T, E>) -> String {
1012///         format!(
1013///             "expected {expression} is {:?}\n   but was: {actual:?}\n  expected: {:?}",
1014///             Ok::<_, Unknown>(Unknown),
1015///             Ok::<_, Unknown>(Unknown),
1016///         )
1017///     }
1018/// }
1019/// ```
1020#[derive(Default, Clone, Copy, PartialEq, Eq)]
1021pub struct Unknown;
1022
1023impl Debug for Unknown {
1024    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1025        write!(f, "{self}")
1026    }
1027}
1028
1029impl Display for Unknown {
1030    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1031        write!(f, "_")
1032    }
1033}
1034
1035#[cfg(feature = "panic")]
1036pub use code::Code;
1037
1038#[cfg(feature = "panic")]
1039mod code {
1040    use std::cell::RefCell;
1041    use std::rc::Rc;
1042
1043    /// Wrapper type that holds a closure as code snippet.
1044    pub struct Code<F>(Rc<RefCell<Option<F>>>);
1045
1046    impl<F> From<F> for Code<F>
1047    where
1048        F: FnOnce(),
1049    {
1050        fn from(value: F) -> Self {
1051            Self(Rc::new(RefCell::new(Some(value))))
1052        }
1053    }
1054
1055    impl<F> Code<F> {
1056        /// Takes the closure out of this `Code` leaving it empty.
1057        #[must_use]
1058        pub fn take(&self) -> Option<F> {
1059            self.0.borrow_mut().take()
1060        }
1061    }
1062}
1063
1064#[cfg(test)]
1065mod tests;