const WINDOWS_DEVICES: [&str; 22] = [
"con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8",
"com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
];
pub fn check_name(name: &str) -> Result<(), String> {
let Some(first) = name.chars().next() else {
return Err("a package name cannot be empty".to_string());
};
if !first.is_ascii_alphabetic() {
return Err(format!(
"package name `{name}` must start with an ASCII letter"
));
}
if let Some(bad) = name
.chars()
.find(|c| !(c.is_ascii_alphanumeric() || *c == '-' || *c == '_'))
{
return Err(format!(
"package name `{name}` contains {bad:?}; a name may use only ASCII letters, \
digits, `-` and `_`"
));
}
if name.len() > 64 {
return Err(format!(
"package name `{name}` is {} characters long; the limit is 64",
name.len()
));
}
if WINDOWS_DEVICES.contains(&name.to_ascii_lowercase().as_str()) {
return Err(format!("`{name}` is a name Windows reserves for a device"));
}
Ok(())
}
pub fn is_portable(component: &str) -> bool {
if component.is_empty() || component == "." || component == ".." {
return false;
}
if component
.chars()
.any(|c| c.is_control() || "/:\\<>\"|?*".contains(c))
{
return false;
}
if component.ends_with('.') || component.ends_with(' ') {
return false;
}
let stem = component.split('.').next().unwrap_or(component);
!WINDOWS_DEVICES.contains(&stem.to_ascii_lowercase().as_str())
}
pub fn import_name(name: &str) -> String {
name.replace('-', "_")
}