fallow_api/audit_run/
scope.rs1use std::path::{Path, PathBuf};
4
5use fallow_engine::changed_files::RenamedFile;
6use fallow_types::results::AnalysisResults;
7use rustc_hash::FxHashSet;
8
9#[expect(
20 clippy::implicit_hasher,
21 reason = "fallow standardizes on FxHashSet across the workspace"
22)]
23pub fn scope_dependency_findings(
24 results: &mut AnalysisResults,
25 root: &Path,
26 changed_files: &FxHashSet<PathBuf>,
27) {
28 let changed: FxHashSet<PathBuf> = changed_files
29 .iter()
30 .map(|path| dunce::simplified(path).to_path_buf())
31 .collect();
32 let declared_in_change = |path: &Path| {
36 let anchored = if path.is_absolute() {
37 path.to_path_buf()
38 } else {
39 root.join(path)
40 };
41 changed.contains(dunce::simplified(&anchored))
42 || dunce::canonicalize(&anchored).is_ok_and(|canonical| changed.contains(&canonical))
43 };
44 results
45 .unused_dependencies
46 .retain(|finding| declared_in_change(&finding.dep.path));
47 results
48 .unused_dev_dependencies
49 .retain(|finding| declared_in_change(&finding.dep.path));
50 results
51 .unused_optional_dependencies
52 .retain(|finding| declared_in_change(&finding.dep.path));
53 results
54 .type_only_dependencies
55 .retain(|finding| declared_in_change(&finding.dep.path));
56 results
57 .test_only_dependencies
58 .retain(|finding| declared_in_change(&finding.dep.path));
59 results
60 .dev_dependencies_in_production
61 .retain(|finding| declared_in_change(&finding.dep.path));
62 results
63 .unused_catalog_entries
64 .retain(|finding| declared_in_change(&finding.entry.path));
65}
66
67#[must_use]
72pub fn renamed_files(root: &Path, base_ref: &str) -> Vec<RenamedFile> {
73 fallow_engine::changed_files::try_get_renamed_files(root, base_ref).unwrap_or_default()
74}
75
76#[must_use]
80#[expect(
81 clippy::implicit_hasher,
82 reason = "fallow standardizes on FxHashSet across the workspace"
83)]
84pub fn base_focus_files(
85 changed_files: &FxHashSet<PathBuf>,
86 renames: &[RenamedFile],
87) -> FxHashSet<PathBuf> {
88 changed_files
89 .iter()
90 .cloned()
91 .chain(renames.iter().map(|rename| rename.from.clone()))
92 .collect()
93}
94
95#[must_use]
106#[expect(
107 clippy::implicit_hasher,
108 reason = "fallow standardizes on FxHashSet across the workspace"
109)]
110pub fn remap_focus_files(
111 files: &FxHashSet<PathBuf>,
112 from_root: &Path,
113 to_root: &Path,
114) -> Option<FxHashSet<PathBuf>> {
115 let simple_from = dunce::simplified(from_root).to_path_buf();
116 let canonical_from = dunce::canonicalize(from_root).unwrap_or_else(|_| simple_from.clone());
117 let mut remapped = FxHashSet::default();
118 for file in files {
119 let simple_file = dunce::simplified(file);
120 let relative = simple_file
121 .strip_prefix(&simple_from)
122 .or_else(|_| simple_file.strip_prefix(&canonical_from))
123 .map(Path::to_path_buf)
124 .ok()
125 .or_else(|| {
126 let canonical_file = dunce::canonicalize(file).ok()?;
127 canonical_file
128 .strip_prefix(&canonical_from)
129 .map(Path::to_path_buf)
130 .ok()
131 });
132 if let Some(relative) = relative {
133 remapped.insert(to_root.join(relative));
134 }
135 }
136 if remapped.is_empty() {
137 return None;
138 }
139 Some(remapped)
140}
141
142#[derive(Debug, Clone, Default, PartialEq, Eq)]
144pub struct BaseCoverageInputs {
145 pub coverage: Option<PathBuf>,
147 pub coverage_root: Option<PathBuf>,
150}
151
152#[must_use]
164pub fn base_coverage_inputs(
165 root: &Path,
166 coverage: Option<&Path>,
167 coverage_root: Option<&Path>,
168) -> BaseCoverageInputs {
169 let canonical_root = dunce::canonicalize(root).unwrap_or_else(|_| root.to_path_buf());
170 let coverage = coverage.map_or_else(
171 || fallow_engine::health::scoring::auto_detect_coverage(&canonical_root),
172 |coverage| {
173 Some(fallow_engine::health::scoring::resolve_relative_to_root(
174 coverage,
175 Some(&canonical_root),
176 ))
177 },
178 );
179 let coverage_root = match (&coverage, coverage_root) {
180 (_, Some(explicit)) => Some(explicit.to_path_buf()),
181 (Some(_), None) => Some(canonical_root),
182 (None, None) => None,
183 };
184 BaseCoverageInputs {
185 coverage,
186 coverage_root,
187 }
188}