use std::fmt;
use super::ProjectError;
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct ProjectName(String);
impl ProjectName {
pub(crate) fn parse(value: &str) -> Result<Self, ProjectError> {
let valid = !value.is_empty()
&& value.len() <= 64
&& value.as_bytes().first().is_some_and(u8::is_ascii_lowercase)
&& value
.bytes()
.all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')
&& !value.ends_with('-')
&& !value.contains("--");
if valid {
Ok(Self(value.to_owned()))
} else {
Err(ProjectError::InvalidName(value.to_owned()))
}
}
}
impl fmt::Display for ProjectName {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::ProjectName;
#[test]
fn validates_cross_ecosystem_project_names() {
assert!(ProjectName::parse("my-app2").is_ok());
for invalid in [
"",
"MyApp",
"my_app",
"../app",
"-app",
"app-",
"app--name",
"đemo",
] {
assert!(
ProjectName::parse(invalid).is_err(),
"{invalid} should be rejected"
);
}
}
}