1use std::collections::HashMap;
2use std::path::Path;
3use std::path::PathBuf;
4
5use crate::change::Change;
6use crate::change::ChangeLog;
7use crate::error::DatabaseError;
8use crate::file::File;
9use crate::file::FileId;
10use crate::file::FileType;
11use crate::file::line_starts;
12
13mod utils;
14
15pub mod change;
16pub mod error;
17pub mod exclusion;
18pub mod file;
19pub mod loader;
20
21#[derive(Debug, Default)]
28pub struct Database {
29 files: HashMap<String, File>,
31 id_to_name: HashMap<FileId, String>,
33}
34
35#[derive(Debug, Clone)]
43pub struct ReadDatabase {
44 files: Vec<File>,
46 id_to_index: HashMap<FileId, usize>,
48 name_to_index: HashMap<String, usize>,
50 path_to_index: HashMap<PathBuf, usize>,
52}
53
54impl Database {
55 pub fn new() -> Self {
57 Self::default()
58 }
59
60 pub fn add(&mut self, file: File) {
62 let name = file.name.clone();
63 let id = file.id;
64
65 if let Some(old_file) = self.files.insert(name.clone(), file) {
66 self.id_to_name.remove(&old_file.id);
67 }
68 self.id_to_name.insert(id, name);
69 }
70
71 pub fn update(&mut self, id: FileId, new_contents: String) -> bool {
76 if let Some(name) = self.id_to_name.get(&id)
77 && let Some(file) = self.files.get_mut(name)
78 {
79 file.contents = new_contents;
80 file.size = file.contents.len();
81 file.lines = line_starts(&file.contents).collect();
82 return true;
83 }
84 false
85 }
86
87 pub fn delete(&mut self, id: FileId) -> bool {
91 if let Some(name) = self.id_to_name.remove(&id) { self.files.remove(&name).is_some() } else { false }
92 }
93
94 pub fn commit(&mut self, change_log: ChangeLog) -> Result<(), DatabaseError> {
103 for change in change_log.into_inner()? {
104 self.apply(change);
105 }
106 Ok(())
107 }
108
109 fn apply(&mut self, change: Change) {
111 match change {
112 Change::Add(file) => self.add(file),
113 Change::Update(id, contents) => {
114 self.update(id, contents);
115 }
116 Change::Delete(id) => {
117 self.delete(id);
118 }
119 }
120 }
121
122 pub fn read_only(&self) -> ReadDatabase {
129 let mut files_vec: Vec<File> = self.files.values().cloned().collect();
130 files_vec.sort_unstable_by_key(|f| f.id);
131
132 let mut id_to_index = HashMap::with_capacity(files_vec.len());
133 let mut name_to_index = HashMap::with_capacity(files_vec.len());
134 let mut path_to_index = HashMap::with_capacity(files_vec.len());
135
136 for (index, file) in files_vec.iter().enumerate() {
137 id_to_index.insert(file.id, index);
138 name_to_index.insert(file.name.clone(), index);
139 if let Some(path) = &file.path {
140 path_to_index.insert(path.clone(), index);
141 }
142 }
143
144 ReadDatabase { files: files_vec, id_to_index, name_to_index, path_to_index }
145 }
146}
147
148impl ReadDatabase {
149 pub fn single(file: File) -> Self {
159 let mut id_to_index = HashMap::with_capacity(1);
160 let mut name_to_index = HashMap::with_capacity(1);
161 let mut path_to_index = HashMap::with_capacity(1);
162
163 id_to_index.insert(file.id, 0);
165 name_to_index.insert(file.name.clone(), 0);
166 if let Some(path) = &file.path {
167 path_to_index.insert(path.clone(), 0);
168 }
169
170 Self { files: vec![file], id_to_index, name_to_index, path_to_index }
171 }
172}
173
174pub trait DatabaseReader {
180 fn get_id(&self, name: &str) -> Option<FileId>;
182
183 fn get_name(&self, id: &FileId) -> Option<&str> {
184 self.get_by_id(id).map(|file| file.name.as_str()).ok()
185 }
186
187 fn get_by_id(&self, id: &FileId) -> Result<&File, DatabaseError>;
193
194 fn get_by_name(&self, name: &str) -> Result<&File, DatabaseError>;
200
201 fn get_by_path(&self, path: &Path) -> Result<&File, DatabaseError>;
207
208 fn files(&self) -> impl Iterator<Item = &File>;
213
214 fn files_with_type(&self, file_type: FileType) -> impl Iterator<Item = &File> {
216 self.files().filter(move |file| file.file_type == file_type)
217 }
218
219 fn files_without_type(&self, file_type: FileType) -> impl Iterator<Item = &File> {
221 self.files().filter(move |file| file.file_type != file_type)
222 }
223
224 fn file_ids(&self) -> impl Iterator<Item = FileId> {
226 self.files().map(|file| file.id)
227 }
228
229 fn file_ids_with_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
231 self.files_with_type(file_type).map(|file| file.id)
232 }
233
234 fn file_ids_without_type(&self, file_type: FileType) -> impl Iterator<Item = FileId> {
236 self.files_without_type(file_type).map(|file| file.id)
237 }
238
239 fn len(&self) -> usize;
241
242 fn is_empty(&self) -> bool {
244 self.len() == 0
245 }
246}
247
248impl DatabaseReader for Database {
249 fn get_id(&self, name: &str) -> Option<FileId> {
250 self.files.get(name).map(|f| f.id)
251 }
252
253 fn get_by_id(&self, id: &FileId) -> Result<&File, DatabaseError> {
254 self.id_to_name.get(id).and_then(|name| self.files.get(name)).ok_or(DatabaseError::FileNotFound)
255 }
256
257 fn get_by_name(&self, name: &str) -> Result<&File, DatabaseError> {
258 self.files.get(name).ok_or(DatabaseError::FileNotFound)
259 }
260
261 fn get_by_path(&self, path: &Path) -> Result<&File, DatabaseError> {
262 self.files.values().find(|file| file.path.as_deref() == Some(path)).ok_or(DatabaseError::FileNotFound)
263 }
264
265 fn files(&self) -> impl Iterator<Item = &File> {
266 self.files.values()
267 }
268
269 fn len(&self) -> usize {
270 self.files.len()
271 }
272}
273
274impl DatabaseReader for ReadDatabase {
275 fn get_id(&self, name: &str) -> Option<FileId> {
276 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).map(|f| f.id)
277 }
278
279 fn get_by_id(&self, id: &FileId) -> Result<&File, DatabaseError> {
280 self.id_to_index.get(id).and_then(|&i| self.files.get(i)).ok_or(DatabaseError::FileNotFound)
281 }
282
283 fn get_by_name(&self, name: &str) -> Result<&File, DatabaseError> {
284 self.name_to_index.get(name).and_then(|&i| self.files.get(i)).ok_or(DatabaseError::FileNotFound)
285 }
286
287 fn get_by_path(&self, path: &Path) -> Result<&File, DatabaseError> {
288 self.path_to_index.get(path).and_then(|&i| self.files.get(i)).ok_or(DatabaseError::FileNotFound)
289 }
290
291 fn files(&self) -> impl Iterator<Item = &File> {
292 self.files.iter()
293 }
294
295 fn len(&self) -> usize {
296 self.files.len()
297 }
298}