pub fn ecosystem_from_path(path: &str) -> Option<&'static str> {
let name = path.rsplit(['/', '\\']).next().unwrap_or(path);
let eco = match name {
"package.json" | "package-lock.json" | "npm-shrinkwrap.json" | "yarn.lock"
| "pnpm-lock.yaml" => "npm",
"requirements.txt" | "Pipfile" | "Pipfile.lock" | "pyproject.toml" | "poetry.lock"
| "setup.py" => "pypi",
"Cargo.toml" | "Cargo.lock" => "crates",
"go.mod" | "go.sum" => "go",
"pom.xml" | "build.gradle" | "build.gradle.kts" => "maven",
"composer.json" | "composer.lock" => "packagist",
"Gemfile" | "Gemfile.lock" => "rubygems",
"packages.config" => "nuget",
"pubspec.yaml" | "pubspec.lock" => "pub",
_ => "",
};
if !eco.is_empty() {
return Some(eco);
}
if name.starts_with("requirements-") && name.ends_with(".txt") {
return Some("pypi");
}
if name.ends_with(".csproj") {
return Some("nuget");
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonical_manifests_map_to_their_ecosystem() {
let cases = [
("package.json", "npm"),
("package-lock.json", "npm"),
("yarn.lock", "npm"),
("pnpm-lock.yaml", "npm"),
("requirements.txt", "pypi"),
("pyproject.toml", "pypi"),
("Pipfile", "pypi"),
("poetry.lock", "pypi"),
("Cargo.toml", "crates"),
("Cargo.lock", "crates"),
("go.mod", "go"),
("go.sum", "go"),
("pom.xml", "maven"),
("build.gradle", "maven"),
("build.gradle.kts", "maven"),
("composer.json", "packagist"),
("Gemfile", "rubygems"),
("Gemfile.lock", "rubygems"),
("packages.config", "nuget"),
("pubspec.yaml", "pub"),
];
for (file, eco) in cases {
assert_eq!(ecosystem_from_path(file), Some(eco), "{file}");
}
}
#[test]
fn inspects_only_the_basename_of_a_full_path() {
assert_eq!(ecosystem_from_path("/home/dev/proj/package.json"), Some("npm"));
assert_eq!(ecosystem_from_path("./sub/dir/Cargo.toml"), Some("crates"));
assert_eq!(ecosystem_from_path(r"C:\proj\go.mod"), Some("go"));
}
#[test]
fn patterned_names_are_detected() {
assert_eq!(ecosystem_from_path("requirements-dev.txt"), Some("pypi"));
assert_eq!(ecosystem_from_path("requirements-test.txt"), Some("pypi"));
assert_eq!(ecosystem_from_path("MyApp.csproj"), Some("nuget"));
}
#[test]
fn unrecognised_or_ambiguous_returns_none() {
assert_eq!(ecosystem_from_path("deps.txt"), None);
assert_eq!(ecosystem_from_path("README.md"), None);
assert_eq!(ecosystem_from_path("manifest"), None);
assert_eq!(ecosystem_from_path(""), None);
}
}