hypercall_db/types/
username_validation.rs1const MAX_LEN: usize = 20;
4const MIN_LEN: usize = 3;
5const RESERVED_USERNAMES: &[&str] = &[
6 "admin",
7 "administrator",
8 "api",
9 "help",
10 "hypercall",
11 "moderator",
12 "root",
13 "staff",
14 "support",
15 "system",
16];
17
18pub fn validate_username(name: &str) -> Result<(), String> {
19 if name.trim() != name {
20 return Err("Username cannot start or end with whitespace".to_string());
21 }
22 if name.len() < MIN_LEN || name.len() > MAX_LEN {
23 return Err(format!(
24 "Username must be between {} and {} characters",
25 MIN_LEN, MAX_LEN
26 ));
27 }
28
29 let normalized = name.to_ascii_lowercase();
30 if RESERVED_USERNAMES.contains(&normalized.as_str()) {
31 return Err("Username is reserved".to_string());
32 }
33 if normalized.starts_with("0x") {
34 return Err("Username cannot look like a wallet address".to_string());
35 }
36 if normalized.contains(".hl") {
37 return Err("Username cannot contain \".hl\"".to_string());
38 }
39
40 for ch in name.chars() {
41 if !ch.is_ascii_alphanumeric() && ch != '_' {
42 return Err(format!(
43 "Username can only contain alphanumeric characters and underscores (found '{}')",
44 ch
45 ));
46 }
47 }
48
49 if let Some(first) = name.chars().next() {
50 if !first.is_ascii_alphanumeric() {
51 return Err("Username must start with an alphanumeric character".to_string());
52 }
53 }
54
55 Ok(())
56}