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