sp_wasm_memfs/
memfs.rs

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(); // skip first
26        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}