crap4rust/
cargo_package_resolver.rs1use std::collections::BTreeSet;
6use std::path::{Path, PathBuf};
7
8use anyhow::{Context, Result, bail};
9use cargo_metadata::{Metadata, MetadataCommand, Package};
10
11use crate::config::Config;
12use crate::package_context::PackageContext;
13use crate::source_root_collector::SourceRootCollector;
14use crate::traits::package_resolver::PackageResolver;
15
16#[derive(Debug, Clone, Copy, Default)]
17pub struct CargoPackageResolver;
18
19impl CargoPackageResolver {
20 #[must_use]
21 pub const fn new() -> Self {
22 Self
23 }
24
25 fn build_package_context(
26 package: &Package,
27 workspace_root: &Path,
28 include_test_targets: bool,
29 exclude_paths: Vec<String>,
30 ) -> Result<PackageContext> {
31 let manifest_dir = package
32 .manifest_path
33 .clone()
34 .into_std_path_buf()
35 .parent()
36 .map(PathBuf::from)
37 .context("package manifest has no parent directory")?;
38
39 let mut collector = SourceRootCollector::new(include_test_targets, &manifest_dir);
40 collector.collect(&package.targets);
41 let source_roots = collector.finalize();
42
43 Ok(PackageContext {
44 name: package.name.to_string(),
45 manifest_dir,
46 workspace_root: workspace_root.to_path_buf(),
47 source_roots,
48 include_test_targets,
49 exclude_paths,
50 })
51 }
52
53 fn select_packages<'a>(
54 metadata: &'a Metadata,
55 requested: &[String],
56 ) -> Result<Vec<&'a Package>> {
57 if !requested.is_empty() {
58 let mut selected = Vec::new();
59 for package_name in requested {
60 let package = metadata
61 .packages
62 .iter()
63 .find(|package| package.name == package_name)
64 .with_context(|| {
65 format!("package {package_name} was not found in the manifest")
66 })?;
67 selected.push(package);
68 }
69 return Ok(selected);
70 }
71
72 if let Some(root) = metadata.root_package()
73 && metadata.workspace_members.len() <= 1
74 {
75 return Ok(vec![root]);
76 }
77
78 let workspace_member_ids = metadata
79 .workspace_members
80 .iter()
81 .cloned()
82 .collect::<BTreeSet<_>>();
83 let selected = metadata
84 .packages
85 .iter()
86 .filter(|package| workspace_member_ids.contains(&package.id))
87 .collect::<Vec<_>>();
88 if selected.is_empty() {
89 bail!("manifest contains no workspace member packages");
90 }
91
92 Ok(selected)
93 }
94}
95
96impl PackageResolver for CargoPackageResolver {
97 fn resolve(&self, config: &Config) -> Result<Vec<PackageContext>> {
98 let mut command = MetadataCommand::new();
99 command.no_deps();
100 if let Some(manifest_path) = &config.manifest_path {
101 command.manifest_path(manifest_path);
102 }
103
104 let metadata = command.exec().context("failed to read Cargo metadata")?;
105 let workspace_root = metadata.workspace_root.clone().into_std_path_buf();
106 let packages = Self::select_packages(&metadata, &config.packages)?;
107
108 packages
109 .into_iter()
110 .map(|package| {
111 Self::build_package_context(
112 package,
113 &workspace_root,
114 config.include_test_targets,
115 config.exclude_paths.clone(),
116 )
117 })
118 .collect()
119 }
120}