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 single(file: File) -> Self {
82 let mut db = Self::new();
83 db.add(file);
84 db
85 }
86
87 pub fn add(&mut self, file: File) -> FileId {
89 let name = file.name.clone();
90 let id = file.id;
91
92 if let Some(old_file) = self.files.insert(name.clone(), Arc::new(file)) {
93 self.id_to_name.remove(&old_file.id);
94 }
95
96 self.id_to_name.insert(id, name);
97
98 id
99 }
100
101 pub fn update(&mut self, id: FileId, new_contents: Cow<'static, str>) -> bool {
106 if let Some(name) = self.id_to_name.get(&id)
107 && let Some(file) = self.files.get_mut(name)
108 && let Some(file) = Arc::get_mut(file)
109 {
110 file.contents = new_contents;
111 file.size = file.contents.len() as u32;
112 file.lines = line_starts(file.contents.as_ref()).collect();
113 return true;
114 }
115 false
116 }
117
118 pub fn delete(&mut self, id: FileId) -> bool {
122 if let Some(name) = self.id_to_name.remove(&id) { self.files.remove(&name).is_some() } else { false }
123 }
124
125 pub fn commit(&mut self, change_log: ChangeLog, write_to_disk: bool) -> Result<(), DatabaseError> {
139 let changes = change_log.into_inner()?;
140 let mut fs_operations = if write_to_disk { Vec::new() } else { Vec::with_capacity(0) };
141
142 for change in changes {
143 match change {
144 Change::Add(file) => {
145 if write_to_disk && let Some(path) = &file.path {
146 fs_operations.push(FilesystemOperation::Write(path.clone(), file.contents.clone()));
147 }
148
149 self.add(file);
150 }
151 Change::Update(id, contents) => {
152 if write_to_disk
153 && let Ok(file) = self.get(&id)
154 && let Some(path) = &file.path
155 {
156 fs_operations.push(FilesystemOperation::Write(path.clone(), contents.clone()));
157 }
158
159 self.update(id, contents);
160 }
161 Change::Delete(id) => {
162 if write_to_disk
163 && let Ok(file) = self.get(&id)
164 && let Some(path) = &file.path
165 {
166 fs_operations.push(FilesystemOperation::Delete(path.clone()));
167 }
168
169 self.delete(id);
170 }
171 }
172 }
173
174 if write_to_disk {
176 fs_operations.into_par_iter().try_for_each(|op| -> Result<(), DatabaseError> { op.execute() })?;
177 }
178
179 Ok(())
180 }
181
182 pub fn read_only(&self) -> ReadDatabase {
189 let mut files_vec: Vec<Arc<File>> = self.files.values().cloned().collect();
190 files_vec.sort_unstable_by_key(|f| f.id);
191
192 let mut id_to_index = HashMap::with_capacity(files_vec.len());
193 let mut name_to_index = HashMap::with_capacity(files_vec.len());
194 let mut path_to_index = HashMap::with_capacity(files_vec.len());
195
196 for (index, file) in files_vec.iter().enumerate() {
197 id_to_index.insert(file.id, index);
198 name_to_index.insert(file.name.clone(), index);
199 if let Some(path) = &file.path {
200 path_to_index.insert(path.clone(), index);
201 }
202 }
203
204 ReadDatabase { files: files_vec, id_to_index, name_to_index, path_to_index }
205 }
206}
207
208impl ReadDatabase {
209 pub fn single(file: File) -> Self {
219 let mut id_to_index = HashMap::with_capacity(1);
220 let mut name_to_index = HashMap::with_capacity(1);
221 let mut path_to_index = HashMap::with_capacity(1);
222
223 id_to_index.insert(file.id, 0);
225 name_to_index.insert(file.name.clone(), 0);
226 if let Some(path) = &file.path {
227 path_to_index.insert(path.clone(), 0);
228 }
229
230 Self { files: vec![Arc::new(file)], id_to_index, name_to_index, path_to_index }
231 }
232}
233
234pub trait DatabaseReader {
240 fn get_id(&self, name: &str) -> Option<FileId>;
242
243 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError>;
249
250 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError>;
256
257 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError>;
263
264 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError>;
270
271 fn files(&self) -> impl Iterator<Item = Arc<File>>;
276
277 fn files_with_type(&self, file_type: FileType) -> impl Iterator<Item = Arc<File>> {
279 self.files().filter(move |file| file.file_type == file_type)
280 }
281
282 fn files_without_type(&self, file_type: FileType) -> impl Iterator<Item = Arc<File>> {
284 self.files().filter(move |file| file.file_type != file_type)
285 }
286
287 fn file_ids(&self) -> impl Iterator<Item = FileId> {
289 self.files().map(|file| file.id)
290 }
291
292 fn file_ids_with_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
294 self.files_with_type(file_type).map(|file| file.id)
295 }
296
297 fn file_ids_without_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
299 self.files_without_type(file_type).map(|file| file.id)
300 }
301
302 fn len(&self) -> usize;
304
305 fn is_empty(&self) -> bool {
307 self.len() == 0
308 }
309}
310
311impl DatabaseReader for Database {
312 fn get_id(&self, name: &str) -> Option<FileId> {
313 self.files.get(name).map(|f| f.id)
314 }
315
316 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError> {
317 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
318 let file = self.files.get(name).ok_or(DatabaseError::FileNotFound)?;
319
320 Ok(file.clone())
321 }
322
323 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError> {
324 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
325 self.files.get(name).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
326 }
327
328 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError> {
329 self.files.get(name).cloned().ok_or(DatabaseError::FileNotFound)
330 }
331
332 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError> {
333 self.files.values().find(|file| file.path.as_deref() == Some(path)).cloned().ok_or(DatabaseError::FileNotFound)
334 }
335
336 fn files(&self) -> impl Iterator<Item = Arc<File>> {
337 self.files.values().cloned()
338 }
339
340 fn len(&self) -> usize {
341 self.files.len()
342 }
343}
344
345impl DatabaseReader for ReadDatabase {
346 fn get_id(&self, name: &str) -> Option<FileId> {
347 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).map(|f| f.id)
348 }
349
350 fn get(&self, id: &FileId) -> Result<Arc<File>, DatabaseError> {
351 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
352
353 self.files.get(*index).cloned().ok_or(DatabaseError::FileNotFound)
354 }
355
356 fn get_ref(&self, id: &FileId) -> Result<&File, DatabaseError> {
357 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
358
359 self.files.get(*index).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
360 }
361
362 fn get_by_name(&self, name: &str) -> Result<Arc<File>, DatabaseError> {
363 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).cloned().ok_or(DatabaseError::FileNotFound)
364 }
365
366 fn get_by_path(&self, path: &Path) -> Result<Arc<File>, DatabaseError> {
367 self.path_to_index.get(path).and_then(|&i| self.files.get(i)).cloned().ok_or(DatabaseError::FileNotFound)
368 }
369
370 fn files(&self) -> impl Iterator<Item = Arc<File>> {
371 self.files.iter().cloned()
372 }
373
374 fn len(&self) -> usize {
375 self.files.len()
376 }
377}