pub fn validate_account_name(account: &str, account_types: &[String]) -> Option<String> {
if account.is_empty() {
return Some("account name is empty".to_string());
}
let parts: Vec<&str> = account.split(':').collect();
if parts.is_empty() {
return Some("account name has no components".to_string());
}
let root = parts[0];
if !account_types.iter().any(|t| t == root) {
return Some(format!(
"account must start with one of: {}",
account_types.join(", ")
));
}
for (i, part) in parts.iter().enumerate() {
if part.is_empty() {
return Some(format!("component {} is empty", i + 1));
}
let Some(first_char) = part.chars().next() else {
return Some(format!("component {} is empty", i + 1));
};
let is_valid_start = first_char.is_ascii_uppercase()
|| first_char.is_ascii_digit()
|| !first_char.is_ascii();
if !is_valid_start {
return Some(format!(
"component '{part}' must start with uppercase letter or digit"
));
}
for c in part.chars().skip(1) {
if !c.is_ascii_alphanumeric() && c != '-' && c.is_ascii() {
return Some(format!(
"component '{part}' contains invalid character '{c}'"
));
}
}
}
None }