bias_vfs/impls/
physical.rs1use crate::error::VfsErrorKind;
4use crate::{FileSystem, SeekAndWrite, VfsMetadata};
5use crate::{SeekAndRead, VfsFileType};
6use crate::{VfsError, VfsResult};
7use filetime::FileTime;
8use std::fs::{File, OpenOptions};
9use std::io::ErrorKind;
10use std::path::{Path, PathBuf};
11use std::time::SystemTime;
12
13#[derive(Debug)]
15pub struct PhysicalFS {
16 root: PathBuf,
17}
18
19impl PhysicalFS {
20 pub fn new<T: AsRef<Path>>(root: T) -> Self {
22 PhysicalFS {
23 root: root.as_ref().to_path_buf(),
24 }
25 }
26
27 fn get_path(&self, mut path: &str) -> PathBuf {
28 if path.starts_with('/') {
29 path = &path[1..];
30 }
31 self.root.join(path)
32 }
33}
34
35impl FileSystem for PhysicalFS {
36 fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> {
37 let entries = Box::new(
38 self.get_path(path)
39 .read_dir()?
40 .map(|entry| entry.unwrap().file_name().into_string().unwrap()),
41 );
42 Ok(entries)
43 }
44
45 fn create_dir(&self, path: &str) -> VfsResult<()> {
46 let fs_path = self.get_path(path);
47 std::fs::create_dir(&fs_path).map_err(|err| match err.kind() {
48 ErrorKind::AlreadyExists => {
49 let metadata = std::fs::metadata(&fs_path).unwrap();
50 if metadata.is_dir() {
51 return VfsError::from(VfsErrorKind::DirectoryExists);
52 }
53 VfsError::from(VfsErrorKind::FileExists)
54 }
55 _ => err.into(),
56 })?;
57 Ok(())
58 }
59
60 fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> {
61 Ok(Box::new(File::open(self.get_path(path))?))
62 }
63
64 fn create_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
65 Ok(Box::new(File::create(self.get_path(path))?))
66 }
67
68 fn append_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
69 Ok(Box::new(
70 OpenOptions::new().append(true).open(self.get_path(path))?,
71 ))
72 }
73
74 fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> {
75 let metadata = self.get_path(path).metadata()?;
76 Ok(if metadata.is_dir() {
77 VfsMetadata {
78 file_type: VfsFileType::Directory,
79 len: 0,
80 modified: metadata.modified().ok(),
81 created: metadata.created().ok(),
82 accessed: metadata.accessed().ok(),
83 }
84 } else {
85 VfsMetadata {
86 file_type: VfsFileType::File,
87 len: metadata.len(),
88 modified: metadata.modified().ok(),
89 created: metadata.created().ok(),
90 accessed: metadata.accessed().ok(),
91 }
92 })
93 }
94
95 fn set_modification_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
96 filetime::set_file_mtime(self.get_path(path), FileTime::from(time))?;
97 Ok(())
98 }
99
100 fn set_access_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
101 filetime::set_file_atime(self.get_path(path), FileTime::from(time))?;
102 Ok(())
103 }
104
105 fn exists(&self, path: &str) -> VfsResult<bool> {
106 Ok(self.get_path(path).exists())
107 }
108
109 fn remove_file(&self, path: &str) -> VfsResult<()> {
110 std::fs::remove_file(self.get_path(path))?;
111 Ok(())
112 }
113
114 fn remove_dir(&self, path: &str) -> VfsResult<()> {
115 std::fs::remove_dir(self.get_path(path))?;
116 Ok(())
117 }
118
119 fn copy_file(&self, src: &str, dest: &str) -> VfsResult<()> {
120 std::fs::copy(self.get_path(src), self.get_path(dest))?;
121 Ok(())
122 }
123
124 fn move_file(&self, src: &str, dest: &str) -> VfsResult<()> {
125 std::fs::rename(self.get_path(src), self.get_path(dest))?;
126
127 Ok(())
128 }
129
130 fn move_dir(&self, src: &str, dest: &str) -> VfsResult<()> {
131 let result = std::fs::rename(self.get_path(src), self.get_path(dest));
132 if result.is_err() {
133 return Err(VfsErrorKind::NotSupported.into());
135 }
136 Ok(())
137 }
138
139 fn real_path(&self, path: &str) -> VfsResult<PathBuf> {
140 let path = path.strip_prefix("/").unwrap_or(path);
141 let real_path = self.root.join(path);
142
143 Ok(real_path)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use std::path::Path;
150
151 use super::*;
152
153 use crate::VfsPath;
154 test_vfs!({
155 let temp_dir = std::env::temp_dir();
156 let dir = temp_dir.join(uuid::Uuid::new_v4().to_string());
157 std::fs::create_dir_all(&dir).unwrap();
158 PhysicalFS::new(dir)
159 });
160 test_vfs_readonly!({ PhysicalFS::new("test/test_directory") });
161
162 fn create_root() -> VfsPath {
163 PhysicalFS::new(std::env::current_dir().unwrap()).into()
164 }
165
166 #[test]
167 fn open_file() {
168 let expected = std::fs::read_to_string("Cargo.toml").unwrap();
169 let root = create_root();
170 let mut string = String::new();
171 root.join("Cargo.toml")
172 .unwrap()
173 .open_file()
174 .unwrap()
175 .read_to_string(&mut string)
176 .unwrap();
177 assert_eq!(string, expected);
178 }
179
180 #[test]
181 fn create_file() {
182 let root = create_root();
183 let _string = String::new();
184 let _ = std::fs::remove_file("target/test.txt");
185 root.join("target/test.txt")
186 .unwrap()
187 .create_file()
188 .unwrap()
189 .write_all(b"Testing only")
190 .unwrap();
191 let read = std::fs::read_to_string("target/test.txt").unwrap();
192 assert_eq!(read, "Testing only");
193 }
194
195 #[test]
196 fn append_file() {
197 let root = create_root();
198 let _string = String::new();
199 let _ = std::fs::remove_file("target/test_append.txt");
200 let path = root.join("target/test_append.txt").unwrap();
201 path.create_file().unwrap().write_all(b"Testing 1").unwrap();
202 path.append_file().unwrap().write_all(b"Testing 2").unwrap();
203 let read = std::fs::read_to_string("target/test_append.txt").unwrap();
204 assert_eq!(read, "Testing 1Testing 2");
205 }
206
207 #[test]
208 fn read_dir() {
209 let _expected = std::fs::read_to_string("Cargo.toml").unwrap();
210 let root = create_root();
211 let entries: Vec<_> = root.read_dir().unwrap().collect();
212 let map: Vec<_> = entries
213 .iter()
214 .map(|path: &VfsPath| path.as_str())
215 .filter(|x| x.ends_with(".toml"))
216 .collect();
217 assert_eq!(&["/Cargo.toml"], &map[..]);
218 }
219
220 #[test]
221 fn create_dir() {
222 let _ = std::fs::remove_dir("target/fs_test");
223 let root = create_root();
224 root.join("target/fs_test").unwrap().create_dir().unwrap();
225 let path = Path::new("target/fs_test");
226 assert!(path.exists(), "Path was not created");
227 assert!(path.is_dir(), "Path is not a directory");
228 std::fs::remove_dir("target/fs_test").unwrap();
229 }
230
231 #[test]
232 fn file_metadata() {
233 let expected = std::fs::read_to_string("Cargo.toml").unwrap();
234 let root = create_root();
235 let metadata = root.join("Cargo.toml").unwrap().metadata().unwrap();
236 assert_eq!(metadata.len, expected.len() as u64);
237 assert_eq!(metadata.file_type, VfsFileType::File);
238 }
239
240 #[test]
241 fn dir_metadata() {
242 let root = create_root();
243 let metadata = root.metadata().unwrap();
244 assert_eq!(metadata.len, 0);
245 assert_eq!(metadata.file_type, VfsFileType::Directory);
246 let metadata = root.join("src").unwrap().metadata().unwrap();
247 assert_eq!(metadata.len, 0);
248 assert_eq!(metadata.file_type, VfsFileType::Directory);
249 }
250}