username.rs (2395B)
1 #![forbid(unsafe_code)] 2 3 #[cfg(not(feature = "std"))] 4 use alloc::string::String; 5 6 pub const RADROOTS_USERNAME_MIN_LEN: usize = 3; 7 pub const RADROOTS_USERNAME_MAX_LEN: usize = 30; 8 pub const RADROOTS_USERNAME_REGEX: &str = r"^(?!.*\.\.)(?!\.)(?!.*\.$)[a-z0-9._-]{3,30}$"; 9 10 pub fn radroots_username_is_valid(username: &str) -> bool { 11 if !username.is_ascii() { 12 return false; 13 } 14 let len = username.len(); 15 if !(RADROOTS_USERNAME_MIN_LEN..=RADROOTS_USERNAME_MAX_LEN).contains(&len) { 16 return false; 17 } 18 let bytes = username.as_bytes(); 19 if bytes.first() == Some(&b'.') || bytes.last() == Some(&b'.') { 20 return false; 21 } 22 let mut prev_dot = false; 23 for &byte in bytes { 24 if byte == b'.' { 25 if prev_dot { 26 return false; 27 } 28 prev_dot = true; 29 continue; 30 } 31 prev_dot = false; 32 let is_alpha = byte.is_ascii_lowercase(); 33 let is_digit = byte.is_ascii_digit(); 34 let is_allowed = is_alpha || is_digit || byte == b'_' || byte == b'-'; 35 if !is_allowed { 36 return false; 37 } 38 } 39 true 40 } 41 42 pub fn radroots_username_normalize(input: &str) -> Option<String> { 43 let trimmed = input.trim(); 44 if trimmed.is_empty() { 45 return None; 46 } 47 let normalized = trimmed.to_ascii_lowercase(); 48 if radroots_username_is_valid(&normalized) { 49 Some(normalized) 50 } else { 51 None 52 } 53 } 54 55 #[cfg(all(test, feature = "std"))] 56 mod tests { 57 use super::*; 58 59 #[test] 60 fn valid_usernames() { 61 for name in ["radroots", "radroots_1", "radroots.test", "rr-01"] { 62 assert!(radroots_username_is_valid(name)); 63 } 64 } 65 66 #[test] 67 fn invalid_usernames() { 68 for name in [ 69 "ra", 70 ".radroots", 71 "radroots.", 72 "radroots..test", 73 "radroots!", 74 "RADROOTS", 75 "rädroots", 76 "radroots-radroots-radroots-radroots", 77 ] { 78 assert!(!radroots_username_is_valid(name)); 79 } 80 } 81 82 #[test] 83 fn normalize_usernames() { 84 assert_eq!( 85 radroots_username_normalize(" RadRoots "), 86 Some("radroots".to_string()) 87 ); 88 assert_eq!(radroots_username_normalize("ra"), None); 89 assert_eq!(radroots_username_normalize(" "), None); 90 } 91 }