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;
9
10use crate::change::Change;
11use crate::change::ChangeLog;
12use crate::error::DatabaseError;
13use crate::file::File;
14use crate::file::FileId;
15use crate::file::FileType;
16use crate::file::line_starts;
17use crate::operation::FilesystemOperation;
18
19mod utils;
20
21pub mod change;
22pub mod error;
23pub mod exclusion;
24pub mod file;
25pub mod loader;
26
27mod operation;
28
29#[derive(Debug, Default)]
36pub struct Database {
37 files: HashMap<Cow<'static, str>, Arc<File>>,
39 id_to_name: HashMap<FileId, Cow<'static, str>>,
41}
42
43#[derive(Debug)]
51pub struct ReadDatabase {
52 files: Vec<Arc<File>>,
54 id_to_index: HashMap<FileId, usize>,
56 name_to_index: HashMap<Cow<'static, str>, usize>,
58 path_to_index: HashMap<PathBuf, usize>,
60}
61
62impl Database {
63 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn add(&mut self, file: File) {
70 let name = file.name.clone();
71 let id = file.id;
72
73 if let Some(old_file) = self.files.insert(name.clone(), Arc::new(file)) {
74 self.id_to_name.remove(&old_file.id);
75 }
76 self.id_to_name.insert(id, name);
77 }
78
79 pub fn update(&mut self, id: FileId, new_contents: Cow<'static, str>) -> bool {
84 if let Some(name) = self.id_to_name.get(&id)
85 && let Some(file) = self.files.get_mut(name)
86 && let Some(file) = Arc::get_mut(file)
87 {
88 file.contents = new_contents;
89 file.size = file.contents.len() as u32;
90 file.lines = line_starts(file.contents.as_ref()).collect();
91 return true;
92 }
93 false
94 }
95
96 pub fn delete(&mut self, id: FileId) -> bool {
100 if let Some(name) = self.id_to_name.remove(&id) { self.files.remove(&name).is_some() } else { false }
101 }
102
103 pub fn commit(&mut self, change_log: ChangeLog, write_to_disk: bool) -> Result<(), DatabaseError> {
117 let changes = change_log.into_inner()?;
118 let mut fs_operations = if write_to_disk { Vec::new() } else { Vec::with_capacity(0) };
119
120 for change in changes {
121 match change {
122 Change::Add(file) => {
123 if write_to_disk && let Some(path) = &file.path {
124 fs_operations.push(FilesystemOperation::Write(path.clone(), file.contents.clone()));
125 }
126
127 self.add(file);
128 }
129 Change::Update(id, contents) => {
130 if write_to_disk
131 && let Ok(file) = self.get(&id)
132 && let Some(path) = &file.path
133 {
134 fs_operations.push(FilesystemOperation::Write(path.clone(), contents.clone()));
135 }
136
137 self.update(id, contents);
138 }
139 Change::Delete(id) => {
140 if write_to_disk
141 && let Ok(file) = self.get(&id)
142 && let Some(path) = &file.path
143 {
144 fs_operations.push(FilesystemOperation::Delete(path.clone()));
145 }
146
147 self.delete(id);
148 }
149 }
150 }
151
152 if write_to_disk {
154 fs_operations.into_par_iter().try_for_each(|op| -> Result<(), DatabaseError> { op.execute() })?;
155 }
156
157 Ok(())
158 }
159
160 pub fn read_only(&self) -> ReadDatabase {
167 let mut files_vec: Vec<Arc<File>> = self.files.values().cloned().collect();
168 files_vec.sort_unstable_by_key(|f| f.id);
169
170 let mut id_to_index = HashMap::with_capacity(files_vec.len());
171 let mut name_to_index = HashMap::with_capacity(files_vec.len());
172 let mut path_to_index = HashMap::with_capacity(files_vec.len());
173
174 for (index, file) in files_vec.iter().enumerate() {
175 id_to_index.insert(file.id, index);
176 name_to_index.insert(file.name.clone(), index);
177 if let Some(path) = &file.path {
178 path_to_index.insert(path.clone(), index);
179 }
180 }
181
182 ReadDatabase { files: files_vec, id_to_index, name_to_index, path_to_index }
183 }
184}
185
186impl ReadDatabase {
187 pub fn single(file: File) -> Self {
197 let mut id_to_index = HashMap::with_capacity(1);
198 let mut name_to_index = HashMap::with_capacity(1);
199 let mut path_to_index = HashMap::with_capacity(1);
200
201 id_to_index.insert(file.id, 0);
203 name_to_index.insert(file.name.clone(), 0);
204 if let Some(path) = &file.path {
205 path_to_index.insert(path.clone(), 0);
206 }
207
208 Self { files: vec![Arc::new(file)], id_to_index, name_to_index, path_to_index }
209 }
210}
211
212pub trait DatabaseReader {
218 fn get_id(&self, name: &str) -> Option<FileId>;
220
221 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError>;
227
228 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError>;
234
235 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError>;
241
242 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError>;
248
249 fn files(&self) -> impl Iterator<Item = Arc<File>>;
254
255 fn files_with_type(&self, file_type: FileType) -> impl Iterator<Item = Arc<File>> {
257 self.files().filter(move |file| file.file_type == file_type)
258 }
259
260 fn files_without_type(&self, file_type: FileType) -> impl Iterator<Item = Arc<File>> {
262 self.files().filter(move |file| file.file_type != file_type)
263 }
264
265 fn file_ids(&self) -> impl Iterator<Item = FileId> {
267 self.files().map(|file| file.id)
268 }
269
270 fn file_ids_with_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
272 self.files_with_type(file_type).map(|file| file.id)
273 }
274
275 fn file_ids_without_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
277 self.files_without_type(file_type).map(|file| file.id)
278 }
279
280 fn len(&self) -> usize;
282
283 fn is_empty(&self) -> bool {
285 self.len() == 0
286 }
287}
288
289impl DatabaseReader for Database {
290 fn get_id(&self, name: &str) -> Option<FileId> {
291 self.files.get(name).map(|f| f.id)
292 }
293
294 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError> {
295 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
296 let file = self.files.get(name).ok_or(DatabaseError::FileNotFound)?;
297
298 Ok(file.clone())
299 }
300
301 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError> {
302 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
303 self.files.get(name).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
304 }
305
306 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError> {
307 self.files.get(name).cloned().ok_or(DatabaseError::FileNotFound)
308 }
309
310 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError> {
311 self.files.values().find(|file| file.path.as_deref() == Some(path)).cloned().ok_or(DatabaseError::FileNotFound)
312 }
313
314 fn files(&self) -> impl Iterator<Item = Arc<File>> {
315 self.files.values().cloned()
316 }
317
318 fn len(&self) -> usize {
319 self.files.len()
320 }
321}
322
323impl DatabaseReader for ReadDatabase {
324 fn get_id(&self, name: &str) -> Option<FileId> {
325 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).map(|f| f.id)
326 }
327
328 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError> {
329 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
330
331 self.files.get(*index).cloned().ok_or(DatabaseError::FileNotFound)
332 }
333
334 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError> {
335 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
336
337 self.files.get(*index).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
338 }
339
340 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError> {
341 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).cloned().ok_or(DatabaseError::FileNotFound)
342 }
343
344 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError> {
345 self.path_to_index.get(path).and_then(|&i| self.files.get(i)).cloned().ok_or(DatabaseError::FileNotFound)
346 }
347
348 fn files(&self) -> impl Iterator<Item = Arc<File>> {
349 self.files.iter().cloned()
350 }
351
352 fn len(&self) -> usize {
353 self.files.len()
354 }
355}