1use super::error::*;
2use super::file::*;
3use super::node::*;
4use super::Result;
5use path_clean::PathClean;
6use std::path::{Path, PathBuf};
7use std::sync::{Arc, Mutex};
8use tool::prelude::*;
9
10#[derive(Debug)]
11pub struct MemFS {
12 root: Arc<Mutex<Node>>,
13}
14
15impl MemFS {
16 pub fn new() -> Self {
17 Self::default()
18 }
19
20 fn walk<P>(&self, path: P, node: Arc<Mutex<Node>>) -> Result<Arc<Mutex<Node>>>
21 where
22 P: AsRef<Path>,
23 {
24 let mut components = path.as_ref().components();
25 components.next(); let path = components.as_path();
27
28 let name: String = match components.next() {
29 Some(component) => component
30 .as_os_str()
31 .to_str()
32 .ok_or(Error::InvalidPath(
33 component.as_os_str().to_string_lossy().to_string(),
34 ))?
35 .to_owned(),
36 None => return Ok(node),
37 };
38
39 if node.lock().unwrap().is_file() {
40 if node.lock().unwrap().name == name {
41 return Ok(Arc::clone(&node));
42 }
43 } else {
44 if let Some(next) = node.lock().unwrap().children.get(&name) {
45 return self.walk(path, Arc::clone(&next));
46 }
47 }
48
49 Err(Error::NotFound(name))
50 }
51
52 fn normalize_path<P>(path: P) -> Result<PathBuf>
53 where
54 P: AsRef<Path>,
55 {
56 if !path.as_ref().has_root() {
57 return Err(Error::InvalidPath(
58 path.as_ref().to_string_lossy().to_string(),
59 ));
60 }
61
62 Ok(PathBuf::from(path.as_ref()).clean())
63 }
64
65 fn resolve_parent<P>(path: P) -> Result<(PathBuf, String)>
66 where
67 P: AsRef<Path>,
68 {
69 let path = Self::normalize_path(path)?;
70 let parent = path.parent().ok_or(Error::IsRoot)?;
71 let filename = path
72 .file_name()
73 .and_then(|s| s.to_str())
74 .ok_or(Error::InvalidPath(path.to_string_lossy().to_string()))?;
75
76 Ok((parent.to_owned(), filename.to_owned()))
77 }
78
79 pub fn create_dir<P>(&self, path: P) -> Result<()>
80 where
81 P: AsRef<Path>,
82 {
83 let (parent, filename) = Self::resolve_parent(path)?;
84 let node = self.walk(parent, Arc::clone(&self.root))?;
85 node.lock()
86 .unwrap()
87 .children
88 .insert(filename.clone(), new_dir_node(filename));
89
90 Ok(())
91 }
92
93 pub fn create_dir_all<P>(&self, path: P) -> Result<()>
94 where
95 P: AsRef<Path>,
96 {
97 let walk_create = fix(|f, path: PathBuf| -> Result<Arc<Mutex<Node>>> {
98 let (parent, filename) = Self::resolve_parent(path)?;
99
100 let node = if self.is_dir(parent.as_path())? {
101 self.walk(parent, Arc::clone(&self.root))?
102 } else {
103 f(parent)?
104 };
105
106 let new_child = new_dir_node(filename.clone());
107 node.lock()
108 .unwrap()
109 .children
110 .insert(filename.clone(), Arc::clone(&new_child));
111
112 Ok(new_child)
113 });
114
115 if let Err(err) = walk_create(path.as_ref().to_owned()) {
116 match err {
117 Error::IsRoot => Ok(()),
118 _ => Err(err),
119 }
120 } else {
121 Ok(())
122 }
123 }
124
125 pub fn create_file<P>(&self, path: P) -> Result<File>
126 where
127 P: AsRef<Path>,
128 {
129 let (parent, filename) = Self::resolve_parent(path)?;
130 let node = self.walk(parent, Arc::clone(&self.root))?;
131 let file_node = new_file_node(filename.clone());
132 node.lock()
133 .unwrap()
134 .children
135 .insert(filename, Arc::clone(&file_node));
136
137 Ok(File::new(file_node))
138 }
139
140 pub fn open_file<P>(&self, path: P) -> Result<File>
141 where
142 P: AsRef<Path>,
143 {
144 let (parent, filename) = Self::resolve_parent(path)?;
145 self.walk(parent.join(filename), Arc::clone(&self.root))
146 .map(|node| File::new(node))
147 }
148
149 pub fn is_dir<P>(&self, path: P) -> Result<bool>
150 where
151 P: AsRef<Path>,
152 {
153 let path = Self::normalize_path(path)?;
154 match self.walk(path, Arc::clone(&self.root)) {
155 Ok(node) => Ok(node.lock().unwrap().is_dir()),
156 _ => Ok(false),
157 }
158 }
159
160 pub fn is_file<P>(&self, path: P) -> Result<bool>
161 where
162 P: AsRef<Path>,
163 {
164 let path = Self::normalize_path(path)?;
165 match self.walk(path, Arc::clone(&self.root)) {
166 Ok(node) => Ok(node.lock().unwrap().is_file()),
167 _ => Ok(false),
168 }
169 }
170}
171
172impl Default for MemFS {
173 fn default() -> Self {
174 Self {
175 root: Arc::new(Mutex::new(Node::new("/", FileType::Dir))),
176 }
177 }
178}
179
180#[cfg(test)]
181mod test {
182 use super::*;
183 use std::path::PathBuf;
184
185 #[test]
186 fn normalize_path() -> Result<()> {
187 assert!(MemFS::normalize_path("tmp/").is_err());
188 assert!(MemFS::normalize_path("a.txt").is_err());
189 assert!(MemFS::normalize_path(".").is_err());
190 assert!(MemFS::normalize_path("").is_err());
191 assert!(MemFS::normalize_path(" ").is_err());
192
193 assert_eq!(MemFS::normalize_path("/")?, PathBuf::from("/"));
194 assert_eq!(MemFS::normalize_path("//")?, PathBuf::from("/"));
195 assert_eq!(MemFS::normalize_path("/../")?, PathBuf::from("/"));
196 assert_eq!(MemFS::normalize_path("/./")?, PathBuf::from("/"));
197 assert_eq!(MemFS::normalize_path("/.././")?, PathBuf::from("/"));
198 assert_eq!(MemFS::normalize_path("/tmp/../")?, PathBuf::from("/"));
199 assert_eq!(MemFS::normalize_path("/tmp/a/../")?, PathBuf::from("/tmp"));
200
201 Ok(())
202 }
203
204 #[test]
205 fn resolve_parent() -> Result<()> {
206 assert_eq!(MemFS::resolve_parent("/").unwrap_err(), Error::IsRoot);
207 assert_eq!(
208 MemFS::resolve_parent("tmp").unwrap_err(),
209 Error::InvalidPath("tmp".to_owned())
210 );
211
212 assert_eq!(
213 MemFS::resolve_parent("/tmp")?,
214 (PathBuf::from("/"), "tmp".to_owned())
215 );
216 assert_eq!(
217 MemFS::resolve_parent("/tmp/a/b/c")?,
218 (PathBuf::from("/tmp/a/b"), "c".to_owned())
219 );
220
221 Ok(())
222 }
223
224 #[test]
225 fn create_dir() -> Result<()> {
226 let fs = MemFS::new();
227 fs.create_dir("/tmp")?;
228 fs.create_dir("/tmp/a")?;
229 fs.create_dir("/tmp/a/b")?;
230 fs.create_dir("/dev")?;
231
232 assert!(fs.is_dir("/tmp")?);
233 assert!(fs.is_dir("/tmp/a")?);
234 assert!(fs.is_dir("/tmp/a/b")?);
235 assert!(fs.is_dir("/dev")?);
236
237 assert_eq!(
238 fs.create_dir("/tmp/c/d").unwrap_err(),
239 Error::NotFound("c".to_owned())
240 );
241 assert_eq!(fs.create_dir("/").unwrap_err(), Error::IsRoot);
242 assert_eq!(
243 fs.create_dir("tmp/a/c").unwrap_err(),
244 Error::InvalidPath("tmp/a/c".to_owned())
245 );
246
247 Ok(())
248 }
249
250 #[test]
251 fn create_dir_all() -> Result<()> {
252 let fs = MemFS::new();
253 fs.create_dir_all("/tmp/a/b/c")?;
254
255 assert!(fs.is_dir("/tmp")?);
256 assert!(fs.is_dir("/tmp/a")?);
257 assert!(fs.is_dir("/tmp/a/b")?);
258 assert!(fs.is_dir("/tmp/a/b/c")?);
259
260 assert!(fs.create_dir_all("/").is_ok());
261
262 Ok(())
263 }
264
265 #[test]
266 fn create_file() -> Result<()> {
267 let fs = MemFS::new();
268 fs.create_dir("/tmp")?;
269 fs.create_file("/tmp/a")?;
270 fs.create_file("/b")?;
271 fs.create_dir("/dev")?;
272 fs.create_file("/dev/random")?;
273
274 assert!(fs.is_file("/tmp/a")?);
275 assert!(fs.is_file("/b")?);
276 assert!(fs.is_file("/dev/random")?);
277
278 assert_eq!(fs.create_file("/").unwrap_err(), Error::IsRoot);
279 assert_eq!(
280 fs.create_file("c").unwrap_err(),
281 Error::InvalidPath("c".to_owned())
282 );
283 assert_eq!(
284 fs.create_file("/d/c").unwrap_err(),
285 Error::NotFound("d".to_owned())
286 );
287
288 Ok(())
289 }
290
291 #[test]
292 fn open_file() -> Result<()> {
293 let fs = MemFS::new();
294 fs.create_file("/test")?;
295
296 assert!(fs.is_file("/test")?);
297 assert!(fs.open_file("/test").is_ok());
298
299 assert_eq!(fs.open_file("/").unwrap_err(), Error::IsRoot);
300 assert_eq!(
301 fs.open_file("/bbb").unwrap_err(),
302 Error::NotFound("bbb".to_owned())
303 );
304 assert_eq!(
305 fs.open_file("bbb").unwrap_err(),
306 Error::InvalidPath("bbb".to_owned())
307 );
308
309 Ok(())
310 }
311}