1use email_address::EmailAddress;
2use regex::Regex;
3use crate::core::validator::validator_fn::ValidatorFn;
4use crate::core::validator::validator_result::ValidatorResult;
5
6pub 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
19pub 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
32pub 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
45pub 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
63pub 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}