ravenrs_lint/
workspace.rs1use std::path::{Path, PathBuf};
4use std::process::Command;
5
6use serde_json::Value;
7
8use crate::{Config, Error};
9
10#[derive(Clone, Debug)]
12pub struct Package {
13 pub name: String,
15 pub manifest: PathBuf,
17 pub root: PathBuf,
19 pub source_dirs: Vec<PathBuf>,
21 pub uses_ravenrs: bool,
23 pub config: Config,
25}
26
27pub fn packages(manifest: &Path) -> Result<Vec<Package>, Error> {
29 let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
30 let output = Command::new(cargo)
31 .args(["metadata", "--no-deps", "--format-version", "1", "--manifest-path"])
32 .arg(manifest)
33 .output()
34 .map_err(|error| Error::Cargo(error.to_string()))?;
35 if !output.status.success() {
36 return Err(Error::Cargo(String::from_utf8_lossy(&output.stderr).trim().to_owned()));
37 }
38 let metadata: Value =
39 serde_json::from_slice(&output.stdout).map_err(|error| Error::Cargo(error.to_string()))?;
40 let workspace = metadata.pointer("/metadata/ravenrs-lint");
41 let Some(packages) = metadata.get("packages").and_then(Value::as_array) else {
42 return Err(Error::Cargo("no packages in the answer".into()));
43 };
44 packages.iter().map(|package| read_package(package, workspace)).collect()
45}
46
47fn read_package(package: &Value, workspace: Option<&Value>) -> Result<Package, Error> {
48 let text = |key: &str| package.get(key).and_then(Value::as_str).unwrap_or_default().to_owned();
49 let name = text("name");
50 let manifest = PathBuf::from(text("manifest_path"));
51 let root = manifest.parent().map(Path::to_path_buf).unwrap_or_default();
52 let config = Config::from_metadata(workspace, package.pointer("/metadata/ravenrs-lint"))
53 .map_err(|error| Error::Config(format!("{name}: {error}")))?;
54 let uses_ravenrs = package.get("dependencies").and_then(Value::as_array).is_some_and(|deps| {
55 deps.iter()
56 .filter_map(|d| d.get("name").and_then(Value::as_str))
57 .any(|d| (d == "ravenrs" || d.starts_with("ravenrs-")) && d != "ravenrs-lint")
58 });
59 let mut dirs: Vec<PathBuf> = package
60 .get("targets")
61 .and_then(Value::as_array)
62 .into_iter()
63 .flatten()
64 .filter(|target| {
65 target.get("kind").and_then(Value::as_array).is_some_and(|kinds| {
66 kinds.iter().filter_map(Value::as_str).any(|kind| {
67 matches!(kind, "lib" | "rlib" | "dylib" | "cdylib" | "staticlib" | "bin" | "proc-macro")
68 })
69 })
70 })
71 .filter_map(|target| target.get("src_path").and_then(Value::as_str))
72 .filter_map(|path| Path::new(path).parent().map(Path::to_path_buf))
73 .collect();
74 dirs.sort();
75 dirs.dedup();
76 let source_dirs = dirs
78 .iter()
79 .filter(|dir| !dirs.iter().any(|other| other != *dir && dir.starts_with(other)))
80 .cloned()
81 .collect();
82 Ok(Package { name, manifest, root, source_dirs, uses_ravenrs, config })
83}