use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::resolver::rust_mod_tree::find_crate_root;
pub fn discover_rust_workspace_members(project_root: &Path) -> HashMap<String, PathBuf> {
let workspace_toml = project_root.join("Cargo.toml");
let content = match std::fs::read_to_string(&workspace_toml) {
Ok(c) => c,
Err(_) => return HashMap::new(),
};
let manifest: toml::Value = match toml::from_str(&content) {
Ok(v) => v,
Err(_) => return HashMap::new(),
};
let mut result: HashMap<String, PathBuf> = HashMap::new();
let workspace_members = manifest
.get("workspace")
.and_then(|ws| ws.get("members"))
.and_then(|m| m.as_array());
if let Some(members) = workspace_members {
for member_value in members {
let member_glob = match member_value.as_str() {
Some(s) => s,
None => continue,
};
let pattern = format!("{}/{}/Cargo.toml", project_root.display(), member_glob);
let entries = match glob::glob(&pattern) {
Ok(e) => e,
Err(_) => continue,
};
for entry in entries.flatten() {
if let Some((name, root)) = find_crate_root(&entry) {
result.insert(name, root);
}
}
}
if manifest.get("package").is_some()
&& let Some((name, root)) = find_crate_root(&workspace_toml)
{
result.entry(name).or_insert(root);
}
result
} else {
if let Some((name, root)) = find_crate_root(&workspace_toml) {
result.insert(name, root);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
fn make_workspace(root: &Path) {
fs::write(
root.join("Cargo.toml"),
"[workspace]\nmembers = [\"crates/*\"]\n",
)
.unwrap();
let alpha = root.join("crates/alpha");
fs::create_dir_all(alpha.join("src")).unwrap();
fs::write(
alpha.join("Cargo.toml"),
"[package]\nname = \"alpha\"\nversion = \"0.1.0\"\n",
)
.unwrap();
fs::write(alpha.join("src/lib.rs"), "// alpha lib\n").unwrap();
let beta = root.join("crates/beta-utils");
fs::create_dir_all(beta.join("src")).unwrap();
fs::write(
beta.join("Cargo.toml"),
"[package]\nname = \"beta-utils\"\nversion = \"0.1.0\"\n",
)
.unwrap();
fs::write(beta.join("src/lib.rs"), "// beta lib\n").unwrap();
}
#[test]
fn test_workspace_discovers_members() {
let tmp = tempfile::tempdir().unwrap();
make_workspace(tmp.path());
let members = discover_rust_workspace_members(tmp.path());
assert!(members.contains_key("alpha"), "alpha should be discovered");
assert!(
members.contains_key("beta_utils"),
"beta-utils should be normalized to beta_utils"
);
assert_eq!(members.len(), 2, "should have exactly 2 workspace members");
}
#[test]
fn test_single_crate_project() {
let tmp = tempfile::tempdir().unwrap();
let p = tmp.path();
fs::create_dir_all(p.join("src")).unwrap();
fs::write(
p.join("Cargo.toml"),
"[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n",
)
.unwrap();
fs::write(p.join("src/main.rs"), "fn main() {}").unwrap();
let members = discover_rust_workspace_members(p);
assert!(
members.contains_key("my_app"),
"single crate should be discovered as my_app"
);
assert_eq!(members.len(), 1);
}
#[test]
fn test_missing_cargo_toml_returns_empty() {
let tmp = tempfile::tempdir().unwrap();
let members = discover_rust_workspace_members(tmp.path());
assert!(
members.is_empty(),
"missing Cargo.toml should return empty map"
);
}
}