Skip to main content

ready_set_rust/
members.rs

1//! Cargo workspace member discovery.
2
3use std::collections::BTreeSet;
4use std::path::Path;
5
6use walkdir::WalkDir;
7
8const MAX_DEPTH: usize = 6;
9
10/// Discover candidate workspace members under `root`.
11///
12/// Scans for directories containing a `Cargo.toml`. Skips `target/`, hidden
13/// directories, and nested workspaces.
14#[must_use]
15pub fn discover(root: &Path) -> Vec<String> {
16    let mut out: BTreeSet<String> = BTreeSet::new();
17    let mut walker = WalkDir::new(root).max_depth(MAX_DEPTH).into_iter();
18
19    while let Some(entry) = walker.next() {
20        let Ok(entry) = entry else { continue };
21        if !entry.file_type().is_dir() {
22            continue;
23        }
24        if is_skipped(entry.path(), root) {
25            walker.skip_current_dir();
26            continue;
27        }
28
29        let manifest = entry.path().join("Cargo.toml");
30        if !manifest.is_file() || entry.path() == root {
31            continue;
32        }
33        if let Ok(raw) = std::fs::read_to_string(&manifest)
34            && raw.contains("[workspace]")
35        {
36            walker.skip_current_dir();
37            continue;
38        }
39
40        if let Ok(rel) = entry.path().strip_prefix(root) {
41            let s = rel.to_string_lossy().replace('\\', "/");
42            if !s.is_empty() {
43                out.insert(s);
44            }
45        }
46    }
47
48    out.into_iter().collect()
49}
50
51fn is_skipped(path: &Path, root: &Path) -> bool {
52    if path == root {
53        return false;
54    }
55    let Some(name) = path.file_name().and_then(|s| s.to_str()) else {
56        return false;
57    };
58    if name == "target" || name == "node_modules" || name == ".git" {
59        return true;
60    }
61    name.starts_with('.') && name != "." && name != ".."
62}