| 1 | /// Routes and reserved words that may not be registered as usernames. |
| 2 | const RESERVED: &[&str] = &[ |
| 3 | "api", "mcp", "login", "logout", "register", "new", "settings", "search", "admin", "auth", |
| 4 | "attempts", "oauth", "assets", "docs", "explore", "g1t", "about", "pricing", "terms", |
| 5 | "privacy", "help", "support", "status", "blog", "verify", "forgot", "reset", "device", |
| 6 | ]; |
| 7 | |
| 8 | /// Namespaces follow GitHub's rules: letters, digits and single hyphens, |
| 9 | /// not starting or ending with a hyphen, at most 39 characters. |
| 10 | pub fn is_valid_namespace(value: &str) -> bool { |
| 11 | let bytes = value.as_bytes(); |
| 12 | !bytes.is_empty() |
| 13 | && bytes.len() <= 39 |
| 14 | && bytes |
| 15 | .iter() |
| 16 | .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || *byte == b'-') |
| 17 | && !value.starts_with('-') |
| 18 | && !value.ends_with('-') |
| 19 | && !value.contains("--") |
| 20 | && !RESERVED.contains(&value) |
| 21 | } |
| 22 | |
| 23 | pub fn is_valid_repo_name(value: &str) -> bool { |
| 24 | !value.is_empty() |
| 25 | && value.len() <= 100 |
| 26 | && value.bytes().all(|byte| { |
| 27 | byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'.' | b'_' | b'-') |
| 28 | }) |
| 29 | && !value.starts_with('.') |
| 30 | && !value.ends_with(".git") |
| 31 | } |