this repo has no description
1pub const MAX_EMAIL_LENGTH: usize = 254; 2pub const MAX_LOCAL_PART_LENGTH: usize = 64; 3pub const MAX_DOMAIN_LENGTH: usize = 253; 4pub const MAX_DOMAIN_LABEL_LENGTH: usize = 63; 5const EMAIL_LOCAL_SPECIAL_CHARS: &str = ".!#$%&'*+/=?^_`{|}~-"; 6 7pub const MIN_HANDLE_LENGTH: usize = 3; 8pub const MAX_HANDLE_LENGTH: usize = 253; 9 10#[derive(Debug, PartialEq)] 11pub enum HandleValidationError { 12 Empty, 13 TooShort, 14 TooLong, 15 InvalidCharacters, 16 StartsWithInvalidChar, 17 EndsWithInvalidChar, 18 ContainsSpaces, 19} 20 21impl std::fmt::Display for HandleValidationError { 22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 match self { 24 Self::Empty => write!(f, "Handle cannot be empty"), 25 Self::TooShort => write!( 26 f, 27 "Handle must be at least {} characters", 28 MIN_HANDLE_LENGTH 29 ), 30 Self::TooLong => write!( 31 f, 32 "Handle exceeds maximum length of {} characters", 33 MAX_HANDLE_LENGTH 34 ), 35 Self::InvalidCharacters => write!( 36 f, 37 "Handle contains invalid characters. Only alphanumeric, hyphens, and underscores are allowed" 38 ), 39 Self::StartsWithInvalidChar => { 40 write!(f, "Handle cannot start with a hyphen or underscore") 41 } 42 Self::EndsWithInvalidChar => write!(f, "Handle cannot end with a hyphen or underscore"), 43 Self::ContainsSpaces => write!(f, "Handle cannot contain spaces"), 44 } 45 } 46} 47 48pub fn validate_short_handle(handle: &str) -> Result<String, HandleValidationError> { 49 let handle = handle.trim(); 50 51 if handle.is_empty() { 52 return Err(HandleValidationError::Empty); 53 } 54 55 if handle.contains(' ') || handle.contains('\t') || handle.contains('\n') { 56 return Err(HandleValidationError::ContainsSpaces); 57 } 58 59 if handle.len() < MIN_HANDLE_LENGTH { 60 return Err(HandleValidationError::TooShort); 61 } 62 63 if handle.len() > MAX_HANDLE_LENGTH { 64 return Err(HandleValidationError::TooLong); 65 } 66 67 if let Some(first_char) = handle.chars().next() { 68 if first_char == '-' || first_char == '_' { 69 return Err(HandleValidationError::StartsWithInvalidChar); 70 } 71 } 72 73 if let Some(last_char) = handle.chars().last() { 74 if last_char == '-' || last_char == '_' { 75 return Err(HandleValidationError::EndsWithInvalidChar); 76 } 77 } 78 79 for c in handle.chars() { 80 if !c.is_ascii_alphanumeric() && c != '-' && c != '_' { 81 return Err(HandleValidationError::InvalidCharacters); 82 } 83 } 84 85 Ok(handle.to_lowercase()) 86} 87 88pub fn is_valid_email(email: &str) -> bool { 89 let email = email.trim(); 90 if email.is_empty() || email.len() > MAX_EMAIL_LENGTH { 91 return false; 92 } 93 let parts: Vec<&str> = email.rsplitn(2, '@').collect(); 94 if parts.len() != 2 { 95 return false; 96 } 97 let domain = parts[0]; 98 let local = parts[1]; 99 if local.is_empty() || local.len() > MAX_LOCAL_PART_LENGTH { 100 return false; 101 } 102 if local.starts_with('.') || local.ends_with('.') { 103 return false; 104 } 105 if local.contains("..") { 106 return false; 107 } 108 for c in local.chars() { 109 if !c.is_ascii_alphanumeric() && !EMAIL_LOCAL_SPECIAL_CHARS.contains(c) { 110 return false; 111 } 112 } 113 if domain.is_empty() || domain.len() > MAX_DOMAIN_LENGTH { 114 return false; 115 } 116 if !domain.contains('.') { 117 return false; 118 } 119 for label in domain.split('.') { 120 if label.is_empty() || label.len() > MAX_DOMAIN_LABEL_LENGTH { 121 return false; 122 } 123 if label.starts_with('-') || label.ends_with('-') { 124 return false; 125 } 126 for c in label.chars() { 127 if !c.is_ascii_alphanumeric() && c != '-' { 128 return false; 129 } 130 } 131 } 132 true 133} 134 135#[cfg(test)] 136mod tests { 137 use super::*; 138 139 #[test] 140 fn test_valid_handles() { 141 assert_eq!(validate_short_handle("alice"), Ok("alice".to_string())); 142 assert_eq!(validate_short_handle("bob123"), Ok("bob123".to_string())); 143 assert_eq!( 144 validate_short_handle("user-name"), 145 Ok("user-name".to_string()) 146 ); 147 assert_eq!( 148 validate_short_handle("user_name"), 149 Ok("user_name".to_string()) 150 ); 151 assert_eq!( 152 validate_short_handle("UPPERCASE"), 153 Ok("uppercase".to_string()) 154 ); 155 assert_eq!( 156 validate_short_handle("MixedCase123"), 157 Ok("mixedcase123".to_string()) 158 ); 159 assert_eq!(validate_short_handle("abc"), Ok("abc".to_string())); 160 } 161 162 #[test] 163 fn test_invalid_handles() { 164 assert_eq!(validate_short_handle(""), Err(HandleValidationError::Empty)); 165 assert_eq!( 166 validate_short_handle(" "), 167 Err(HandleValidationError::Empty) 168 ); 169 assert_eq!( 170 validate_short_handle("ab"), 171 Err(HandleValidationError::TooShort) 172 ); 173 assert_eq!( 174 validate_short_handle("a"), 175 Err(HandleValidationError::TooShort) 176 ); 177 assert_eq!( 178 validate_short_handle("test spaces"), 179 Err(HandleValidationError::ContainsSpaces) 180 ); 181 assert_eq!( 182 validate_short_handle("test\ttab"), 183 Err(HandleValidationError::ContainsSpaces) 184 ); 185 assert_eq!( 186 validate_short_handle("-starts"), 187 Err(HandleValidationError::StartsWithInvalidChar) 188 ); 189 assert_eq!( 190 validate_short_handle("_starts"), 191 Err(HandleValidationError::StartsWithInvalidChar) 192 ); 193 assert_eq!( 194 validate_short_handle("ends-"), 195 Err(HandleValidationError::EndsWithInvalidChar) 196 ); 197 assert_eq!( 198 validate_short_handle("ends_"), 199 Err(HandleValidationError::EndsWithInvalidChar) 200 ); 201 assert_eq!( 202 validate_short_handle("test@user"), 203 Err(HandleValidationError::InvalidCharacters) 204 ); 205 assert_eq!( 206 validate_short_handle("test!user"), 207 Err(HandleValidationError::InvalidCharacters) 208 ); 209 assert_eq!( 210 validate_short_handle("test.user"), 211 Err(HandleValidationError::InvalidCharacters) 212 ); 213 } 214 215 #[test] 216 fn test_handle_trimming() { 217 assert_eq!(validate_short_handle(" alice "), Ok("alice".to_string())); 218 } 219 220 #[test] 221 fn test_valid_emails() { 222 assert!(is_valid_email("user@example.com")); 223 assert!(is_valid_email("user.name@example.com")); 224 assert!(is_valid_email("user+tag@example.com")); 225 assert!(is_valid_email("user@sub.example.com")); 226 assert!(is_valid_email("USER@EXAMPLE.COM")); 227 assert!(is_valid_email("user123@example123.com")); 228 assert!(is_valid_email("a@b.co")); 229 } 230 #[test] 231 fn test_invalid_emails() { 232 assert!(!is_valid_email("")); 233 assert!(!is_valid_email("user")); 234 assert!(!is_valid_email("user@")); 235 assert!(!is_valid_email("@example.com")); 236 assert!(!is_valid_email("user@example")); 237 assert!(!is_valid_email("user@@example.com")); 238 assert!(!is_valid_email("user@.example.com")); 239 assert!(!is_valid_email("user@example..com")); 240 assert!(!is_valid_email(".user@example.com")); 241 assert!(!is_valid_email("user.@example.com")); 242 assert!(!is_valid_email("user..name@example.com")); 243 assert!(!is_valid_email("user@-example.com")); 244 assert!(!is_valid_email("user@example-.com")); 245 } 246 #[test] 247 fn test_trimmed_whitespace() { 248 assert!(is_valid_email(" user@example.com ")); 249 } 250}