type_rules/rules/
and.rs

1use super::Rule;
2
3#[cfg(doc)]
4use super::{All, Opt};
5
6/// Rule to ensure that 2 other rules are [`Ok`].
7///
8/// The `rule` attribute accepts multiple rules and has the same behavior
9/// but the [`And`] rule is useful for rules that accept a single rule such
10/// as [`All`] and [`Opt`].
11///
12/// In case of error on both rules, the first one is returned.
13///
14/// # Example
15/// ```
16/// use type_rules::prelude::*;
17///
18/// #[derive(Validator)]
19/// struct OptionalMail(
20///     #[rule(Opt(And(MaxLength(1000), RegEx(r"^\S+@\S+\.\S+"))))]
21///     Option<String>
22/// );
23/// ```
24pub struct And<T, U>(pub T, pub U);
25
26impl<T, U, F> Rule<F> for And<T, U>
27where
28    T: Rule<F>,
29    U: Rule<F>,
30{
31    fn check(&self, value: &F) -> Result<(), String> {
32        self.0.check(value)?;
33        self.1.check(value)
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use crate::prelude::*;
40    use claim::assert_ok;
41
42    const RULE: Opt<And<MaxLength, RegEx>> = Opt(And(MaxLength(20), RegEx(r"^\S+@\S+\.\S+")));
43
44    #[test]
45    fn and_ok() {
46        assert_ok!(RULE.check(&Some("example@example.fr")));
47    }
48
49    #[test]
50    fn and_0_err() {
51        let val = Some("too.long.example@too.long.example.fr");
52
53        let res_error_message = RULE.check(&val).expect_err("Should be an Err");
54
55        assert_eq!(res_error_message, "Value is too long")
56    }
57
58    #[test]
59    fn and_1_err() {
60        let val = Some("example.example.fr");
61
62        let res_error_message = RULE.check(&val).expect_err("Should be an Err");
63
64        assert_eq!(res_error_message, "The regex does not match")
65    }
66
67    #[test]
68    fn and_0_1_err() {
69        let val = Some("too.long.example.too.long.example.fr");
70
71        let res_error_message = RULE.check(&val).expect_err("Should be an Err");
72
73        assert_eq!(res_error_message, "Value is too long")
74    }
75}