type_rules/rules/
is_in.rs

1use super::Rule;
2
3/// Rule to constrain a type to be `in` a collection
4///
5/// # Example
6/// ```
7/// use type_rules::prelude::*;
8///
9/// #[derive(Validator)]
10/// struct Fruit(#[rule(In(["apple", "banana", "orange", "pear"], "Value need to be a fruit"))] String);
11/// ```
12pub struct In<'a, T>(pub T, pub &'a str);
13
14impl<'a, T, U> Rule<U> for In<'a, T>
15where
16    T: IntoIterator,
17    for<'b> &'b T: IntoIterator<Item = &'b T::Item>,
18    U: PartialEq<T::Item>,
19{
20    fn check(&self, value: &U) -> Result<(), String> {
21        if (&self.0).into_iter().any(|v| value == v) {
22            Ok(())
23        } else {
24            Err(String::from(self.1))
25        }
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use crate::prelude::*;
32    use claim::assert_ok;
33
34    const ERROR_MESSAGE: &str = "Value need to be a fruit";
35    const RULE: In<[&str; 4]> = In(["apple", "banana", "orange", "pear"], ERROR_MESSAGE);
36
37    #[test]
38    fn in_ok() {
39        assert_ok!(RULE.check(&"banana"));
40    }
41    #[test]
42    fn in_err() {
43        let res_error_message = RULE.check(&"sandwich").expect_err("Should be an Err");
44        assert_eq!(res_error_message, ERROR_MESSAGE);
45    }
46}