1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct RepoEntry {
9 pub id: String,
11 pub name: String,
12 pub path: PathBuf,
13 pub db_path: PathBuf,
15 pub indexed_at: String,
16 pub node_count: u64,
17 pub edge_count: u64,
18 #[serde(default)]
20 pub language_breakdown: HashMap<String, f64>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Registry {
26 #[serde(default = "default_version")]
27 pub version: u32,
28 #[serde(default)]
29 pub repos: Vec<RepoEntry>,
30}
31
32fn default_version() -> u32 {
33 1
34}
35
36impl Registry {
37 fn path() -> PathBuf {
38 dirs::home_dir()
39 .unwrap_or_else(|| PathBuf::from("."))
40 .join(".cgx")
41 .join("registry.json")
42 }
43
44 pub fn load() -> anyhow::Result<Self> {
46 let path = Self::path();
47 if path.exists() {
48 let content = std::fs::read_to_string(&path)?;
49 let registry: Registry = serde_json::from_str(&content)?;
50 Ok(registry)
51 } else {
52 if let Some(dir) = path.parent() {
53 std::fs::create_dir_all(dir)?;
54 }
55 Ok(Registry {
56 version: 1,
57 repos: Vec::new(),
58 })
59 }
60 }
61
62 pub fn save(&self) -> anyhow::Result<()> {
64 let path = Self::path();
65 if let Some(dir) = path.parent() {
66 std::fs::create_dir_all(dir)?;
67 }
68 let content = serde_json::to_string_pretty(self)?;
69 std::fs::write(&path, content)?;
70 Ok(())
71 }
72
73 pub fn register(&mut self, entry: RepoEntry) {
75 self.repos.retain(|r| r.id != entry.id);
76 self.repos.push(entry);
77 }
78
79 pub fn find_by_path(&self, path: &Path) -> Option<&RepoEntry> {
81 let canonical = path.canonicalize().ok()?;
82 self.repos
83 .iter()
84 .find(|r| r.path.canonicalize().ok().as_ref() == Some(&canonical))
85 }
86
87 pub fn find_by_id(&self, id: &str) -> Option<&RepoEntry> {
89 self.repos.iter().find(|r| r.id == id)
90 }
91}