Skip to main content

crap4rust/
source_root_collector.rs

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