1use std::path::{Path, PathBuf};
9
10use serde::{Deserialize, Serialize};
11
12use wipe_core::Store;
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub struct ProjectEntry {
17 pub path: String,
19 #[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
30fn 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
55pub 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(®);
65 }
66}
67
68pub 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}