expectation_shared/
filesystem.rs1use 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}