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 let package = m.package.context("Not a package (only a workspace)")?;
47 let description = package.description().map(|v| v.into());
48
49 Ok(Manifest {
50 manifest_type: ManifestType::Cargo,
51 number_of_dependencies: m.dependencies.len(),
52 name: package.name.clone(),
53 description,
54 version: package.version().into(),
55 license: package.license().map(|x| x.into()),
56 })
57}
58
59fn parse_npm_manifest(path: &Path) -> Result<Manifest> {
60 let package = npm_package_json::Package::from_path(path)?;
61 Ok(Manifest {
62 manifest_type: ManifestType::Npm,
63 number_of_dependencies: package.dependencies.len(),
64 name: package.name,
65 description: package.description,
66 version: package.version,
67 license: package.license,
68 })
69}
70
71fn file_name_to_manifest_type(filename: &str) -> Option<ManifestType> {
72 match filename {
73 "Cargo.toml" => Some(ManifestType::Cargo),
74 "package.json" => Some(ManifestType::Npm),
75 _ => None,
76 }
77}