use std::path::{Path, PathBuf};
use crate::core::trusty_tools_config::{TrustyToolsConfig, workspace_root};
pub const OLD_ROOT_SEGMENTS: [&str; 2] = [".trusty-mpm", "workspaces"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkspaceEra {
Old,
New,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiscoveredWorkspace {
pub path: PathBuf,
pub era: WorkspaceEra,
}
pub fn old_root_at(home: &Path) -> PathBuf {
let mut p = home.to_path_buf();
for seg in OLD_ROOT_SEGMENTS {
p.push(seg);
}
p
}
pub fn discovered_roots(
config: &TrustyToolsConfig,
home: Option<&Path>,
) -> (Option<PathBuf>, PathBuf) {
let old = home.map(old_root_at);
let new = workspace_root(config);
(old, new)
}
fn leaf_dirs(root: &Path, depth: usize) -> Vec<PathBuf> {
fn child_dirs(dir: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
out.push(path);
}
}
}
out
}
let mut frontier = vec![root.to_path_buf()];
for _ in 0..depth {
let mut next = Vec::new();
for dir in &frontier {
next.extend(child_dirs(dir));
}
frontier = next;
}
frontier
}
pub fn scan_workspaces(
config: &TrustyToolsConfig,
home: Option<&Path>,
) -> Vec<DiscoveredWorkspace> {
let (old_root, new_root) = discovered_roots(config, home);
let mut found = Vec::new();
if let Some(old) = old_root.filter(|p| p.is_dir()) {
for path in leaf_dirs(&old, 2) {
found.push(DiscoveredWorkspace {
path,
era: WorkspaceEra::Old,
});
}
}
if new_root.is_dir() {
for path in leaf_dirs(&new_root, 3) {
found.push(DiscoveredWorkspace {
path,
era: WorkspaceEra::New,
});
}
}
found
}
#[cfg(test)]
mod tests {
use super::*;
fn mkdirs(base: &Path, segments: &[&str]) -> PathBuf {
let mut p = base.to_path_buf();
for s in segments {
p.push(s);
}
std::fs::create_dir_all(&p).unwrap();
p
}
fn config_with_new_root(new_root: &Path) -> TrustyToolsConfig {
TrustyToolsConfig {
workspace_root_template: Some(new_root.to_string_lossy().to_string()),
..Default::default()
}
}
fn clear_env() -> std::sync::MutexGuard<'static, ()> {
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
let guard = LOCK.lock().unwrap_or_else(|e| e.into_inner());
unsafe { std::env::remove_var(super::super::trusty_tools_config::WORKSPACE_ROOT_ENV) };
guard
}
#[test]
fn dual_root_scan_finds_both_eras() {
let _g = clear_env();
let home = tempfile::TempDir::new().unwrap();
let new_root_dir = tempfile::TempDir::new().unwrap();
let cfg = config_with_new_root(new_root_dir.path());
let old_leaf = mkdirs(
home.path(),
&[".trusty-mpm", "workspaces", "trusty-tools", "sess-old-1"],
);
let new_leaf = mkdirs(
new_root_dir.path(),
&["bobmatnyc", "trusty-tools", "sess-new-1"],
);
let found = scan_workspaces(&cfg, Some(home.path()));
let paths: Vec<&PathBuf> = found.iter().map(|w| &w.path).collect();
assert!(
paths.contains(&&old_leaf),
"old-root workspace must be discovered: {found:?}"
);
assert!(
paths.contains(&&new_leaf),
"new-root workspace must be discovered: {found:?}"
);
let old_era = found.iter().find(|w| w.path == old_leaf).unwrap().era;
let new_era = found.iter().find(|w| w.path == new_leaf).unwrap().era;
assert_eq!(old_era, WorkspaceEra::Old);
assert_eq!(new_era, WorkspaceEra::New);
}
#[test]
fn old_root_only() {
let _g = clear_env();
let home = tempfile::TempDir::new().unwrap();
let new_root_dir = tempfile::TempDir::new().unwrap(); let cfg = config_with_new_root(new_root_dir.path());
let old_leaf = mkdirs(
home.path(),
&[".trusty-mpm", "workspaces", "proj", "sess-1"],
);
let found = scan_workspaces(&cfg, Some(home.path()));
assert_eq!(found.len(), 1, "{found:?}");
assert_eq!(found[0].path, old_leaf);
assert_eq!(found[0].era, WorkspaceEra::Old);
}
#[test]
fn new_root_only() {
let _g = clear_env();
let home = tempfile::TempDir::new().unwrap(); let new_root_dir = tempfile::TempDir::new().unwrap();
let cfg = config_with_new_root(new_root_dir.path());
let new_leaf = mkdirs(new_root_dir.path(), &["acme", "widget", "sess-9"]);
let found = scan_workspaces(&cfg, Some(home.path()));
assert_eq!(found.len(), 1, "{found:?}");
assert_eq!(found[0].path, new_leaf);
assert_eq!(found[0].era, WorkspaceEra::New);
}
#[test]
fn absent_roots_yield_empty() {
let _g = clear_env();
let home = tempfile::TempDir::new().unwrap();
let cfg = TrustyToolsConfig {
workspace_root_template: Some(
home.path()
.join("does-not-exist")
.to_string_lossy()
.to_string(),
),
..Default::default()
};
let found = scan_workspaces(&cfg, Some(home.path()));
assert!(found.is_empty(), "{found:?}");
}
}