Skip to main content

wipe_daemon/
registry.rs

1//! A machine-wide registry of known wipe projects.
2//!
3//! Because collaboration is git-only, there is no server that "knows" your
4//! projects. Instead, every time the daemon serves a board it records that
5//! project's root in a small JSON file under the user's config directory, so the
6//! UI can list and switch between every board you have opened locally.
7
8use std::path::{Path, PathBuf};
9
10use serde::{Deserialize, Serialize};
11
12use wipe_core::Store;
13
14/// One registered project.
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub struct ProjectEntry {
17    /// Absolute path to the project root (the parent of `.wipe`).
18    pub path: String,
19    /// Board name, resolved when listed (best-effort).
20    #[serde(default)]
21    pub name: String,
22}
23
24#[derive(Debug, Default, Serialize, Deserialize)]
25struct RegistryFile {
26    #[serde(default)]
27    projects: Vec<String>,
28}
29
30/// Path to the registry JSON (`<config>/wipe/projects.json`).
31fn registry_path() -> Option<PathBuf> {
32    directories::ProjectDirs::from("dev", "wipe", "wipe")
33        .map(|d| d.config_dir().join("projects.json"))
34}
35
36fn load() -> RegistryFile {
37    registry_path()
38        .and_then(|p| std::fs::read(p).ok())
39        .and_then(|b| serde_json::from_slice(&b).ok())
40        .unwrap_or_default()
41}
42
43fn save(reg: &RegistryFile) {
44    if let Some(path) = registry_path() {
45        if let Some(dir) = path.parent() {
46            let _ = std::fs::create_dir_all(dir);
47        }
48        if let Ok(mut s) = serde_json::to_string_pretty(reg) {
49            s.push('\n');
50            let _ = std::fs::write(path, s);
51        }
52    }
53}
54
55/// Record a project root in the registry (idempotent). Best-effort: failures to
56/// persist are ignored so serving never breaks over a registry issue.
57pub fn register(root: &Path) {
58    let canonical = std::fs::canonicalize(root).unwrap_or_else(|_| root.to_path_buf());
59    let key = canonical.display().to_string();
60    let mut reg = load();
61    if !reg.projects.iter().any(|p| p == &key) {
62        reg.projects.push(key);
63        reg.projects.sort();
64        save(&reg);
65    }
66}
67
68/// List all registered projects that still have a `.wipe` board, annotating each
69/// with its current board name.
70pub fn list() -> Vec<ProjectEntry> {
71    load()
72        .projects
73        .into_iter()
74        .filter_map(|path| {
75            let store = Store::open(&path).ok()?;
76            let name = store.load_board().map(|b| b.name).unwrap_or_default();
77            Some(ProjectEntry { path, name })
78        })
79        .collect()
80}