1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::core::style::AT_LESAT_ONE_NOT_OK_MSG;
use crate::core::{DispatchFormat, MatchError, Matcher};
use crate::matchers::{ChainAssertion, ChainMatcher};

use super::{FailureFormat, MessageFormat};

/// Succeeds when all of the passed matchers succeed.
///
/// This method is similar to [`each`], except it short-circuits on the first failed match and
/// chains the output of each matcher into the next.
///
/// This matcher accepts a closure which is passed a [`ChainAssertion`] value, which is similar to
/// the [`Assertion`] value returned by [`expect!`]. You can call [`to`] and [`to_not`] on it to use
/// matchers.
///
/// This matcher has some limitations compared to [`each`] when you negate it (such as with
/// [`not`]):
///
/// 1. If it succeeds (meaning that all the matchers failed), it can't return the transformed value
///    at the end. Matchers don't return the value that was passed into them when they fail.
/// 2. If it fails (meaning that all the matchers succeeded), the output it produces won't be
///    particularly useful. Matchers don't produce failure output when they succeed.
///
/// # Examples
///
/// Unlike [`each`], this matcher lets you chain matchers together to do things like this.
///
/// ```
/// use xpct::{expect, all, equal, be_some};
///
/// fn favorite_clothing() -> Option<String> {
///     Some(String::from("necktie"))
/// }
///
/// expect!(favorite_clothing()).to(all(|ctx| ctx
///     .to(be_some())?
///     .to(equal("necktie"))
/// ));
/// ```
///
/// Instead of returning the transformed value at the end, when negated, it returns `()`.
///
/// ```
/// use xpct::{expect, all, equal, be_some};
///
/// fn favorite_clothing() -> Option<String> {
///     None
/// }
///
/// let result: () = expect!(favorite_clothing())
///     .to_not(all(|ctx| ctx
///         .to(be_some())?
///         .to(equal("necktie"))
///     ))
///     .into_inner();
/// ```
///
/// [`each`]: crate::each
/// [`Assertion`]: crate::core::Assertion
/// [`to`]: crate::matchers::ChainAssertion::to
/// [`to_not`]: crate::matchers::ChainAssertion::to_not
/// [`not`]: crate::not
/// [`expect!`]: crate::expect
pub fn all<'a, In, Out>(
    block: impl FnOnce(ChainAssertion<In>) -> Result<ChainAssertion<Out>, MatchError> + 'a,
) -> Matcher<'a, In, Out, ()>
where
    In: 'a,
    Out: 'a,
{
    let format = DispatchFormat::new(
        FailureFormat::new(),
        MessageFormat::new("", AT_LESAT_ONE_NOT_OK_MSG),
    );

    Matcher::new(ChainMatcher::new(block), format)
}

#[cfg(test)]
mod tests {
    use super::all;
    use crate::{be_gt, be_lt, expect};

    #[test]
    fn succeeds_when_all_matchers_succeed() {
        expect!(1).to(all(|ctx| ctx.to(be_lt(2))?.to(be_gt(0))));
    }

    #[test]
    fn succeeds_when_not_all_matchers_succeed() {
        expect!(1).to_not(all(|ctx| ctx.to(be_lt(0))?.to(be_gt(0))));
    }

    #[test]
    #[should_panic]
    fn fails_when_all_matchers_succeed() {
        expect!(1).to_not(all(|ctx| ctx.to(be_lt(2))?.to(be_gt(0))));
    }

    #[test]
    #[should_panic]
    fn fails_when_not_all_matchers_succeed() {
        expect!(1).to(all(|ctx| ctx.to(be_lt(0))?.to(be_gt(0))));
    }
}