1use crate::error::VfsErrorKind;
4use crate::{FileSystem, VfsFileType};
5use crate::{SeekAndRead, VfsMetadata};
6use crate::{SeekAndWrite, VfsResult};
7use core::cmp;
8use std::collections::hash_map::Entry;
9use std::collections::HashMap;
10use std::fmt;
11use std::fmt::{Debug, Formatter};
12use std::io::{Cursor, Read, Seek, SeekFrom, Write};
13use std::mem::swap;
14use std::sync::{Arc, RwLock};
15use std::time::SystemTime;
16
17type MemoryFsHandle = Arc<RwLock<MemoryFsImpl>>;
18
19pub struct MemoryFS {
21 handle: MemoryFsHandle,
22}
23
24impl Debug for MemoryFS {
25 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
26 f.write_str("In Memory File System")
27 }
28}
29
30impl MemoryFS {
31 pub fn new() -> Self {
33 MemoryFS {
34 handle: Arc::new(RwLock::new(MemoryFsImpl::new())),
35 }
36 }
37
38 fn ensure_has_parent(&self, path: &str) -> VfsResult<()> {
39 let separator = path.rfind('/');
40 if let Some(index) = separator {
41 if self.exists(&path[..index])? {
42 return Ok(());
43 }
44 }
45 Err(VfsErrorKind::Other("Parent path does not exist".into()).into())
46 }
47}
48
49impl Default for MemoryFS {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55struct WritableFile {
56 content: Cursor<Vec<u8>>,
57 destination: String,
58 fs: MemoryFsHandle,
59}
60
61impl Seek for WritableFile {
62 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
63 self.content.seek(pos)
64 }
65}
66
67impl Write for WritableFile {
68 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
69 self.content.write(buf)
70 }
71
72 fn flush(&mut self) -> std::io::Result<()> {
73 self.content.flush()?;
74 let mut content = self.content.get_ref().clone();
75 swap(&mut content, self.content.get_mut());
76 let mut handle = self.fs.write().unwrap();
77 let previous_file = handle.files.get(&self.destination);
78
79 let new_file = MemoryFile {
80 file_type: VfsFileType::File,
81 content: Arc::new(content),
82 created: previous_file
83 .map(|file| file.created)
84 .unwrap_or(SystemTime::now()),
85 modified: Some(SystemTime::now()),
86 accessed: previous_file.map(|file| file.accessed).unwrap_or(None),
87 };
88
89 handle.files.insert(self.destination.clone(), new_file);
90 Ok(())
91 }
92}
93
94impl Drop for WritableFile {
95 fn drop(&mut self) {
96 self.flush()
97 .expect("Flush failed while dropping in-memory file");
98 }
99}
100
101struct ReadableFile {
102 #[allow(clippy::rc_buffer)] content: Arc<Vec<u8>>,
104 position: u64,
105}
106
107impl ReadableFile {
108 fn len(&self) -> u64 {
109 self.content.len() as u64 - self.position
110 }
111}
112
113impl Read for ReadableFile {
114 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
115 let amt = cmp::min(buf.len(), self.len() as usize);
116
117 if amt == 1 {
118 buf[0] = self.content[self.position as usize];
119 } else {
120 buf[..amt].copy_from_slice(
121 &self.content.as_slice()[self.position as usize..self.position as usize + amt],
122 );
123 }
124 self.position += amt as u64;
125 Ok(amt)
126 }
127}
128
129impl Seek for ReadableFile {
130 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
131 match pos {
132 SeekFrom::Start(offset) => self.position = offset,
133 SeekFrom::Current(offset) => self.position = (self.position as i64 + offset) as u64,
134 SeekFrom::End(offset) => self.position = (self.content.len() as i64 + offset) as u64,
135 }
136 Ok(self.position)
137 }
138}
139
140impl FileSystem for MemoryFS {
141 fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> {
142 let prefix = format!("{}/", path);
143 let handle = self.handle.read().unwrap();
144 let mut found_directory = false;
145 #[allow(clippy::needless_collect)] let entries: Vec<_> = handle
147 .files
148 .iter()
149 .filter_map(|(candidate_path, _)| {
150 if candidate_path == path {
151 found_directory = true;
152 }
153 if candidate_path.starts_with(&prefix) {
154 let rest = &candidate_path[prefix.len()..];
155 if !rest.contains('/') {
156 return Some(rest.to_string());
157 }
158 }
159 None
160 })
161 .collect();
162 if !found_directory {
163 return Err(VfsErrorKind::FileNotFound.into());
164 }
165 Ok(Box::new(entries.into_iter()))
166 }
167
168 fn create_dir(&self, path: &str) -> VfsResult<()> {
169 self.ensure_has_parent(path)?;
170 let map = &mut self.handle.write().unwrap().files;
171 let entry = map.entry(path.to_string());
172 match entry {
173 Entry::Occupied(file) => {
174 return match file.get().file_type {
175 VfsFileType::File => Err(VfsErrorKind::FileExists.into()),
176 VfsFileType::Directory => Err(VfsErrorKind::DirectoryExists.into()),
177 }
178 }
179 Entry::Vacant(_) => {
180 map.insert(
181 path.to_string(),
182 MemoryFile {
183 file_type: VfsFileType::Directory,
184 content: Default::default(),
185 created: SystemTime::now(),
186 modified: Some(SystemTime::now()),
187 accessed: Some(SystemTime::now()),
188 },
189 );
190 }
191 }
192 Ok(())
193 }
194
195 fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> {
196 self.set_access_time(path, SystemTime::now())?;
197
198 let handle = self.handle.read().unwrap();
199 let file = handle.files.get(path).ok_or(VfsErrorKind::FileNotFound)?;
200 ensure_file(file)?;
201 Ok(Box::new(ReadableFile {
202 content: file.content.clone(),
203 position: 0,
204 }))
205 }
206
207 fn create_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
208 self.ensure_has_parent(path)?;
209 let content = Arc::new(Vec::<u8>::new());
210 self.handle.write().unwrap().files.insert(
211 path.to_string(),
212 MemoryFile {
213 file_type: VfsFileType::File,
214 content,
215 created: SystemTime::now(),
216 modified: Some(SystemTime::now()),
217 accessed: Some(SystemTime::now()),
218 },
219 );
220 let writer = WritableFile {
221 content: Cursor::new(vec![]),
222 destination: path.to_string(),
223 fs: self.handle.clone(),
224 };
225 Ok(Box::new(writer))
226 }
227
228 fn append_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
229 let handle = self.handle.write().unwrap();
230 let file = handle.files.get(path).ok_or(VfsErrorKind::FileNotFound)?;
231 let mut content = Cursor::new(file.content.as_ref().clone());
232 content.seek(SeekFrom::End(0))?;
233 let writer = WritableFile {
234 content,
235 destination: path.to_string(),
236 fs: self.handle.clone(),
237 };
238 Ok(Box::new(writer))
239 }
240
241 fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> {
242 let guard = self.handle.read().unwrap();
243 let files = &guard.files;
244 let file = files.get(path).ok_or(VfsErrorKind::FileNotFound)?;
245 Ok(VfsMetadata {
246 file_type: file.file_type,
247 len: file.content.len() as u64,
248 modified: file.modified,
249 created: Some(file.created),
250 accessed: file.accessed,
251 })
252 }
253
254 fn set_creation_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
255 let mut guard = self.handle.write().unwrap();
256 let files = &mut guard.files;
257 let file = files.get_mut(path).ok_or(VfsErrorKind::FileNotFound)?;
258
259 file.created = time;
260
261 Ok(())
262 }
263
264 fn set_modification_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
265 let mut guard = self.handle.write().unwrap();
266 let files = &mut guard.files;
267 let file = files.get_mut(path).ok_or(VfsErrorKind::FileNotFound)?;
268
269 file.modified = Some(time);
270
271 Ok(())
272 }
273
274 fn set_access_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
275 let mut guard = self.handle.write().unwrap();
276 let files = &mut guard.files;
277 let file = files.get_mut(path).ok_or(VfsErrorKind::FileNotFound)?;
278
279 file.accessed = Some(time);
280
281 Ok(())
282 }
283
284 fn exists(&self, path: &str) -> VfsResult<bool> {
285 Ok(self.handle.read().unwrap().files.contains_key(path))
286 }
287
288 fn remove_file(&self, path: &str) -> VfsResult<()> {
289 let mut handle = self.handle.write().unwrap();
290 handle
291 .files
292 .remove(path)
293 .ok_or(VfsErrorKind::FileNotFound)?;
294 Ok(())
295 }
296
297 fn remove_dir(&self, path: &str) -> VfsResult<()> {
298 if self.read_dir(path)?.next().is_some() {
299 return Err(VfsErrorKind::Other("Directory to remove is not empty".into()).into());
300 }
301 let mut handle = self.handle.write().unwrap();
302 handle
303 .files
304 .remove(path)
305 .ok_or(VfsErrorKind::FileNotFound)?;
306 Ok(())
307 }
308}
309
310struct MemoryFsImpl {
311 files: HashMap<String, MemoryFile>,
312}
313
314impl MemoryFsImpl {
315 pub fn new() -> Self {
316 let mut files = HashMap::new();
317 files.insert(
319 "".to_string(),
320 MemoryFile {
321 file_type: VfsFileType::Directory,
322 content: Arc::new(vec![]),
323 created: SystemTime::now(),
324 modified: None,
325 accessed: None,
326 },
327 );
328 Self { files }
329 }
330}
331
332struct MemoryFile {
333 file_type: VfsFileType,
334 #[allow(clippy::rc_buffer)] content: Arc<Vec<u8>>,
336
337 created: SystemTime,
338 modified: Option<SystemTime>,
339 accessed: Option<SystemTime>,
340}
341
342fn ensure_file(file: &MemoryFile) -> VfsResult<()> {
343 if file.file_type != VfsFileType::File {
344 return Err(VfsErrorKind::Other("Not a file".into()).into());
345 }
346 Ok(())
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use crate::VfsPath;
353 test_vfs!(MemoryFS::new());
354
355 #[test]
356 fn write_and_read_file() -> VfsResult<()> {
357 let root = VfsPath::new(MemoryFS::new());
358 let path = root.join("foobar.txt").unwrap();
359 let _send = &path as &dyn Send;
360 {
361 let mut file = path.create_file().unwrap();
362 write!(file, "Hello world").unwrap();
363 write!(file, "!").unwrap();
364 }
365 {
366 let mut file = path.open_file().unwrap();
367 let mut string: String = String::new();
368 file.read_to_string(&mut string).unwrap();
369 assert_eq!(string, "Hello world!");
370 }
371 assert!(path.exists()?);
372 assert!(!root.join("foo").unwrap().exists()?);
373 let metadata = path.metadata().unwrap();
374 assert_eq!(metadata.len, 12);
375 assert_eq!(metadata.file_type, VfsFileType::File);
376 Ok(())
377 }
378
379 #[test]
380 fn write_and_seek_and_read_file() -> VfsResult<()> {
381 let root = VfsPath::new(MemoryFS::new());
382 let path = root.join("foobar.txt").unwrap();
383 let _send = &path as &dyn Send;
384 {
385 let mut file = path.create_file().unwrap();
386 write!(file, "Hello world").unwrap();
387 write!(file, "!").unwrap();
388 write!(file, " Before seek!!").unwrap();
389 file.seek(SeekFrom::Current(-2)).unwrap();
390 write!(file, " After the Seek!").unwrap();
391 }
392 {
393 let mut file = path.open_file().unwrap();
394 let mut string: String = String::new();
395 file.read_to_string(&mut string).unwrap();
396 assert_eq!(string, "Hello world! Before seek After the Seek!");
397 }
398 assert!(path.exists()?);
399 assert!(!root.join("foo").unwrap().exists()?);
400 let metadata = path.metadata().unwrap();
401 assert_eq!(metadata.len, 40);
402 assert_eq!(metadata.file_type, VfsFileType::File);
403 Ok(())
404 }
405
406 #[test]
407 fn append_file() {
408 let root = VfsPath::new(MemoryFS::new());
409 let _string = String::new();
410 let path = root.join("test_append.txt").unwrap();
411 path.create_file().unwrap().write_all(b"Testing 1").unwrap();
412 path.append_file().unwrap().write_all(b"Testing 2").unwrap();
413 {
414 let mut file = path.open_file().unwrap();
415 let mut string: String = String::new();
416 file.read_to_string(&mut string).unwrap();
417 assert_eq!(string, "Testing 1Testing 2");
418 }
419 }
420
421 #[test]
422 fn append_file_with_seek() {
423 let root = VfsPath::new(MemoryFS::new());
424 let _string = String::new();
425 let path = root.join("test_append.txt").unwrap();
426 path.create_file().unwrap().write_all(b"Testing 1").unwrap();
427 path.append_file().unwrap().write_all(b"Testing 2").unwrap();
428 {
429 let mut file = path.append_file().unwrap();
430 file.seek(SeekFrom::End(-1)).unwrap();
431 file.write_all(b"Testing 3").unwrap();
432 }
433 {
434 let mut file = path.open_file().unwrap();
435 let mut string: String = String::new();
436 file.read_to_string(&mut string).unwrap();
437 assert_eq!(string, "Testing 1Testing Testing 3");
438 }
439 }
440
441 #[test]
442 fn create_dir() {
443 let root = VfsPath::new(MemoryFS::new());
444 let _string = String::new();
445 let path = root.join("foo").unwrap();
446 path.create_dir().unwrap();
447 let metadata = path.metadata().unwrap();
448 assert_eq!(metadata.file_type, VfsFileType::Directory);
449 }
450
451 #[test]
452 fn remove_dir_error_message() {
453 let root = VfsPath::new(MemoryFS::new());
454 let path = root.join("foo").unwrap();
455 let result = path.remove_dir();
456 assert_eq!(
457 format!("{}", result.unwrap_err()),
458 "Could not remove directory for '/foo': The file or directory could not be found"
459 );
460 }
461
462 #[test]
463 fn read_dir_error_message() {
464 let root = VfsPath::new(MemoryFS::new());
465 let path = root.join("foo").unwrap();
466 let result = path.read_dir();
467 match result {
468 Ok(_) => panic!("Error expected"),
469 Err(err) => {
470 assert_eq!(
471 format!("{}", err),
472 "Could not read directory for '/foo': The file or directory could not be found"
473 );
474 }
475 }
476 }
477
478 #[test]
479 fn copy_file_across_filesystems() -> VfsResult<()> {
480 let root_a = VfsPath::new(MemoryFS::new());
481 let root_b = VfsPath::new(MemoryFS::new());
482 let src = root_a.join("a.txt")?;
483 let dest = root_b.join("b.txt")?;
484 src.create_file()?.write_all(b"Hello World")?;
485 src.copy_file(&dest)?;
486 assert_eq!(&dest.read_to_string()?, "Hello World");
487 Ok(())
488 }
489
490 #[test]
492 fn flush_then_read_with_new_handle() {
493 let root = VfsPath::new(MemoryFS::new());
494 let path = root.join("test.txt").unwrap();
495 let mut write_handle = path.create_file().unwrap();
496 write_handle.write_all(b"Testing 1").unwrap();
497
498 write_handle.flush().unwrap();
500 let mut read_handle = path.open_file().unwrap();
501 let mut string: String = String::new();
502 read_handle.read_to_string(&mut string).unwrap();
503 assert_eq!(string, "Testing 1");
504
505 write_handle.write_all(b"Testing 2").unwrap();
507 write_handle.flush().unwrap();
508 let mut read_handle = path.open_file().unwrap();
509 let mut string: String = String::new();
510 read_handle.read_to_string(&mut string).unwrap();
511 assert_eq!(string, "Testing 1Testing 2");
512
513 write_handle.write_all(b"Testing 3").unwrap();
515 drop(write_handle);
516 let mut read_handle = path.open_file().unwrap();
517 let mut string: String = String::new();
518 read_handle.read_to_string(&mut string).unwrap();
519 assert_eq!(string, "Testing 1Testing 2Testing 3");
520 }
521}