1use super::*;
2use crate::HashMap;
3use crate::pristine::InodeMetadata;
4use parking_lot::Mutex;
5use std::sync::Arc;
6use std::time::SystemTime;
7
8#[derive(Debug, Clone)]
9pub struct Memory(Arc<Mutex<Memory_>>);
10
11#[derive(Debug)]
12struct Memory_ {
13 files: FileTree,
14 last_modified: SystemTime,
15}
16
17#[derive(Debug, Default)]
18struct FileTree {
19 children: HashMap<String, Inode>,
20}
21#[derive(Debug)]
22enum Inode {
23 File {
24 meta: InodeMetadata,
25 last_modified: SystemTime,
26 contents: Arc<Mutex<Vec<u8>>>,
27 },
28 Directory {
29 meta: InodeMetadata,
30 last_modified: SystemTime,
31 children: FileTree,
32 },
33}
34
35impl Default for Memory {
36 fn default() -> Self {
37 Memory(Arc::new(Mutex::new(Memory_ {
38 files: FileTree::default(),
39 last_modified: SystemTime::now(),
40 })))
41 }
42}
43
44impl Memory {
45 pub fn new() -> Self {
46 Self::default()
47 }
48 pub fn list_files(&self) -> Vec<String> {
49 let m = self.0.lock();
50 let mut result = Vec::new();
51 let mut current_files = vec![(String::new(), &m.files)];
52 let mut next_files = Vec::new();
53 loop {
54 if current_files.is_empty() {
55 break;
56 }
57 for (path, tree) in current_files.iter() {
58 for (name, inode) in tree.children.iter() {
59 let mut path = path.clone();
60 crate::path::push(&mut path, name);
61 match inode {
62 Inode::File { .. } => {
63 result.push(path);
64 }
65 Inode::Directory { children, .. } => {
66 result.push(path.clone());
67 next_files.push((path, children))
68 }
69 }
70 }
71 }
72 std::mem::swap(&mut current_files, &mut next_files);
73 next_files.clear();
74 }
75 result
76 }
77
78 pub fn add_file(&self, file: &str, file_contents: Vec<u8>) {
79 let file_meta = InodeMetadata::new(0, false);
80 let last = SystemTime::now();
81 self.add_inode(
82 file,
83 Inode::File {
84 meta: file_meta,
85 last_modified: last,
86 contents: Arc::new(Mutex::new(file_contents)),
87 },
88 )
89 }
90
91 pub fn add_dir(&self, file: &str) {
92 let file_meta = InodeMetadata::new(0o100, true);
93 let last = SystemTime::now();
94 self.add_inode(
95 file,
96 Inode::Directory {
97 meta: file_meta,
98 last_modified: last,
99 children: FileTree {
100 children: HashMap::default(),
101 },
102 },
103 )
104 }
105
106 fn add_inode(&self, file: &str, inode: Inode) {
107 let mut m = self.0.lock();
108 let last = SystemTime::now();
109 m.last_modified = last;
110 debug!("add_inode {:?} {:?}", last, inode);
111 let mut file_tree = &mut m.files;
112 let file = file.split('/').filter(|c| !c.is_empty());
113 let mut p = file.peekable();
114 while let Some(f) = p.next() {
115 if p.peek().is_some() {
116 let entry = file_tree
117 .children
118 .entry(f.to_string())
119 .or_insert(Inode::Directory {
120 meta: InodeMetadata::new(0o100, true),
121 children: FileTree {
122 children: HashMap::default(),
123 },
124 last_modified: last,
125 });
126 match *entry {
127 Inode::Directory {
128 ref mut children, ..
129 } => file_tree = children,
130 _ => panic!("Not a directory"),
131 }
132 } else {
133 file_tree.children.insert(f.to_string(), inode);
134 break;
135 }
136 }
137 }
138}
139
140impl Memory_ {
141 fn get_file(&self, file: &str) -> Option<&Inode> {
142 debug!("get_file {:?}", file);
143 debug!("repo = {:?}", self);
144 let mut t = Some(&self.files);
145 let mut inode = None;
146 let it = file.split('/').filter(|c| !c.is_empty());
147 for c in it {
148 debug!("c = {:?}", c);
149 inode = t.take().unwrap().children.get(c);
150 debug!("inode = {:?}", inode);
151 match inode {
152 Some(Inode::Directory { children, .. }) => t = Some(children),
153 _ => break,
154 }
155 }
156 inode
157 }
158
159 fn get_file_mut<'a>(&'a mut self, file: &str) -> Option<&'a mut Inode> {
160 debug!("get_file_mut {:?}", file);
161 debug!("repo = {:?}", self);
162 let mut t = Some(&mut self.files);
163 let mut it = file.split('/').filter(|c| !c.is_empty()).peekable();
164 self.last_modified = SystemTime::now();
165 while let Some(c) = it.next() {
166 debug!("c = {:?}", c);
167 let inode_ = t.take().unwrap().children.get_mut(c);
168 debug!("inode = {:?}", inode_);
169 if it.peek().is_none() {
170 return inode_;
171 }
172 match inode_ {
173 Some(Inode::Directory { children, .. }) => t = Some(children),
174 _ => return None,
175 }
176 }
177 None
178 }
179
180 fn remove_path_(&mut self, path: &str) -> Option<Inode> {
181 debug!("remove_path {:?}", path);
182 debug!("repo = {:?}", self);
183 self.last_modified = SystemTime::now();
184 let mut t = Some(&mut self.files);
185 let mut it = path.split('/').filter(|c| !c.is_empty());
186 let mut c = it.next().unwrap();
187 loop {
188 debug!("c = {:?}", c);
189 let next_c = it.next();
190 let t_ = t.take().unwrap();
191 let next_c = if let Some(next_c) = next_c {
192 next_c
193 } else {
194 return t_.children.remove(c);
195 };
196 let inode = t_.children.get_mut(c);
197 c = next_c;
198 debug!("inode = {:?}", inode);
199 match inode {
200 Some(Inode::Directory { children, .. }) => t = Some(children),
201 _ => return None,
202 }
203 }
204 }
205}
206
207#[derive(Debug, Error)]
208pub enum Error {
209 #[error("Path not found: {path}")]
210 NotFound { path: String },
211}
212
213impl WorkingCopyRead for Memory {
214 type Error = Error;
215 fn file_metadata(&self, file: &str) -> Result<InodeMetadata, Self::Error> {
216 let m = self.0.lock();
217 match m.get_file(file) {
218 Some(Inode::Directory { meta, .. }) => Ok(*meta),
219 Some(Inode::File { meta, .. }) => Ok(*meta),
220 None => Err(Error::NotFound {
221 path: file.to_string(),
222 }),
223 }
224 }
225 fn read_file(&self, file: &str, buffer: &mut Vec<u8>) -> Result<(), Self::Error> {
226 let m = self.0.lock();
227 match m.get_file(file) {
228 Some(Inode::Directory { .. }) => panic!("Not a file: {:?}", file),
229 Some(Inode::File { contents, .. }) => {
230 buffer.extend(&contents.lock()[..]);
231 Ok(())
232 }
233 None => Err(Error::NotFound {
234 path: file.to_string(),
235 }),
236 }
237 }
238 fn modified_time(&self, file: &str) -> Result<std::time::SystemTime, Self::Error> {
239 let m = self.0.lock();
240 match m.get_file(file) {
241 Some(Inode::Directory { last_modified, .. })
242 | Some(Inode::File { last_modified, .. }) => Ok(*last_modified),
243 _ => {
244 debug!("modified_time not found {:?}", file);
245 Ok(m.last_modified)
246 }
247 }
248 }
249}
250
251impl WorkingCopy for Memory {
252 fn create_dir_all(&self, file: &str) -> Result<(), Self::Error> {
253 let not_already_exists = {
254 let m = self.0.lock();
255 m.get_file(file).is_none()
256 };
257 if not_already_exists {
258 let last = SystemTime::now();
259 self.add_inode(
260 file,
261 Inode::Directory {
262 meta: InodeMetadata::new(0o100, true),
263 children: FileTree {
264 children: HashMap::default(),
265 },
266 last_modified: last,
267 },
268 );
269 }
270 Ok(())
271 }
272
273 fn remove_path(&self, path: &str, _rec: bool) -> Result<(), Self::Error> {
274 self.0.lock().remove_path_(path);
275 Ok(())
276 }
277
278 fn rename(&self, old: &str, new: &str) -> Result<(), Self::Error> {
279 debug!("rename {:?} to {:?}", old, new);
280 let inode = {
281 let mut m = self.0.lock();
282 m.remove_path_(old)
283 };
284 if let Some(inode) = inode {
285 self.add_inode(new, inode)
286 }
287 Ok(())
288 }
289 fn set_permissions(&self, file: &str, permissions: u16) -> Result<(), Self::Error> {
290 debug!("set_permissions {:?}", file);
291 let mut m = self.0.lock();
292 match m.get_file_mut(file) {
293 Some(Inode::File {
294 meta,
295 last_modified,
296 ..
297 }) => {
298 *last_modified = SystemTime::now();
299 *meta = InodeMetadata::new(permissions as usize & 0o100, false);
300 }
301 Some(Inode::Directory {
302 meta,
303 last_modified,
304 ..
305 }) => {
306 *last_modified = SystemTime::now();
307 *meta = InodeMetadata::new(permissions as usize & 0o100, true);
308 }
309 None => panic!("file not found: {:?}", file),
310 }
311 Ok(())
312 }
313
314 type Writer = Writer;
315 fn write_file(&self, file: &str, _: crate::Inode) -> Result<Self::Writer, Self::Error> {
316 let mut m = self.0.lock();
317 if let Some(f) = m.get_file_mut(file) {
318 if let Inode::File {
319 contents,
320 last_modified,
321 ..
322 } = f
323 {
324 *last_modified = SystemTime::now();
325 contents.lock().clear();
326 return Ok(Writer {
327 w: contents.clone(),
328 });
329 } else {
330 unreachable!()
331 }
332 }
333 std::mem::drop(m);
334 let contents = Arc::new(Mutex::new(Vec::new()));
335 let last_modified = SystemTime::now();
336 debug!("add_inode {:?}", last_modified);
337 self.add_inode(
338 file,
339 Inode::File {
340 meta: InodeMetadata::new(0, false),
341 contents: contents.clone(),
342 last_modified,
343 },
344 );
345 Ok(Writer { w: contents })
346 }
347
348 fn touch(&self, file: &str, time: std::time::SystemTime) -> Result<(), Self::Error> {
349 let mut m = self.0.lock();
350 if let Some(f) = m.get_file_mut(file) {
351 if let Inode::File { last_modified, .. } = f {
352 *last_modified = time
353 } else {
354 unreachable!()
355 }
356 }
357 Ok(())
358 }
359}
360
361pub struct Writer {
362 w: Arc<Mutex<Vec<u8>>>,
363}
364
365impl std::io::Write for Writer {
366 fn write(&mut self, b: &[u8]) -> Result<usize, std::io::Error> {
367 self.w.lock().write(b)
368 }
369 fn flush(&mut self) -> Result<(), std::io::Error> {
370 Ok(())
371 }
372}