1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::path::Path;
4use std::path::PathBuf;
5use std::sync::Arc;
6
7use crate::change::Change;
8use crate::change::ChangeLog;
9use crate::error::DatabaseError;
10use crate::file::File;
11use crate::file::FileId;
12use crate::file::FileType;
13use crate::file::line_starts;
14
15mod utils;
16
17pub mod change;
18pub mod error;
19pub mod exclusion;
20pub mod file;
21pub mod loader;
22
23#[derive(Debug, Default)]
30pub struct Database {
31 files: HashMap<Cow<'static, str>, Arc<File>>,
33 id_to_name: HashMap<FileId, Cow<'static, str>>,
35}
36
37#[derive(Debug)]
45pub struct ReadDatabase {
46 files: Vec<Arc<File>>,
48 id_to_index: HashMap<FileId, usize>,
50 name_to_index: HashMap<Cow<'static, str>, usize>,
52 path_to_index: HashMap<PathBuf, usize>,
54}
55
56impl Database {
57 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn add(&mut self, file: File) {
64 let name = file.name.clone();
65 let id = file.id;
66
67 if let Some(old_file) = self.files.insert(name.clone(), Arc::new(file)) {
68 self.id_to_name.remove(&old_file.id);
69 }
70 self.id_to_name.insert(id, name);
71 }
72
73 pub fn update(&mut self, id: FileId, new_contents: Cow<'static, str>) -> bool {
78 if let Some(name) = self.id_to_name.get(&id)
79 && let Some(file) = self.files.get_mut(name)
80 && let Some(file) = Arc::get_mut(file)
81 {
82 file.contents = new_contents;
83 file.size = file.contents.len() as u32;
84 file.lines = line_starts(file.contents.as_ref()).collect();
85 return true;
86 }
87 false
88 }
89
90 pub fn delete(&mut self, id: FileId) -> bool {
94 if let Some(name) = self.id_to_name.remove(&id) { self.files.remove(&name).is_some() } else { false }
95 }
96
97 pub fn commit(&mut self, change_log: ChangeLog) -> Result<(), DatabaseError> {
106 for change in change_log.into_inner()? {
107 self.apply(change);
108 }
109 Ok(())
110 }
111
112 fn apply(&mut self, change: Change) {
114 match change {
115 Change::Add(file) => self.add(file),
116 Change::Update(id, contents) => {
117 self.update(id, contents);
118 }
119 Change::Delete(id) => {
120 self.delete(id);
121 }
122 }
123 }
124
125 pub fn read_only(&self) -> ReadDatabase {
132 let mut files_vec: Vec<Arc<File>> = self.files.values().cloned().collect();
133 files_vec.sort_unstable_by_key(|f| f.id);
134
135 let mut id_to_index = HashMap::with_capacity(files_vec.len());
136 let mut name_to_index = HashMap::with_capacity(files_vec.len());
137 let mut path_to_index = HashMap::with_capacity(files_vec.len());
138
139 for (index, file) in files_vec.iter().enumerate() {
140 id_to_index.insert(file.id, index);
141 name_to_index.insert(file.name.clone(), index);
142 if let Some(path) = &file.path {
143 path_to_index.insert(path.clone(), index);
144 }
145 }
146
147 ReadDatabase { files: files_vec, id_to_index, name_to_index, path_to_index }
148 }
149}
150
151impl ReadDatabase {
152 pub fn single(file: File) -> Self {
162 let mut id_to_index = HashMap::with_capacity(1);
163 let mut name_to_index = HashMap::with_capacity(1);
164 let mut path_to_index = HashMap::with_capacity(1);
165
166 id_to_index.insert(file.id, 0);
168 name_to_index.insert(file.name.clone(), 0);
169 if let Some(path) = &file.path {
170 path_to_index.insert(path.clone(), 0);
171 }
172
173 Self { files: vec![Arc::new(file)], id_to_index, name_to_index, path_to_index }
174 }
175}
176
177pub trait DatabaseReader {
183 fn get_id(&self, name: &str) -> Option<FileId>;
185
186 fn get_name(&self, id: &FileId) -> Option<&str> {
187 self.get_by_id(id).map(|file| file.name.as_ref()).ok()
188 }
189
190 fn get_by_id(&self, id: &FileId) -> Result<&File, DatabaseError>;
196
197 fn get_by_name(&self, name: &str) -> Result<&File, DatabaseError>;
203
204 fn get_by_path(&self, path: &Path) -> Result<&File, DatabaseError>;
210
211 fn files(&self) -> impl Iterator<Item = &File>;
216
217 fn files_with_type(&self, file_type: FileType) -> impl Iterator<Item = &File> {
219 self.files().filter(move |file| file.file_type == file_type)
220 }
221
222 fn files_without_type(&self, file_type: FileType) -> impl Iterator<Item = &File> {
224 self.files().filter(move |file| file.file_type != file_type)
225 }
226
227 fn file_ids(&self) -> impl Iterator<Item = FileId> {
229 self.files().map(|file| file.id)
230 }
231
232 fn file_ids_with_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
234 self.files_with_type(file_type).map(|file| file.id)
235 }
236
237 fn file_ids_without_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
239 self.files_without_type(file_type).map(|file| file.id)
240 }
241
242 fn len(&self) -> usize;
244
245 fn is_empty(&self) -> bool {
247 self.len() == 0
248 }
249}
250
251impl DatabaseReader for Database {
252 fn get_id(&self, name: &str) -> Option<FileId> {
253 self.files.get(name).map(|f| f.id)
254 }
255
256 fn get_by_id(&self, id: &FileId) -> Result<&File, DatabaseError> {
257 let name = self.id_to_name.get(id).ok_or(DatabaseError::FileNotFound)?;
258 let file = self.files.get(name).ok_or(DatabaseError::FileNotFound)?;
259
260 Ok(file.as_ref())
261 }
262
263 fn get_by_name(&self, name: &str) -> Result<&File, DatabaseError> {
264 self.files.get(name).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
265 }
266
267 fn get_by_path(&self, path: &Path) -> Result<&File, DatabaseError> {
268 self.files
269 .values()
270 .find(|file| file.path.as_deref() == Some(path))
271 .map(|file| file.as_ref())
272 .ok_or(DatabaseError::FileNotFound)
273 }
274
275 fn files(&self) -> impl Iterator<Item = &File> {
276 self.files.values().map(|file| file.as_ref())
277 }
278
279 fn len(&self) -> usize {
280 self.files.len()
281 }
282}
283
284impl DatabaseReader for ReadDatabase {
285 fn get_id(&self, name: &str) -> Option<FileId> {
286 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).map(|f| f.id)
287 }
288
289 fn get_by_id(&self, id: &FileId) -> Result<&File, DatabaseError> {
290 let index = self.id_to_index.get(id).ok_or(DatabaseError::FileNotFound)?;
291
292 self.files.get(*index).map(|file| file.as_ref()).ok_or(DatabaseError::FileNotFound)
293 }
294
295 fn get_by_name(&self, name: &str) -> Result<&File, DatabaseError> {
296 self.name_to_index
297 .get(name)
298 .and_then(|&i| self.files.get(i))
299 .map(|file| file.as_ref())
300 .ok_or(DatabaseError::FileNotFound)
301 }
302
303 fn get_by_path(&self, path: &Path) -> Result<&File, DatabaseError> {
304 self.path_to_index
305 .get(path)
306 .and_then(|&i| self.files.get(i))
307 .map(|file| file.as_ref())
308 .ok_or(DatabaseError::FileNotFound)
309 }
310
311 fn files(&self) -> impl Iterator<Item = &File> {
312 self.files.iter().map(|file| file.as_ref())
313 }
314
315 fn len(&self) -> usize {
316 self.files.len()
317 }
318}