1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::sync::{Arc, Mutex};
4use std::time::SystemTime;
5
6use crate::filesystem::{EntryMetadata, FileSystem, FileSystemError, Result};
7
8#[derive(Debug, Clone)]
9struct MockFile {
10 content: Vec<u8>,
11 modified: SystemTime,
12}
13
14#[derive(Debug, Clone)]
15pub struct MockFileSystem {
16 files: Arc<Mutex<HashMap<PathBuf, MockFile>>>,
17 directories: Arc<Mutex<Vec<PathBuf>>>,
18}
19
20impl MockFileSystem {
21 pub fn new() -> Self {
22 Self {
23 files: Arc::new(Mutex::new(HashMap::new())),
24 directories: Arc::new(Mutex::new(Vec::new())),
25 }
26 }
27
28 pub fn add_file(&self, path: impl Into<PathBuf>, content: Vec<u8>, modified: SystemTime) {
29 let path = path.into();
30 let mut files = self.files.lock().unwrap();
31 files.insert(path, MockFile { content, modified });
32 }
33
34 pub fn add_directory(&self, path: impl Into<PathBuf>) {
35 let path = path.into();
36 let mut directories = self.directories.lock().unwrap();
37 if !directories.contains(&path) {
38 directories.push(path);
39 }
40 }
41
42 pub fn get_file_content(&self, path: &Path) -> Option<Vec<u8>> {
43 let files = self.files.lock().unwrap();
44 files.get(path).map(|f| f.content.clone())
45 }
46
47 pub fn list_all_files(&self) -> Vec<PathBuf> {
48 let files = self.files.lock().unwrap();
49 files.keys().cloned().collect()
50 }
51}
52
53impl FileSystem for MockFileSystem {
54 fn list_directory(&self, path: &Path) -> Result<Vec<EntryMetadata>> {
55 let files = self.files.lock().unwrap();
56 let directories = self.directories.lock().unwrap();
57
58 let mut results = Vec::new();
59
60 if path != Path::new("") && !directories.contains(&path.to_path_buf()) {
62 return Err(FileSystemError::NotFound(path.to_path_buf()));
63 }
64
65 for (file_path, file) in files.iter() {
67 if let Some(parent) = file_path.parent() {
68 if parent == path {
69 results.push(EntryMetadata {
70 path: file_path.clone(),
71 modified: file.modified,
72 is_directory: false,
73 });
74 }
75 }
76 }
77
78 for dir_path in directories.iter() {
80 if let Some(parent) = dir_path.parent() {
81 if parent == path && dir_path != path {
82 results.push(EntryMetadata {
83 path: dir_path.clone(),
84 modified: SystemTime::now(),
85 is_directory: true,
86 });
87 }
88 }
89 }
90
91 Ok(results)
92 }
93
94 fn get_metadata(&self, path: &Path) -> Result<EntryMetadata> {
95 let files = self.files.lock().unwrap();
96 let directories = self.directories.lock().unwrap();
97
98 if let Some(file) = files.get(path) {
99 Ok(EntryMetadata {
100 path: path.to_path_buf(),
101 modified: file.modified,
102 is_directory: false,
103 })
104 } else if directories.contains(&path.to_path_buf()) {
105 Ok(EntryMetadata {
106 path: path.to_path_buf(),
107 modified: SystemTime::now(),
108 is_directory: true,
109 })
110 } else {
111 Err(FileSystemError::NotFound(path.to_path_buf()))
112 }
113 }
114
115 fn copy_file(&self, from: &Path, to: &Path) -> Result<()> {
116 let mut files = self.files.lock().unwrap();
117
118 let source_file = files.get(from)
119 .ok_or_else(|| FileSystemError::NotFound(from.to_path_buf()))?
120 .clone();
121
122 files.insert(to.to_path_buf(), source_file);
123 Ok(())
124 }
125
126 fn create_directory(&self, path: &Path) -> Result<()> {
127 self.add_directory(path);
128 Ok(())
129 }
130
131 fn exists(&self, path: &Path) -> Result<bool> {
132 let files = self.files.lock().unwrap();
133 let directories = self.directories.lock().unwrap();
134
135 Ok(files.contains_key(path) || directories.contains(&path.to_path_buf()))
136 }
137
138 fn set_modified_time(&self, path: &Path, time: SystemTime) -> Result<()> {
139 let mut files = self.files.lock().unwrap();
140
141 if let Some(file) = files.get_mut(path) {
142 file.modified = time;
143 Ok(())
144 } else {
145 Err(FileSystemError::NotFound(path.to_path_buf()))
146 }
147 }
148}