specler/specs/
string.rs

1use email_address::EmailAddress;
2use regex::Regex;
3use crate::core::validator::validator_fn::ValidatorFn;
4use crate::core::validator::validator_result::ValidatorResult;
5
6/// Validates that a string is not empty
7pub fn not_empty() -> ValidatorFn<String> {
8    Box::new(
9        move |input: String| {
10            if input.is_empty() {
11                ValidatorResult::invalid("cannot be empty")
12            } else {
13                ValidatorResult::valid()
14            }
15        }
16    )
17}
18
19/// Validates a string is shorter than a given length
20pub fn no_longer_than(length: usize) -> ValidatorFn<String> {
21    Box::new(
22        move |input: String | {
23            if input.len() <= length {
24                ValidatorResult::valid()
25            } else {
26                ValidatorResult::invalid(&format!("cannot be longer than {} characters", length))
27            }
28        }
29    )
30}
31
32/// Validates a string is longer than a given length
33pub fn no_shorter_than(length: usize) -> ValidatorFn<String> {
34    Box::new(
35        move |input: String| {
36            if input.len() >= length {
37                ValidatorResult::valid()
38            } else {
39                ValidatorResult::invalid(&format!("cannot be shorter than {} characters", length))
40            }
41        }
42    )
43}
44
45/// Validates a string matches a given regex pattern
46pub fn matching(pattern: &str) -> Box<dyn Fn(String) -> ValidatorResult + '_> {
47    Box::new(
48        move |input: String| {
49            match Regex::new(pattern) {
50                Ok(re) => {
51                    if re.is_match(&input) {
52                        ValidatorResult::valid()
53                    } else {
54                        ValidatorResult::invalid(format!("does not match the pattern '{}'", pattern).as_str())
55                    }
56                }
57                Err(_) => ValidatorResult::invalid(format!("invalid pattern '{}'", pattern).as_str())
58            }
59        }
60    )
61}
62
63/// Validates a string is a valid email address
64pub fn an_email() -> ValidatorFn<String> {
65    Box::new(
66        move |input: String| {
67            match EmailAddress::is_valid(&input) {
68                true => ValidatorResult::valid(),
69                false => ValidatorResult::invalid("is not a valid email address")
70            }
71        }
72    )
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    mod not_empty {
80        use crate::core::validator::validator_result::ValidatorResult;
81
82        #[test]
83        fn test_valid() {
84            let result = super::not_empty()("James".to_string());
85            assert_eq!(result, ValidatorResult::valid());
86        }
87
88        #[test]
89        fn test_invalid() {
90            let result = super::not_empty()("".to_string());
91            assert_eq!(result.error_message(), "cannot be empty");
92        }
93    }
94
95    mod not_be_longer_than {
96        #[test]
97        fn test_valid() {
98            let input = "James".to_string();
99            let result = super::no_longer_than(6)(input);
100            assert!(result.is_valid());
101        }
102
103        #[test]
104        fn test_invalid() {
105            let input = "James".to_string();
106            let result = super::no_longer_than(4)(input);
107            assert!(!result.is_valid());
108            assert_eq!(result.error_message(), "cannot be longer than 4 characters");
109        }
110    }
111
112    mod not_be_shorter_than {
113        #[test]
114        fn test_valid() {
115            let input = "James".to_string();
116            let result = super::no_shorter_than(4)(input);
117            assert!(result.is_valid());
118        }
119
120        #[test]
121        fn test_invalid() {
122            let input = "James".to_string();
123            let result = super::no_shorter_than(6)(input);
124            assert!(!result.is_valid());
125            assert_eq!(result.error_message(), "cannot be shorter than 6 characters");
126        }
127    }
128
129    mod matching {
130
131        const PATTERN: &str = r"^abba$";
132
133        #[test]
134        fn test_valid() {
135            let valid_input: String = String::from("abba");
136            let result = super::matching(PATTERN)(valid_input);
137            assert!(result.is_valid());
138        }
139
140        #[test]
141        fn test_invalid() {
142            let pattern = r"^[a-zA-Z]+$";
143            let invalid_input: String = String::from("oops123");
144            let result = super::matching(pattern)(invalid_input);
145            assert!(!result.is_valid());
146            assert_eq!(result.error_message(), "does not match the pattern '^[a-zA-Z]+$'");
147        }
148    }
149
150    mod email {
151
152        mod test_valid {
153
154            macro_rules! test_valid_email {
155                ($test_id:ident, $email:expr) => {
156                    #[test]
157                    fn $test_id() {
158                        let valid_input: String = String::from($email);
159                        let result = crate::specs::string::an_email()(valid_input.clone());
160                        assert!(result.is_valid(), "'{}' should be a valid email address, but is not", valid_input);
161                    }
162                };
163            }
164
165            test_valid_email!(test_valid_email_1, "a@b.something");
166            test_valid_email!(test_valid_email_2, "john.doe@some.domain.com");
167            test_valid_email!(test_valid_email_3, "john.doe+a45d8f89@abc123.com");
168        }
169
170        mod test_invalid {
171
172            macro_rules! test_invalid_email {
173                ($test_id:ident, $email:expr) => {
174                    #[test]
175                    fn $test_id() {
176                        let valid_input: String = String::from($email);
177                        let result = crate::specs::string::an_email()(valid_input.clone());
178                        assert!(!result.is_valid(), "'{}' should be an invalid email address, but is marked as valid", valid_input);
179                    }
180                };
181            }
182
183            test_invalid_email!(test_invalid_email_1, "a");
184            test_invalid_email!(test_invalid_email_2, "@b");
185        }
186    }
187}