use cargo_metadata::Package;
use url::Url;
use crate::utils::{warn, Result};
pub fn basic_checks(pkg: &Package) -> Result {
let mut problems = Vec::new();
if pkg.description.is_none() {
problems.push("'description' field should be set".to_string());
}
if pkg.license.is_none() && pkg.license_file.is_none() {
problems.push("either 'license' or 'license-file' field should be set".to_string());
}
const MAX_DESCRIPTION_LEN: usize = 1000;
if pkg
.description
.as_ref()
.map(|d| d.len() > MAX_DESCRIPTION_LEN)
.unwrap_or(false)
{
problems.push(format!(
"Description is too long (max {} characters)",
MAX_DESCRIPTION_LEN
));
}
validate_url(&pkg.homepage.as_deref(), "homepage", &mut problems);
validate_url(
&pkg.documentation.as_deref(),
"documentation",
&mut problems,
);
validate_url(&pkg.repository.as_deref(), "repository", &mut problems);
const MAX_KEYWORDS: usize = 5;
if pkg.keywords.len() > MAX_KEYWORDS {
problems.push(format!("Too many keywords (max {} keywords)", MAX_KEYWORDS));
}
const MAX_KEYWORD_LEN: usize = 20;
for kw in pkg.keywords.iter() {
if kw.len() > MAX_KEYWORD_LEN {
problems.push(format!(
"Keyword is too long (max {} characters): {}",
MAX_KEYWORD_LEN, kw
));
} else if !valid_keyword(kw) {
problems.push(format!("Keyword contains invalid characters: {}", kw));
}
}
for problem in problems {
warn!("check failed", problem);
}
Ok(())
}
fn valid_keyword(keyword: &str) -> bool {
let mut chars = keyword.chars();
let first = match chars.next() {
None => return false,
Some(c) => c,
};
first.is_ascii_alphanumeric()
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '+')
}
fn validate_url(url: &Option<&str>, field: &str, problems: &mut Vec<String>) {
let Some(url) = url else {
return;
};
if !url.starts_with("http://") && !url.starts_with("https://") {
problems.push(format!(
"URL for field `{field}` must begin with http:// or https:// (url: {url})"
));
}
if Url::parse(url).is_err() {
problems.push(format!("`{field}` is not a valid url: `{url}`"));
}
}