onefetch_manifest/
lib.rs

1use anyhow::{Context, Result};
2use std::fs;
3use std::path::Path;
4use strum::{Display, EnumIter};
5
6#[derive(Clone, PartialEq, Eq, Debug)]
7pub struct Manifest {
8    pub manifest_type: ManifestType,
9    pub number_of_dependencies: usize,
10    pub name: String,
11    pub description: Option<String>,
12    pub version: String,
13    pub license: Option<String>,
14}
15
16#[derive(Display, Clone, Copy, PartialEq, Eq, Debug, EnumIter)]
17pub enum ManifestType {
18    Npm,
19    Cargo,
20}
21
22pub fn get_manifests<P: AsRef<Path>>(path: P) -> Result<Vec<Manifest>> {
23    let manifest_paths = fs::read_dir(path)?
24        .filter_map(std::result::Result::ok)
25        .map(|entry| entry.path())
26        .filter(|entry| entry.is_file())
27        .filter_map(|file_path| {
28            let file_name = file_path
29                .file_name()
30                .and_then(|name| name.to_str())
31                .unwrap_or_default();
32            file_name_to_manifest_type(file_name).map(|manifest_type| (file_path, manifest_type))
33        })
34        .map(|(file_path, manifest_type)| match manifest_type {
35            ManifestType::Cargo => parse_cargo_manifest(&file_path),
36            ManifestType::Npm => parse_npm_manifest(&file_path),
37        })
38        .filter_map(std::result::Result::ok)
39        .collect::<Vec<_>>();
40
41    Ok(manifest_paths)
42}
43
44fn parse_cargo_manifest(path: &Path) -> Result<Manifest> {
45    let m = cargo_toml::Manifest::from_path(path)
46        .with_context(|| format!("Failed to parse Cargo.toml at '{}'", path.display()))?;
47    let package = m.package.context("Not a package (only a workspace)")?;
48    let description = package.description().map(Into::into);
49
50    Ok(Manifest {
51        manifest_type: ManifestType::Cargo,
52        number_of_dependencies: m.dependencies.len(),
53        name: package.name.clone(),
54        description,
55        version: package.version().into(),
56        license: package.license().map(Into::into),
57    })
58}
59
60fn parse_npm_manifest(path: &Path) -> Result<Manifest> {
61    let package = npm_package_json::Package::from_path(path)
62        .with_context(|| format!("Failed to parse package.json at '{}'", path.display()))?;
63    Ok(Manifest {
64        manifest_type: ManifestType::Npm,
65        number_of_dependencies: package.dependencies.len(),
66        name: package.name,
67        description: package.description,
68        version: package.version,
69        license: package.license,
70    })
71}
72
73fn file_name_to_manifest_type(filename: &str) -> Option<ManifestType> {
74    match filename {
75        "Cargo.toml" => Some(ManifestType::Cargo),
76        "package.json" => Some(ManifestType::Npm),
77        _ => None,
78    }
79}