1use anyhow::Result;
5use std::path::{Path, PathBuf};
6
7pub use crate::core::paths::{canonical, kaizen_dir};
8
9pub fn resolve(path: Option<&Path>) -> Result<PathBuf> {
10 let root = path
11 .map(Path::to_path_buf)
12 .map(Ok)
13 .unwrap_or_else(std::env::current_dir)?;
14 let canonical = canonical(&root);
15 let _ = crate::core::machine_registry::upsert_from_resolve(&canonical);
16 Ok(canonical)
17}
18
19pub fn machine_workspaces(seed: Option<&Path>) -> Result<Vec<PathBuf>> {
20 let seed = seed.map(canonical);
21 let mut roots = registry_entries()?;
22 if let Some(path) = seed.as_ref() {
23 push_unique(&mut roots, path.clone());
24 }
25 roots.retain(|p| {
26 if seed.as_ref() == Some(p) {
27 return true;
28 }
29 p.exists() && (db_path(p).exists() || crate::core::machine_registry::is_registered(p))
30 });
31 if roots.is_empty()
32 && let Some(path) = seed
33 {
34 roots.push(path);
35 }
36 Ok(roots)
37}
38
39pub fn db_path(workspace: &Path) -> PathBuf {
40 workspace.join(".kaizen/kaizen.db")
41}
42
43fn registry_entries() -> Result<Vec<PathBuf>> {
44 crate::core::machine_registry::list_paths()
45}
46
47fn push_unique(roots: &mut Vec<PathBuf>, path: PathBuf) {
48 if !roots.iter().any(|row| row == &path) {
49 roots.push(path);
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::core::paths::test_lock;
57 use tempfile::TempDir;
58
59 #[test]
60 fn registry_round_trip() {
61 let _guard = test_lock::global().lock().unwrap();
62 let home = TempDir::new().unwrap();
63 let ws = home.path().join("repo");
64 std::fs::create_dir_all(&ws).unwrap();
65 unsafe { std::env::set_var("KAIZEN_HOME", home.path().join(".kaizen")) };
66 let first = resolve(Some(&ws)).unwrap();
67 let rows = machine_workspaces(Some(&first)).unwrap();
68 assert_eq!(rows, vec![first]);
69 unsafe { std::env::remove_var("KAIZEN_HOME") };
70 }
71}