use anyhow::bail;
pub(crate) const MAX_BYTES: usize = 1024;
pub fn validate_cargo_package_name(name: &str) -> anyhow::Result<()> {
if name.is_empty() {
bail!("Cargo package name must not be empty");
}
if name.len() > MAX_BYTES {
bail!(
"Cargo package name is too long (max {MAX_BYTES} bytes): {} bytes",
name.len()
);
}
if name.starts_with('-') {
bail!("Cargo package name must not start with '-': {name:?}");
}
if let Some(c) = name.chars().find(|c| c.is_ascii_control()) {
bail!("Cargo package name contains control character {c:?}: {name:?}");
}
Ok(())
}
pub fn validate_npm_package_name(name: &str) -> anyhow::Result<()> {
if name.is_empty() {
bail!("npm package name must not be empty");
}
if name.len() > MAX_BYTES {
bail!(
"npm package name is too long (max {MAX_BYTES} bytes): {} bytes",
name.len()
);
}
if let Some(rest) = name.strip_prefix('@') {
let Some((scope, pkg)) = rest.split_once('/') else {
bail!("scoped npm package name must have the form @scope/name: {name:?}");
};
validate_npm_segment(scope, "scope", name)?;
validate_npm_segment(pkg, "package", name)?;
} else {
validate_npm_segment(name, "package", name)?;
}
Ok(())
}
fn validate_npm_segment(segment: &str, label: &str, full_name: &str) -> anyhow::Result<()> {
if segment.is_empty() {
bail!("npm {label} name segment must not be empty: {full_name:?}");
}
if segment.starts_with('-') {
bail!("npm {label} name must not start with '-': {full_name:?}");
}
if let Some(c) = segment.chars().find(|c| c.is_ascii_control()) {
bail!("npm {label} name contains control character {c:?}: {full_name:?}");
}
Ok(())
}