1use crate::error::ValidationError;
2use regex::Regex;
3use std::sync::LazyLock;
4
5static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
14 Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
15 .expect("Invalid email regex pattern")
16});
17
18pub fn validate_email(email: &str) -> Result<(), ValidationError> {
37 if email.is_empty() {
38 return Err(ValidationError::MissingField(
39 "Email is required".to_string(),
40 ));
41 }
42
43 if email.len() > 254 {
44 return Err(ValidationError::InvalidEmail(
45 "Email is too long".to_string(),
46 ));
47 }
48
49 if EMAIL_REGEX.is_match(email) {
50 Ok(())
51 } else {
52 Err(ValidationError::InvalidEmail(format!(
53 "Invalid email format: {email}"
54 )))
55 }
56}
57
58pub fn validate_password(password: &str) -> Result<(), ValidationError> {
83 if password.is_empty() {
84 return Err(ValidationError::MissingField(
85 "Password is required".to_string(),
86 ));
87 }
88
89 if password.trim().is_empty() {
90 return Err(ValidationError::InvalidPassword(
91 "Password cannot be only whitespace".to_string(),
92 ));
93 }
94
95 if password.len() < 8 {
96 return Err(ValidationError::InvalidPassword(
97 "Password must be at least 8 characters long".to_string(),
98 ));
99 }
100
101 if password.len() > 128 {
102 return Err(ValidationError::InvalidPassword(
103 "Password must be no more than 128 characters long".to_string(),
104 ));
105 }
106
107 Ok(())
108}
109
110pub fn validate_name(name: Option<&str>) -> Result<(), ValidationError> {
135 if let Some(name) = name {
136 if name.trim().is_empty() {
137 return Err(ValidationError::InvalidName(
138 "Name cannot be empty or whitespace only".to_string(),
139 ));
140 }
141
142 if name.len() > 100 {
143 return Err(ValidationError::InvalidName(
144 "Name must be no more than 100 characters long".to_string(),
145 ));
146 }
147 }
148
149 Ok(())
150}
151
152pub fn validate_user_id_string(user_id: &str) -> Result<(), ValidationError> {
178 if user_id.is_empty() {
179 return Err(ValidationError::MissingField(
180 "User ID is required".to_string(),
181 ));
182 }
183
184 if user_id.len() > 50 {
185 return Err(ValidationError::InvalidUserId(
186 "User ID must be no more than 50 characters long".to_string(),
187 ));
188 }
189
190 if !user_id
192 .chars()
193 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-' || c == '_')
194 {
195 return Err(ValidationError::InvalidUserId(
196 "User ID must contain only lowercase letters, numbers, hyphens, and underscores"
197 .to_string(),
198 ));
199 }
200
201 Ok(())
202}
203
204pub fn validate_oauth_provider(provider: &str) -> Result<(), ValidationError> {
231 if provider.is_empty() {
232 return Err(ValidationError::MissingField(
233 "OAuth provider is required".to_string(),
234 ));
235 }
236
237 if provider.len() > 50 {
238 return Err(ValidationError::InvalidProvider(
239 "OAuth provider name must be no more than 50 characters long".to_string(),
240 ));
241 }
242
243 if !provider
245 .chars()
246 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
247 {
248 return Err(ValidationError::InvalidProvider(
249 "OAuth provider name must contain only lowercase letters, numbers, and hyphens"
250 .to_string(),
251 ));
252 }
253
254 Ok(())
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_validate_email_valid() {
263 assert!(validate_email("user@example.com").is_ok());
264 assert!(validate_email("test.email+tag@domain.co.uk").is_ok());
265 assert!(validate_email("user123@test-domain.com").is_ok());
266 }
267
268 #[test]
269 fn test_validate_email_invalid() {
270 assert!(validate_email("").is_err());
271 assert!(validate_email("invalid-email").is_err());
272 assert!(validate_email("@domain.com").is_err());
273 assert!(validate_email("user@").is_err());
274 assert!(validate_email("user@domain").is_err());
275
276 let long_email = format!("{}@example.com", "a".repeat(250));
278 assert!(validate_email(&long_email).is_err());
279 }
280
281 #[test]
282 fn test_validate_password_valid() {
283 assert!(validate_password("password123").is_ok());
284 assert!(validate_password("a_very_secure_password_with_symbols!@#").is_ok());
285 assert!(validate_password("12345678").is_ok()); }
287
288 #[test]
289 fn test_validate_password_invalid() {
290 assert!(validate_password("").is_err());
291 assert!(validate_password(" ").is_err()); assert!(validate_password("short").is_err()); assert!(validate_password(&"a".repeat(129)).is_err()); }
295
296 #[test]
297 fn test_validate_name_valid() {
298 assert!(validate_name(None).is_ok());
299 assert!(validate_name(Some("John Doe")).is_ok());
300 assert!(validate_name(Some("Jane")).is_ok());
301 assert!(validate_name(Some("José María García-López")).is_ok());
302 }
303
304 #[test]
305 fn test_validate_name_invalid() {
306 assert!(validate_name(Some("")).is_err());
307 assert!(validate_name(Some(" ")).is_err()); assert!(validate_name(Some(&"a".repeat(101))).is_err()); }
310
311 #[test]
312 fn test_validate_user_id_string_valid() {
313 assert!(validate_user_id_string("usr_1234567890abcdef").is_ok());
314 assert!(validate_user_id_string("user-123").is_ok());
315 assert!(validate_user_id_string("simple_id").is_ok());
316 assert!(validate_user_id_string("12345").is_ok());
317 }
318
319 #[test]
320 fn test_validate_user_id_string_invalid() {
321 assert!(validate_user_id_string("").is_err());
322 assert!(validate_user_id_string("user@invalid").is_err()); assert!(validate_user_id_string("User_ID").is_err()); assert!(validate_user_id_string(&"a".repeat(51)).is_err()); }
326
327 #[test]
328 fn test_validate_oauth_provider_valid() {
329 assert!(validate_oauth_provider("google").is_ok());
330 assert!(validate_oauth_provider("github").is_ok());
331 assert!(validate_oauth_provider("microsoft-azure").is_ok());
332 assert!(validate_oauth_provider("auth0").is_ok());
333 }
334
335 #[test]
336 fn test_validate_oauth_provider_invalid() {
337 assert!(validate_oauth_provider("").is_err());
338 assert!(validate_oauth_provider("Google").is_err()); assert!(validate_oauth_provider("provider_name").is_err()); assert!(validate_oauth_provider("provider.name").is_err()); assert!(validate_oauth_provider(&"a".repeat(51)).is_err()); }
343}