reproto_core/
fs.rs

1//! Filesystem abstractions.
2
3use errors::Result;
4use linked_hash_map::LinkedHashMap;
5use std::cell::RefCell;
6use std::fs;
7use std::io;
8use std::path::{Path, PathBuf};
9use std::rc::Rc;
10use {RelativePath, RelativePathBuf};
11
12pub trait Handle {
13    /// Check if the given path is a directory or not.
14    fn is_dir(&self, path: &RelativePath) -> bool;
15
16    /// Check if the given path is a file or not.
17    fn is_file(&self, path: &RelativePath) -> bool;
18
19    /// Recursively create the given path.
20    fn create_dir_all(&self, path: &RelativePath) -> Result<()>;
21
22    /// Create the given file (for writing).
23    fn create(&self, path: &RelativePath) -> Result<Box<io::Write>>;
24}
25
26/// Filesystem abstraction.
27pub trait Filesystem {
28    /// Open the filesystem from the given root path.
29    fn open_root(&self, root: Option<&Path>) -> Result<Box<Handle>>;
30}
31
32/// Real filesystem implementation.
33pub struct RealFilesystem {}
34
35impl RealFilesystem {
36    pub fn new() -> RealFilesystem {
37        Self {}
38    }
39}
40
41impl Filesystem for RealFilesystem {
42    fn open_root(&self, root: Option<&Path>) -> Result<Box<Handle>> {
43        let root = root.ok_or_else(|| {
44            "Missing root directory, specify using `--out`, or `output` key in manifest"
45        })?
46            .to_owned();
47
48        return Ok(Box::new(RealHandle { root: root }));
49
50        struct RealHandle {
51            root: PathBuf,
52        }
53
54        impl Handle for RealHandle {
55            fn is_dir(&self, path: &RelativePath) -> bool {
56                path.to_path(&self.root).is_dir()
57            }
58
59            fn is_file(&self, path: &RelativePath) -> bool {
60                path.to_path(&self.root).is_file()
61            }
62
63            fn create_dir_all(&self, path: &RelativePath) -> Result<()> {
64                let path = path.to_path(&self.root);
65                Ok(fs::create_dir_all(&path)?)
66            }
67
68            fn create(&self, path: &RelativePath) -> Result<Box<io::Write>> {
69                let path = path.to_path(&self.root);
70                Ok(Box::new(fs::File::create(&path)?))
71            }
72        }
73    }
74}
75
76/// Capture all filesystem operations in-memory.
77///
78/// Used (among other things) for rendering output in WASM.
79pub struct CapturingFilesystem {
80    files: Rc<RefCell<LinkedHashMap<RelativePathBuf, Vec<u8>>>>,
81}
82
83impl CapturingFilesystem {
84    pub fn new() -> CapturingFilesystem {
85        Self {
86            files: Rc::new(RefCell::new(LinkedHashMap::new())),
87        }
88    }
89
90    /// Create a new filesystem handle that can be passed into `Context`.
91    pub fn filesystem(&self) -> Box<Filesystem> {
92        Box::new(CapturingFilesystem {
93            files: self.files.clone(),
94        })
95    }
96
97    /// Access the underlying captured files.
98    pub fn files(&self) -> &Rc<RefCell<LinkedHashMap<RelativePathBuf, Vec<u8>>>> {
99        &self.files
100    }
101}
102
103impl Filesystem for CapturingFilesystem {
104    fn open_root(&self, _root: Option<&Path>) -> Result<Box<Handle>> {
105        return Ok(Box::new(CapturingHandle {
106            files: self.files.clone(),
107        }));
108    }
109}
110
111/// A handle that captures files into a RefCell.
112struct CapturingHandle {
113    files: Rc<RefCell<LinkedHashMap<RelativePathBuf, Vec<u8>>>>,
114}
115
116impl Handle for CapturingHandle {
117    fn is_dir(&self, _path: &RelativePath) -> bool {
118        true
119    }
120
121    fn is_file(&self, path: &RelativePath) -> bool {
122        self.files.borrow().contains_key(path)
123    }
124
125    fn create_dir_all(&self, _path: &RelativePath) -> Result<()> {
126        Ok(())
127    }
128
129    fn create(&self, path: &RelativePath) -> Result<Box<io::Write>> {
130        Ok(Box::new(CapturingFileCreate {
131            files: self.files.clone(),
132            path: path.to_owned(),
133            buffer: Vec::new(),
134        }))
135    }
136}
137
138/// An 'open file' for the capturing handle.
139struct CapturingFileCreate {
140    files: Rc<RefCell<LinkedHashMap<RelativePathBuf, Vec<u8>>>>,
141    path: RelativePathBuf,
142    buffer: Vec<u8>,
143}
144
145impl io::Write for CapturingFileCreate {
146    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
147        self.buffer.write(buf)
148    }
149
150    fn flush(&mut self) -> io::Result<()> {
151        self.buffer.flush()
152    }
153}
154
155impl Drop for CapturingFileCreate {
156    fn drop(&mut self) {
157        let mut files = self.files.borrow_mut();
158        files.insert(self.path.clone(), self.buffer.clone());
159    }
160}