expectation_shared/
filesystem.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::fs::{create_dir_all, File};
4use std::io::{BufRead, Cursor, Result as IoResult, Seek, Write};
5use std::io::{BufReader, BufWriter, Error as IoError, ErrorKind};
6use std::path::{Path, PathBuf};
7use std::rc::Rc;
8
9pub trait ReadSeek: Seek + BufRead {}
10impl<R: BufRead + Seek> ReadSeek for R {}
11
12#[derive(Clone)]
13pub struct RealFileSystem {
14    pub root: PathBuf,
15}
16
17#[derive(Clone, Debug)]
18pub struct FakeFileSystem {
19    root: PathBuf,
20    mapping: Rc<RefCell<HashMap<PathBuf, Vec<u8>>>>,
21}
22
23pub trait FileSystem {
24    fn duplicate(&self) -> Box<FileSystem>;
25    fn subsystem(&self, path: &Path) -> Box<FileSystem>;
26    fn exists(&self, path: &Path) -> bool;
27    fn read(&self, path: &Path, f: &mut FnMut(&mut ReadSeek) -> IoResult<()>) -> IoResult<()>;
28    fn write(&self, path: &Path, f: &mut FnMut(&mut Write) -> IoResult<()>) -> IoResult<()>;
29    fn full_path_for(&self, path: &Path) -> PathBuf;
30    fn files(&self) -> Vec<PathBuf>;
31    fn remove(&self, path: &Path) -> IoResult<()>;
32    fn is_empty(&self) -> bool {
33        self.files().is_empty()
34    }
35    fn copy(&self, from: &Path, to: &Path) -> IoResult<()> {
36        self.write(to, &mut |writer| {
37            self.read(from, &mut |reader| {
38                ::std::io::copy(reader, writer).map(|_| ())
39            })
40        })
41    }
42}
43
44impl FakeFileSystem {
45    pub fn new() -> Self {
46        FakeFileSystem {
47            root: PathBuf::from("/"),
48            mapping: Rc::new(RefCell::new(HashMap::new())),
49        }
50    }
51}
52
53impl FileSystem for RealFileSystem {
54    fn subsystem(&self, path: &Path) -> Box<FileSystem> {
55        assert!(path.is_relative(), "path must be relative");
56        let mut new = self.clone();
57        new.root.push(path);
58        Box::new(new)
59    }
60
61    fn remove(&self, path: &Path) -> IoResult<()> {
62        let path = self.root.join(path);
63        ::std::fs::remove_file(path)
64    }
65
66    fn duplicate(&self) -> Box<FileSystem> {
67        Box::new(self.clone())
68    }
69
70    fn exists(&self, path: &Path) -> bool {
71        let path = self.root.join(path);
72        path.exists()
73    }
74
75    fn read(&self, path: &Path, f: &mut FnMut(&mut ReadSeek) -> IoResult<()>) -> IoResult<()> {
76        let path = self.root.join(path);
77        match File::open(path) {
78            Ok(file) => {
79                let mut file = BufReader::new(file);
80                f(&mut file)
81            }
82            Err(e) => Err(e),
83        }
84    }
85
86    fn write(&self, path: &Path, f: &mut FnMut(&mut Write) -> IoResult<()>) -> IoResult<()> {
87        let path = self.root.join(path);
88        create_dir_all(path.parent().unwrap())?;
89
90        match File::create(path) {
91            Ok(file) => {
92                let mut file = BufWriter::new(file);
93                f(&mut file)
94            }
95            Err(e) => Err(e),
96        }
97    }
98
99    fn full_path_for(&self, path: &Path) -> PathBuf {
100        self.root.join(path)
101    }
102
103    fn files(&self) -> Vec<PathBuf> {
104        ::walkdir::WalkDir::new(&self.root)
105            .into_iter()
106            .filter_map(|e| e.ok())
107            .filter(|e| e.file_type().is_file())
108            .map(|p| p.path().to_owned())
109            .filter_map(|p| p.strip_prefix(&self.root).ok().map(|p| p.to_owned()))
110            .collect()
111    }
112}
113
114impl FileSystem for FakeFileSystem {
115    fn subsystem(&self, path: &Path) -> Box<FileSystem> {
116        assert!(path.is_relative(), "path must be relative");
117        let mut new = self.clone();
118        new.root.push(path);
119        Box::new(new)
120    }
121    fn duplicate(&self) -> Box<FileSystem> {
122        Box::new(self.clone())
123    }
124
125    fn exists(&self, path: &Path) -> bool {
126        let path = self.root.join(path);
127        self.mapping.borrow().contains_key(&path)
128    }
129
130    fn remove(&self, path: &Path) -> IoResult<()> {
131        let path = self.root.join(path);
132        self.mapping.borrow_mut().remove(&path);
133        Ok(())
134    }
135
136    fn read(&self, path: &Path, f: &mut FnMut(&mut ReadSeek) -> IoResult<()>) -> IoResult<()> {
137        let path = self.root.join(path);
138
139        let contents = match self.mapping.borrow().get(&path) {
140            Some(contents) => contents.clone(),
141            None => {
142                return Err(IoError::new(
143                    ErrorKind::NotFound,
144                    format!("{:?} does not exist", path),
145                ))
146            }
147        };
148
149        f(&mut Cursor::new(&contents[..]))
150    }
151
152    fn write(&self, path: &Path, f: &mut FnMut(&mut Write) -> IoResult<()>) -> IoResult<()> {
153        let path = self.root.join(path);
154
155        let mut contents = vec![];
156        f(&mut contents)?;
157
158        self.mapping.borrow_mut().insert(path, contents);
159        Ok(())
160    }
161
162    fn full_path_for(&self, path: &Path) -> PathBuf {
163        self.root.join(path)
164    }
165
166    fn files(&self) -> Vec<PathBuf> {
167        let root = self.root.clone();
168        self.mapping
169            .borrow()
170            .keys()
171            .filter_map(|p| p.strip_prefix(&root).ok())
172            .map(|p| p.into())
173            .collect()
174    }
175}