1use std::collections::HashMap;
7use std::path::PathBuf;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11#[derive(Debug, Error)]
12pub enum RegistryError {
13 #[error("Package not found: {0}")]
14 NotFound(String),
15 #[error("IO error: {0}")]
16 Io(#[from] std::io::Error),
17 #[error("JSON error: {0}")]
18 Json(#[from] serde_json::Error),
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct PackageEntry {
24 pub name: String,
25 pub version: String,
26 pub description: String,
27 pub author: String,
28 pub nodes: Vec<String>,
29 pub path: Option<PathBuf>,
30 pub checksum: Option<String>,
31}
32
33pub struct PackageRegistry {
35 pub root: PathBuf,
36 packages: HashMap<String, PackageEntry>,
37}
38
39impl PackageRegistry {
40 pub fn open() -> Result<Self, RegistryError> {
42 let root = dirs_home().join(".aether").join("registry");
43 std::fs::create_dir_all(&root)?;
44 let mut reg = Self { root: root.clone(), packages: HashMap::new() };
45 reg.load_index()?;
46 Ok(reg)
47 }
48
49 pub fn open_at(root: PathBuf) -> Result<Self, RegistryError> {
51 std::fs::create_dir_all(&root)?;
52 let mut reg = Self { root, packages: HashMap::new() };
53 reg.load_index()?;
54 Ok(reg)
55 }
56
57 fn index_path(&self) -> PathBuf {
58 self.root.join("index.json")
59 }
60
61 fn load_index(&mut self) -> Result<(), RegistryError> {
62 let path = self.index_path();
63 if path.exists() {
64 let json = std::fs::read_to_string(&path)?;
65 let entries: Vec<PackageEntry> = serde_json::from_str(&json)?;
66 for e in entries {
67 self.packages.insert(e.name.clone(), e);
68 }
69 }
70 Ok(())
71 }
72
73 fn save_index(&self) -> Result<(), RegistryError> {
74 let entries: Vec<&PackageEntry> = self.packages.values().collect();
75 let json = serde_json::to_string_pretty(&entries)?;
76 std::fs::write(self.index_path(), json)?;
77 Ok(())
78 }
79
80 pub fn install_local(&mut self, entry: PackageEntry) -> Result<(), RegistryError> {
82 self.packages.insert(entry.name.clone(), entry);
83 self.save_index()
84 }
85
86 pub fn remove(&mut self, name: &str) -> Result<(), RegistryError> {
88 if self.packages.remove(name).is_none() {
89 return Err(RegistryError::NotFound(name.to_string()));
90 }
91 self.save_index()
92 }
93
94 pub fn get(&self, name: &str) -> Option<&PackageEntry> {
96 self.packages.get(name)
97 }
98
99 pub fn list(&self) -> Vec<&PackageEntry> {
101 let mut pkgs: Vec<_> = self.packages.values().collect();
102 pkgs.sort_by_key(|p| p.name.as_str());
103 pkgs
104 }
105
106 pub fn len(&self) -> usize { self.packages.len() }
107 pub fn is_empty(&self) -> bool { self.packages.is_empty() }
108}
109
110fn dirs_home() -> PathBuf {
111 std::env::var("HOME")
112 .or_else(|_| std::env::var("USERPROFILE"))
113 .map(PathBuf::from)
114 .unwrap_or_else(|_| PathBuf::from("."))
115}