Skip to main content

kaizen/core/
workspace.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! Workspace path canonicalization + machine-local registry.
3
4use anyhow::Result;
5use std::path::{Path, PathBuf};
6
7pub use crate::core::paths::{canonical, kaizen_dir, project_data_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    if let Ok(data_dir) = project_data_dir(&canonical)
17        && let Err(e) = crate::core::migrate_home::migrate_legacy_in_repo(&canonical, &data_dir)
18    {
19        tracing::warn!("legacy migration failed: {e}");
20    }
21    Ok(canonical)
22}
23
24pub fn machine_workspaces(seed: Option<&Path>) -> Result<Vec<PathBuf>> {
25    let seed = seed.map(canonical);
26    let mut roots = registry_entries()?;
27    if let Some(path) = seed.as_ref() {
28        push_unique(&mut roots, path.clone());
29    }
30    roots.retain(|p| {
31        if seed.as_ref() == Some(p) {
32            return true;
33        }
34        p.exists()
35            && (db_path(p).ok().is_some_and(|d| d.exists())
36                || crate::core::machine_registry::is_registered(p))
37    });
38    if roots.is_empty()
39        && let Some(path) = seed
40    {
41        roots.push(path);
42    }
43    Ok(roots)
44}
45
46pub fn db_path(workspace: &Path) -> Result<PathBuf> {
47    Ok(project_data_dir(workspace)?.join("kaizen.db"))
48}
49
50fn registry_entries() -> Result<Vec<PathBuf>> {
51    crate::core::machine_registry::list_paths()
52}
53
54fn push_unique(roots: &mut Vec<PathBuf>, path: PathBuf) {
55    if !roots.iter().any(|row| row == &path) {
56        roots.push(path);
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use crate::core::paths::test_lock;
64    use tempfile::TempDir;
65
66    #[test]
67    fn registry_round_trip() {
68        let _guard = test_lock::global().lock().unwrap();
69        let home = TempDir::new().unwrap();
70        let ws = home.path().join("repo");
71        std::fs::create_dir_all(&ws).unwrap();
72        unsafe { std::env::set_var("KAIZEN_HOME", home.path().join(".kaizen")) };
73        let first = resolve(Some(&ws)).unwrap();
74        let rows = machine_workspaces(Some(&first)).unwrap();
75        assert_eq!(rows, vec![first]);
76        unsafe { std::env::remove_var("KAIZEN_HOME") };
77    }
78}