1use crate::core::{Object, ObjectHash};
2use crate::crypto::encryption::{EncryptionConfig, EncryptionManager};
3use flate2::read::ZlibDecoder;
4use flate2::write::ZlibEncoder;
5use flate2::Compression;
6use std::fs;
7use std::io::{Read, Write};
8use std::path::{Path, PathBuf};
9use std::sync::{Arc, Mutex};
10
11pub struct ObjectStore {
13 objects_dir: PathBuf,
14 packs_dir: PathBuf,
18 encryption: Arc<Mutex<EncryptionManager>>,
19}
20
21impl ObjectStore {
22 pub fn new(repo_path: &Path) -> Self {
24 let objects_dir = repo_path.join(".lit").join("objects");
25
26 let encryption_config = EncryptionConfig::load(repo_path).unwrap_or_default();
28 let encryption = Arc::new(Mutex::new(EncryptionManager::new_auto(
29 encryption_config,
30 repo_path,
31 )));
32
33 ObjectStore {
34 objects_dir,
35 packs_dir: crate::storage::pack::packs_dir(repo_path),
36 encryption,
37 }
38 }
39
40 pub fn new_with_encryption(repo_path: &Path, passphrase: Option<&str>) -> Result<Self, String> {
42 let objects_dir = repo_path.join(".lit").join("objects");
43
44 let encryption_config = EncryptionConfig::load(repo_path)?;
46 let mut encryption_manager = EncryptionManager::new(encryption_config);
47
48 if let Some(pass) = passphrase {
50 encryption_manager.initialize(pass)?;
51 }
52
53 let encryption = Arc::new(Mutex::new(encryption_manager));
54
55 Ok(ObjectStore {
56 objects_dir,
57 packs_dir: crate::storage::pack::packs_dir(repo_path),
58 encryption,
59 })
60 }
61
62 fn object_path(&self, hash: &ObjectHash) -> PathBuf {
65 let hash_str = hash.as_str();
66 let (dir, file) = hash_str.split_at(4);
67 self.objects_dir.join(dir).join(file)
68 }
69
70 pub fn write(&self, object: &Object) -> Result<ObjectHash, String> {
72 let hash = object.hash();
73 let path = self.object_path(&hash);
74
75 if let Some(parent) = path.parent() {
77 fs::create_dir_all(parent)
78 .map_err(|e| format!("Failed to create object directory: {}", e))?;
79 }
80
81 let data = object.to_bytes();
83 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
84 encoder
85 .write_all(&data)
86 .map_err(|e| format!("Failed to compress object: {}", e))?;
87 let compressed = encoder
88 .finish()
89 .map_err(|e| format!("Failed to finish compression: {}", e))?;
90
91 let final_data = {
93 let encryption = self
94 .encryption
95 .lock()
96 .map_err(|e| format!("Failed to acquire encryption lock: {}", e))?;
97 encryption.encrypt(&compressed)?
98 };
99
100 fs::write(&path, final_data).map_err(|e| format!("Failed to write object: {}", e))?;
101
102 Ok(hash)
103 }
104
105 pub fn read(&self, hash: &ObjectHash) -> Result<Object, String> {
107 let path = self.object_path(hash);
108
109 if !path.exists() {
110 return self.read_packed(hash);
113 }
114
115 let encrypted_data =
117 fs::read(&path).map_err(|e| format!("Failed to read object: {}", e))?;
118
119 let compressed = {
121 let encryption = self
122 .encryption
123 .lock()
124 .map_err(|e| format!("Failed to acquire encryption lock: {}", e))?;
125 encryption.decrypt(&encrypted_data)?
126 };
127
128 let mut decoder = ZlibDecoder::new(&compressed[..]);
130 let mut data = Vec::new();
131 decoder
132 .read_to_end(&mut data)
133 .map_err(|e| format!("Failed to decompress object: {}", e))?;
134
135 Object::from_bytes(&data)
136 }
137
138 pub fn exists(&self, hash: &ObjectHash) -> bool {
140 self.object_path(hash).exists()
141 || crate::storage::pack::load_all(&self.packs_dir).contains_key(hash.as_str())
142 }
143
144 fn read_packed(&self, hash: &ObjectHash) -> Result<Object, String> {
146 let packed = crate::storage::pack::load_all(&self.packs_dir);
147 let Some((pack_path, offset)) = packed.get(hash.as_str()) else {
148 return Err(format!("Object {} not found", hash.short()));
149 };
150
151 let encryption = self
152 .encryption
153 .lock()
154 .map_err(|e| format!("Failed to acquire encryption lock: {}", e))?;
155
156 crate::storage::pack::read_pack_object(pack_path, *offset, &encryption)
157 .map_err(|e| format!("Failed to read {} from pack: {}", hash.short(), e))
158 }
159
160 pub fn encryption(&self) -> Arc<Mutex<EncryptionManager>> {
165 Arc::clone(&self.encryption)
166 }
167
168 pub fn list(&self) -> Result<Vec<ObjectHash>, String> {
170 let mut objects = Vec::new();
171
172 for hash in crate::storage::pack::load_all(&self.packs_dir).into_keys() {
176 objects.push(ObjectHash::from_hex(hash));
177 }
178
179 if !self.objects_dir.exists() {
180 return Ok(objects);
181 }
182
183 for entry in walkdir::WalkDir::new(&self.objects_dir)
184 .min_depth(2)
185 .max_depth(2)
186 {
187 let entry = entry.map_err(|e| format!("Failed to read objects: {}", e))?;
188
189 if entry.file_type().is_file() {
190 let path = entry.path();
191
192 if let Some(file_name) = path.file_name() {
194 if let Some(parent) = path.parent() {
195 if let Some(dir_name) = parent.file_name() {
196 let hash = format!(
197 "{}{}",
198 dir_name.to_string_lossy(),
199 file_name.to_string_lossy()
200 );
201 objects.push(ObjectHash::from_hex(hash));
202 }
203 }
204 }
205 }
206 }
207
208 objects.sort_by(|a, b| a.as_str().cmp(b.as_str()));
209 objects.dedup_by(|a, b| a.as_str() == b.as_str());
210 Ok(objects)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::core::Blob;
218 use tempfile::TempDir;
219
220 #[test]
221 fn test_object_store() {
222 let temp_dir = TempDir::new().unwrap();
223 let repo_path = temp_dir.path();
224
225 fs::create_dir_all(repo_path.join(".lit").join("objects")).unwrap();
227
228 let store = ObjectStore::new(repo_path);
229
230 let content = b"Hello, world!".to_vec();
232 let blob = Blob::new(content.clone());
233 let object = Object::Blob(blob);
234
235 let hash = store.write(&object).unwrap();
236
237 let read_object = store.read(&hash).unwrap();
239
240 match read_object {
241 Object::Blob(blob) => assert_eq!(blob.content, content),
242 _ => panic!("Expected blob"),
243 }
244 }
245}