Skip to main content

crap4rust/
source_root_collector.rs

1// Copyright 2025 Umberto Gotti <umberto.gotti@umbertogotti.dev>
2// Licensed under the MIT License
3// SPDX-License-Identifier: MIT
4
5use 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}