1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::path::Path;
4use std::path::PathBuf;
5use std::sync::Arc;
6
7use rayon::iter::IntoParallelIterator;
8use rayon::iter::ParallelIterator;
9use serde::Deserialize;
10use serde::Serialize;
11
12use crate::change::Change;
13use crate::change::ChangeLog;
14use crate::error::DatabaseError;
15use crate::file::File;
16use crate::file::FileId;
17use crate::file::FileType;
18use crate::file::line_starts;
19use crate::operation::FilesystemOperation;
20
21mod utils;
22
23pub mod change;
24pub mod error;
25pub mod exclusion;
26pub mod file;
27pub mod loader;
28
29mod operation;
30
31#[derive(Debug, Clone, Default, Serialize, Deserialize)]
44pub struct Database {
45 files: HashMap<Cow<'static, str>, Arc<File>>,
47 id_to_name: HashMap<FileId, Cow<'static, str>>,
49}
50
51#[derive(Debug)]
59pub struct ReadDatabase {
60 files: Vec<Arc<File>>,
62 id_to_index: HashMap<FileId, usize>,
64 name_to_index: HashMap<Cow<'static, str>, usize>,
66 path_to_index: HashMap<PathBuf, usize>,
68}
69
70impl Database {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn add(&mut self, file: File) -> FileId {
78 let name = file.name.clone();
79 let id = file.id;
80
81 if let Some(old_file) = self.files.insert(name.clone(), Arc::new(file)) {
82 self.id_to_name.remove(&old_file.id);
83 }
84
85 self.id_to_name.insert(id, name);
86
87 id
88 }
89
90 pub fn update(&mut self, id: FileId, new_contents: Cow<'static, str>) -> bool {
95 if let Some(name) = self.id_to_name.get(&id)
96 && let Some(file) = self.files.get_mut(name)
97 && let Some(file) = Arc::get_mut(file)
98 {
99 file.contents = new_contents;
100 file.size = file.contents.len() as u32;
101 file.lines = line_starts(file.contents.as_ref()).collect();
102 return true;
103 }
104 false
105 }
106
107 pub fn delete(&mut self, id: FileId) -> bool {
111 if let Some(name) = self.id_to_name.remove(&id) { self.files.remove(&name).is_some() } else { false }
112 }
113
114 pub fn commit(&mut self, change_log: ChangeLog, write_to_disk: bool) -> Result<(), DatabaseError> {
128 let changes = change_log.into_inner()?;
129 let mut fs_operations = if write_to_disk { Vec::new() } else { Vec::with_capacity(0) };
130
131 for change in changes {
132 match change {
133 Change::Add(file) => {
134 if write_to_disk && let Some(path) = &file.path {
135 fs_operations.push(FilesystemOperation::Write(path.clone(), file.contents.clone()));
136 }
137
138 self.add(file);
139 }
140 Change::Update(id, contents) => {
141 if write_to_disk
142 && let Ok(file) = self.get(&id)
143 && let Some(path) = &file.path
144 {
145 fs_operations.push(FilesystemOperation::Write(path.clone(), contents.clone()));
146 }
147
148 self.update(id, contents);
149 }
150 Change::Delete(id) => {
151 if write_to_disk
152 && let Ok(file) = self.get(&id)
153 && let Some(path) = &file.path
154 {
155 fs_operations.push(FilesystemOperation::Delete(path.clone()));
156 }
157
158 self.delete(id);
159 }
160 }
161 }
162
163 if write_to_disk {
165 fs_operations.into_par_iter().try_for_each(|op| -> Result<(), DatabaseError> { op.execute() })?;
166 }
167
168 Ok(())
169 }
170
171 pub fn read_only(&self) -> ReadDatabase {
178 let mut files_vec: Vec<Arc<File>> = self.files.values().cloned().collect();
179 files_vec.sort_unstable_by_key(|f| f.id);
180
181 let mut id_to_index = HashMap::with_capacity(files_vec.len());
182 let mut name_to_index = HashMap::with_capacity(files_vec.len());
183 let mut path_to_index = HashMap::with_capacity(files_vec.len());
184
185 for (index, file) in files_vec.iter().enumerate() {
186 id_to_index.insert(file.id, index);
187 name_to_index.insert(file.name.clone(), index);
188 if let Some(path) = &file.path {
189 path_to_index.insert(path.clone(), index);
190 }
191 }
192
193 ReadDatabase { files: files_vec, id_to_index, name_to_index, path_to_index }
194 }
195}
196
197impl ReadDatabase {
198 pub fn single(file: File) -> Self {
208 let mut id_to_index = HashMap::with_capacity(1);
209 let mut name_to_index = HashMap::with_capacity(1);
210 let mut path_to_index = HashMap::with_capacity(1);
211
212 id_to_index.insert(file.id, 0);
214 name_to_index.insert(file.name.clone(), 0);
215 if let Some(path) = &file.path {
216 path_to_index.insert(path.clone(), 0);
217 }
218
219 Self { files: vec![Arc::new(file)], id_to_index, name_to_index, path_to_index }
220 }
221}
222
223pub trait DatabaseReader {
229 fn get_id(&self, name: &str) -> Option<FileId>;
231
232 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError>;
238
239 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError>;
245
246 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError>;
252
253 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError>;
259
260 fn files(&self) -> impl Iterator<Item = Arc<File>>;
265
266 fn files_with_type(&self, file_type: FileType) -> impl Iterator<Item = Arc<File>> {
268 self.files().filter(move |file| file.file_type == file_type)
269 }
270
271 fn files_without_type(&self, file_type: FileType) -> impl Iterator<Item = Arc<File>> {
273 self.files().filter(move |file| file.file_type != file_type)
274 }
275
276 fn file_ids(&self) -> impl Iterator<Item = FileId> {
278 self.files().map(|file| file.id)
279 }
280
281 fn file_ids_with_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
283 self.files_with_type(file_type).map(|file| file.id)
284 }
285
286 fn file_ids_without_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
288 self.files_without_type(file_type).map(|file| file.id)
289 }
290
291 fn len(&self) -> usize;
293
294 fn is_empty(&self) -> bool {
296 self.len() == 0
297 }
298}
299
300impl DatabaseReader for Database {
301 fn get_id(&self, name: &str) -> Option<FileId> {
302 self.files.get(name).map(|f| f.id)
303 }
304
305 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError> {
306 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
307 let file = self.files.get(name).ok_or(DatabaseError::FileNotFound)?;
308
309 Ok(file.clone())
310 }
311
312 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError> {
313 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
314 self.files.get(name).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
315 }
316
317 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError> {
318 self.files.get(name).cloned().ok_or(DatabaseError::FileNotFound)
319 }
320
321 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError> {
322 self.files.values().find(|file| file.path.as_deref() == Some(path)).cloned().ok_or(DatabaseError::FileNotFound)
323 }
324
325 fn files(&self) -> impl Iterator<Item = Arc<File>> {
326 self.files.values().cloned()
327 }
328
329 fn len(&self) -> usize {
330 self.files.len()
331 }
332}
333
334impl DatabaseReader for ReadDatabase {
335 fn get_id(&self, name: &str) -> Option<FileId> {
336 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).map(|f| f.id)
337 }
338
339 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError> {
340 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
341
342 self.files.get(*index).cloned().ok_or(DatabaseError::FileNotFound)
343 }
344
345 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError> {
346 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
347
348 self.files.get(*index).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
349 }
350
351 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError> {
352 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).cloned().ok_or(DatabaseError::FileNotFound)
353 }
354
355 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError> {
356 self.path_to_index.get(path).and_then(|&i| self.files.get(i)).cloned().ok_or(DatabaseError::FileNotFound)
357 }
358
359 fn files(&self) -> impl Iterator<Item = Arc<File>> {
360 self.files.iter().cloned()
361 }
362
363 fn len(&self) -> usize {
364 self.files.len()
365 }
366}