1use std::env;
2use std::ffi::{OsStr, OsString};
3use std::io::Result;
4use std::iter::Iterator;
5use std::path::{Path, PathBuf};
6use std::sync::{Arc, Mutex, MutexGuard};
7use std::vec::IntoIter;
8
9use FileSystem;
10#[cfg(unix)]
11use UnixFileSystem;
12#[cfg(feature = "temp")]
13use {TempDir, TempFileSystem};
14
15#[cfg(feature = "temp")]
16pub use self::tempdir::FakeTempDir;
17
18use self::registry::Registry;
19
20mod node;
21mod registry;
22#[cfg(feature = "temp")]
23mod tempdir;
24
25#[derive(Debug, Clone)]
26pub struct DirEntry {
27 parent: PathBuf,
28 file_name: OsString,
29}
30
31impl DirEntry {
32 fn new<P, S>(parent: P, file_name: S) -> Self
33 where
34 P: AsRef<Path>,
35 S: AsRef<OsStr>,
36 {
37 DirEntry {
38 parent: parent.as_ref().to_path_buf(),
39 file_name: file_name.as_ref().to_os_string(),
40 }
41 }
42}
43
44impl ::DirEntry for DirEntry {
45 fn file_name(&self) -> OsString {
46 self.file_name.clone()
47 }
48
49 fn path(&self) -> PathBuf {
50 self.parent.join(&self.file_name)
51 }
52}
53
54#[derive(Debug)]
55pub struct ReadDir(IntoIter<Result<DirEntry>>);
56
57impl ReadDir {
58 fn new(entries: Vec<Result<DirEntry>>) -> Self {
59 ReadDir(entries.into_iter())
60 }
61}
62
63impl Iterator for ReadDir {
64 type Item = Result<DirEntry>;
65
66 fn next(&mut self) -> Option<Self::Item> {
67 self.0.next()
68 }
69}
70
71impl ::ReadDir<DirEntry> for ReadDir {}
72
73#[derive(Clone, Debug, Default)]
75pub struct FakeFileSystem {
76 registry: Arc<Mutex<Registry>>,
77}
78
79impl FakeFileSystem {
80 pub fn new() -> Self {
81 let registry = Registry::new();
82
83 FakeFileSystem {
84 registry: Arc::new(Mutex::new(registry)),
85 }
86 }
87
88 fn apply<F, T>(&self, path: &Path, f: F) -> T
89 where
90 F: FnOnce(&MutexGuard<Registry>, &Path) -> T,
91 {
92 let registry = self.registry.lock().unwrap();
93 let storage;
94 let path = if path.is_relative() {
95 storage = registry
96 .current_dir()
97 .unwrap_or_else(|_| PathBuf::from("/"))
98 .join(path);
99 &storage
100 } else {
101 path
102 };
103
104 f(®istry, path)
105 }
106
107 fn apply_mut<F, T>(&self, path: &Path, mut f: F) -> T
108 where
109 F: FnMut(&mut MutexGuard<Registry>, &Path) -> T,
110 {
111 let mut registry = self.registry.lock().unwrap();
112 let storage;
113 let path = if path.is_relative() {
114 storage = registry
115 .current_dir()
116 .unwrap_or_else(|_| PathBuf::from("/"))
117 .join(path);
118 &storage
119 } else {
120 path
121 };
122
123 f(&mut registry, path)
124 }
125
126 fn apply_mut_from_to<F, T>(&self, from: &Path, to: &Path, mut f: F) -> T
127 where
128 F: FnMut(&mut MutexGuard<Registry>, &Path, &Path) -> T,
129 {
130 let mut registry = self.registry.lock().unwrap();
131 let from_storage;
132 let from = if from.is_relative() {
133 from_storage = registry
134 .current_dir()
135 .unwrap_or_else(|_| PathBuf::from("/"))
136 .join(from);
137 &from_storage
138 } else {
139 from
140 };
141 let to_storage;
142 let to = if to.is_relative() {
143 to_storage = registry
144 .current_dir()
145 .unwrap_or_else(|_| PathBuf::from("/"))
146 .join(to);
147 &to_storage
148 } else {
149 to
150 };
151
152 f(&mut registry, from, to)
153 }
154}
155
156impl FileSystem for FakeFileSystem {
157 type DirEntry = DirEntry;
158 type ReadDir = ReadDir;
159
160 fn current_dir(&self) -> Result<PathBuf> {
161 let registry = self.registry.lock().unwrap();
162 registry.current_dir()
163 }
164
165 fn set_current_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
166 self.apply_mut(path.as_ref(), |r, p| r.set_current_dir(p.to_path_buf()))
167 }
168
169 fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
170 self.apply(path.as_ref(), |r, p| r.is_dir(p))
171 }
172
173 fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
174 self.apply(path.as_ref(), |r, p| r.is_file(p))
175 }
176
177 fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
178 self.apply_mut(path.as_ref(), |r, p| r.create_dir(p))
179 }
180
181 fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
182 self.apply_mut(path.as_ref(), |r, p| r.create_dir_all(p))
183 }
184
185 fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
186 self.apply_mut(path.as_ref(), |r, p| r.remove_dir(p))
187 }
188
189 fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
190 self.apply_mut(path.as_ref(), |r, p| r.remove_dir_all(p))
191 }
192
193 fn read_dir<P: AsRef<Path>>(&self, path: P) -> Result<Self::ReadDir> {
194 let path = path.as_ref();
195
196 self.apply(path, |r, p| r.read_dir(p)).map(|entries| {
197 let entries = entries
198 .iter()
199 .map(|e| {
200 let file_name = e.file_name().unwrap_or_else(|| e.as_os_str());
201
202 Ok(DirEntry::new(path, &file_name))
203 })
204 .collect();
205
206 ReadDir::new(entries)
207 })
208 }
209
210 fn create_file<P, B>(&self, path: P, buf: B) -> Result<()>
211 where
212 P: AsRef<Path>,
213 B: AsRef<[u8]>,
214 {
215 self.apply_mut(path.as_ref(), |r, p| r.create_file(p, buf.as_ref()))
216 }
217
218 fn write_file<P, B>(&self, path: P, buf: B) -> Result<()>
219 where
220 P: AsRef<Path>,
221 B: AsRef<[u8]>,
222 {
223 self.apply_mut(path.as_ref(), |r, p| r.write_file(p, buf.as_ref()))
224 }
225
226 fn overwrite_file<P, B>(&self, path: P, buf: B) -> Result<()>
227 where
228 P: AsRef<Path>,
229 B: AsRef<[u8]>,
230 {
231 self.apply_mut(path.as_ref(), |r, p| r.overwrite_file(p, buf.as_ref()))
232 }
233
234 fn read_file<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>> {
235 self.apply(path.as_ref(), |r, p| r.read_file(p))
236 }
237
238 fn read_file_to_string<P: AsRef<Path>>(&self, path: P) -> Result<String> {
239 self.apply(path.as_ref(), |r, p| r.read_file_to_string(p))
240 }
241
242 fn read_file_into<P, B>(&self, path: P, mut buf: B) -> Result<usize>
243 where
244 P: AsRef<Path>,
245 B: AsMut<Vec<u8>>,
246 {
247 self.apply(path.as_ref(), |r, p| r.read_file_into(p, buf.as_mut()))
248 }
249
250 fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
251 self.apply_mut(path.as_ref(), |r, p| r.remove_file(p))
252 }
253
254 fn copy_file<P, Q>(&self, from: P, to: Q) -> Result<()>
255 where
256 P: AsRef<Path>,
257 Q: AsRef<Path>,
258 {
259 self.apply_mut_from_to(from.as_ref(), to.as_ref(), |r, from, to| {
260 r.copy_file(from, to)
261 })
262 }
263
264 fn rename<P, Q>(&self, from: P, to: Q) -> Result<()>
265 where
266 P: AsRef<Path>,
267 Q: AsRef<Path>,
268 {
269 self.apply_mut_from_to(from.as_ref(), to.as_ref(), |r, from, to| r.rename(from, to))
270 }
271
272 fn readonly<P: AsRef<Path>>(&self, path: P) -> Result<bool> {
273 self.apply(path.as_ref(), |r, p| r.readonly(p))
274 }
275
276 fn set_readonly<P: AsRef<Path>>(&self, path: P, readonly: bool) -> Result<()> {
277 self.apply_mut(path.as_ref(), |r, p| r.set_readonly(p, readonly))
278 }
279
280 fn len<P: AsRef<Path>>(&self, path: P) -> u64 {
281 self.apply(path.as_ref(), |r, p| r.len(p))
282 }
283}
284
285#[cfg(unix)]
286impl UnixFileSystem for FakeFileSystem {
287 fn mode<P: AsRef<Path>>(&self, path: P) -> Result<u32> {
288 self.apply(path.as_ref(), |r, p| r.mode(p))
289 }
290
291 fn set_mode<P: AsRef<Path>>(&self, path: P, mode: u32) -> Result<()> {
292 self.apply_mut(path.as_ref(), |r, p| r.set_mode(p, mode))
293 }
294}
295
296#[cfg(feature = "temp")]
297impl TempFileSystem for FakeFileSystem {
298 type TempDir = FakeTempDir;
299
300 fn temp_dir<S: AsRef<str>>(&self, prefix: S) -> Result<Self::TempDir> {
301 let base = env::temp_dir();
302 let dir = FakeTempDir::new(Arc::downgrade(&self.registry), &base, prefix.as_ref());
303
304 self.create_dir_all(&dir.path()).and(Ok(dir))
305 }
306}