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