crap4rust/
source_root_collector.rs1use std::collections::BTreeSet;
6use std::path::{Path, PathBuf};
7
8use cargo_metadata::Target;
9
10pub struct SourceRootCollector<'a> {
11 include_test_targets: bool,
12 manifest_dir: &'a Path,
13 source_roots: BTreeSet<PathBuf>,
14}
15
16impl<'a> SourceRootCollector<'a> {
17 pub fn new(include_test_targets: bool, manifest_dir: &'a Path) -> Self {
18 Self {
19 include_test_targets,
20 manifest_dir,
21 source_roots: BTreeSet::new(),
22 }
23 }
24
25 pub fn collect(&mut self, targets: &[Target]) {
26 for target in targets {
27 self.process_target(target);
28 }
29 }
30
31 pub fn process_target(&mut self, target: &Target) {
32 if !Self::is_selected_target(target, self.include_test_targets) {
33 return;
34 }
35
36 if target
37 .src_path
38 .extension()
39 .is_some_and(|extension| extension == "rs")
40 {
41 let path = target.src_path.clone().into_std_path_buf();
42 if let Some(parent) = path.parent() {
43 self.source_roots.insert(parent.to_path_buf());
44 }
45 }
46 }
47
48 pub fn finalize(mut self) -> Vec<PathBuf> {
49 if self.source_roots.is_empty() {
50 self.source_roots.insert(self.manifest_dir.join("src"));
51 }
52 self.source_roots.into_iter().collect()
53 }
54
55 pub fn is_selected_target(target: &Target, include_test_targets: bool) -> bool {
56 let kinds = target
57 .kind
58 .iter()
59 .map(|kind| kind.to_string())
60 .collect::<Vec<_>>();
61
62 if kinds.iter().any(|kind| kind == "custom-build") {
63 return false;
64 }
65
66 if include_test_targets {
67 return kinds.iter().any(|kind| {
68 matches!(
69 kind.as_str(),
70 "lib"
71 | "bin"
72 | "proc-macro"
73 | "rlib"
74 | "dylib"
75 | "cdylib"
76 | "staticlib"
77 | "test"
78 )
79 });
80 }
81
82 if kinds
83 .iter()
84 .any(|kind| matches!(kind.as_str(), "test" | "bench" | "example"))
85 {
86 return false;
87 }
88
89 kinds.iter().any(|kind| {
90 matches!(
91 kind.as_str(),
92 "lib" | "bin" | "proc-macro" | "rlib" | "dylib" | "cdylib" | "staticlib"
93 )
94 })
95 }
96}