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 let first_char = handle.chars().next().unwrap(); 68 if first_char == '-' || first_char == '_' { 69 return Err(HandleValidationError::StartsWithInvalidChar); 70 } 71 72 let last_char = handle.chars().last().unwrap(); 73 if last_char == '-' || last_char == '_' { 74 return Err(HandleValidationError::EndsWithInvalidChar); 75 } 76 77 for c in handle.chars() { 78 if !c.is_ascii_alphanumeric() && c != '-' && c != '_' { 79 return Err(HandleValidationError::InvalidCharacters); 80 } 81 } 82 83 Ok(handle.to_lowercase()) 84} 85 86pub fn is_valid_email(email: &str) -> bool { 87 let email = email.trim(); 88 if email.is_empty() || email.len() > MAX_EMAIL_LENGTH { 89 return false; 90 } 91 let parts: Vec<&str> = email.rsplitn(2, '@').collect(); 92 if parts.len() != 2 { 93 return false; 94 } 95 let domain = parts[0]; 96 let local = parts[1]; 97 if local.is_empty() || local.len() > MAX_LOCAL_PART_LENGTH { 98 return false; 99 } 100 if local.starts_with('.') || local.ends_with('.') { 101 return false; 102 } 103 if local.contains("..") { 104 return false; 105 } 106 for c in local.chars() { 107 if !c.is_ascii_alphanumeric() && !EMAIL_LOCAL_SPECIAL_CHARS.contains(c) { 108 return false; 109 } 110 } 111 if domain.is_empty() || domain.len() > MAX_DOMAIN_LENGTH { 112 return false; 113 } 114 if !domain.contains('.') { 115 return false; 116 } 117 for label in domain.split('.') { 118 if label.is_empty() || label.len() > MAX_DOMAIN_LABEL_LENGTH { 119 return false; 120 } 121 if label.starts_with('-') || label.ends_with('-') { 122 return false; 123 } 124 for c in label.chars() { 125 if !c.is_ascii_alphanumeric() && c != '-' { 126 return false; 127 } 128 } 129 } 130 true 131} 132 133#[cfg(test)] 134mod tests { 135 use super::*; 136 137 #[test] 138 fn test_valid_handles() { 139 assert_eq!(validate_short_handle("alice"), Ok("alice".to_string())); 140 assert_eq!(validate_short_handle("bob123"), Ok("bob123".to_string())); 141 assert_eq!( 142 validate_short_handle("user-name"), 143 Ok("user-name".to_string()) 144 ); 145 assert_eq!( 146 validate_short_handle("user_name"), 147 Ok("user_name".to_string()) 148 ); 149 assert_eq!( 150 validate_short_handle("UPPERCASE"), 151 Ok("uppercase".to_string()) 152 ); 153 assert_eq!( 154 validate_short_handle("MixedCase123"), 155 Ok("mixedcase123".to_string()) 156 ); 157 assert_eq!(validate_short_handle("abc"), Ok("abc".to_string())); 158 } 159 160 #[test] 161 fn test_invalid_handles() { 162 assert_eq!(validate_short_handle(""), Err(HandleValidationError::Empty)); 163 assert_eq!( 164 validate_short_handle(" "), 165 Err(HandleValidationError::Empty) 166 ); 167 assert_eq!( 168 validate_short_handle("ab"), 169 Err(HandleValidationError::TooShort) 170 ); 171 assert_eq!( 172 validate_short_handle("a"), 173 Err(HandleValidationError::TooShort) 174 ); 175 assert_eq!( 176 validate_short_handle("test spaces"), 177 Err(HandleValidationError::ContainsSpaces) 178 ); 179 assert_eq!( 180 validate_short_handle("test\ttab"), 181 Err(HandleValidationError::ContainsSpaces) 182 ); 183 assert_eq!( 184 validate_short_handle("-starts"), 185 Err(HandleValidationError::StartsWithInvalidChar) 186 ); 187 assert_eq!( 188 validate_short_handle("_starts"), 189 Err(HandleValidationError::StartsWithInvalidChar) 190 ); 191 assert_eq!( 192 validate_short_handle("ends-"), 193 Err(HandleValidationError::EndsWithInvalidChar) 194 ); 195 assert_eq!( 196 validate_short_handle("ends_"), 197 Err(HandleValidationError::EndsWithInvalidChar) 198 ); 199 assert_eq!( 200 validate_short_handle("test@user"), 201 Err(HandleValidationError::InvalidCharacters) 202 ); 203 assert_eq!( 204 validate_short_handle("test!user"), 205 Err(HandleValidationError::InvalidCharacters) 206 ); 207 assert_eq!( 208 validate_short_handle("test.user"), 209 Err(HandleValidationError::InvalidCharacters) 210 ); 211 } 212 213 #[test] 214 fn test_handle_trimming() { 215 assert_eq!(validate_short_handle(" alice "), Ok("alice".to_string())); 216 } 217 218 #[test] 219 fn test_valid_emails() { 220 assert!(is_valid_email("user@example.com")); 221 assert!(is_valid_email("user.name@example.com")); 222 assert!(is_valid_email("user+tag@example.com")); 223 assert!(is_valid_email("user@sub.example.com")); 224 assert!(is_valid_email("USER@EXAMPLE.COM")); 225 assert!(is_valid_email("user123@example123.com")); 226 assert!(is_valid_email("a@b.co")); 227 } 228 #[test] 229 fn test_invalid_emails() { 230 assert!(!is_valid_email("")); 231 assert!(!is_valid_email("user")); 232 assert!(!is_valid_email("user@")); 233 assert!(!is_valid_email("@example.com")); 234 assert!(!is_valid_email("user@example")); 235 assert!(!is_valid_email("user@@example.com")); 236 assert!(!is_valid_email("user@.example.com")); 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..name@example.com")); 241 assert!(!is_valid_email("user@-example.com")); 242 assert!(!is_valid_email("user@example-.com")); 243 } 244 #[test] 245 fn test_trimmed_whitespace() { 246 assert!(is_valid_email(" user@example.com ")); 247 } 248}