Add PackageName::validate

This commit is contained in:
Zanie 2023-11-01 11:38:22 -05:00
parent 3d5f8249ef
commit ac906c6dc7

View file

@ -1,11 +1,11 @@
use std::fmt;
use std::fmt::{Display, Formatter};
use crate::dist_info_name::DistInfoName;
use anyhow::{anyhow, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use crate::dist_info_name::DistInfoName;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PackageName(String);
@ -23,20 +23,37 @@ impl Display for PackageName {
}
static NAME_NORMALIZE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[-_.]+").unwrap());
static NAME_VALIDATE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$").unwrap());
/// A package name.
///
/// See:
/// - <https://packaging.python.org/en/latest/specifications/name-normalization/>
/// - <https://peps.python.org/pep-0508/#names>
/// - <https://peps.python.org/pep-0503/#normalized-names>
impl PackageName {
/// Create a normalized representation of a package name.
/// Create a normalized package name without validation.
///
/// Converts the name to lowercase and collapses any run of the characters `-`, `_` and `.`
/// down to a single `-`, e.g., `---`, `.`, and `__` all get converted to just `-`.
///
/// See: <https://packaging.python.org/en/latest/specifications/name-normalization/>
pub fn normalize(name: impl AsRef<str>) -> Self {
// TODO(charlie): Avoid allocating in the common case (when no normalization is required).
let mut normalized = NAME_NORMALIZE.replace_all(name.as_ref(), "-").to_string();
normalized.make_ascii_lowercase();
Self(normalized)
}
/// Create a validated, normalized extra name.
pub fn validate(name: impl AsRef<str>) -> Result<Self> {
if NAME_VALIDATE.is_match(name.as_ref()) {
Ok(Self::normalize(name))
} else {
Err(anyhow!(
"Package names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters"
))
}
}
}
impl AsRef<str> for PackageName {
@ -86,4 +103,27 @@ mod tests {
"friendly-bard"
);
}
#[test]
fn validate() {
// Unchanged
assert_eq!(
PackageName::validate("friendly-bard").unwrap().as_ref(),
"friendly-bard"
);
assert_eq!(PackageName::validate("1okay").unwrap().as_ref(), "1okay");
assert_eq!(PackageName::validate("okay2").unwrap().as_ref(), "okay2");
// Normalizes
assert_eq!(
PackageName::validate("Friendly-Bard").unwrap().as_ref(),
"friendly-bard"
);
// Failures...
assert!(PackageName::validate(" starts-with-space").is_err());
assert!(PackageName::validate("-starts-with-dash").is_err());
assert!(PackageName::validate("ends-with-dash-").is_err());
assert!(PackageName::validate("ends-with-space ").is_err());
assert!(PackageName::validate("includes!invalid-char").is_err());
assert!(PackageName::validate("space in middle").is_err());
}
}