1use std::{
2 cell::RefCell,
3 collections::HashMap,
4 fmt::Display,
5 fs,
6 io::{self},
7 path::{Path, PathBuf},
8 rc::Rc,
9 sync::Arc,
10};
11
12pub trait FileSystem: Clone {
14 type FSError: std::fmt::Debug;
16
17 fn exists<P: AsRef<Path>>(&self, path: P) -> Result<bool, Self::FSError>;
19
20 fn read<P: AsRef<Path>>(&self, filename: P) -> Result<String, Self::FSError>;
22
23 fn write<P: AsRef<Path>, C: AsRef<[u8]>>(
25 &self,
26 filename: P,
27 contents: C,
28 ) -> Result<(), Self::FSError>;
29}
30
31#[derive(Copy, Clone)]
33pub struct RealFileSystem;
34
35impl FileSystem for RealFileSystem {
36 type FSError = io::Error;
37
38 fn exists<P: AsRef<Path>>(&self, path: P) -> Result<bool, Self::FSError> {
39 Ok(path.as_ref().exists())
40 }
41
42 fn read<P: AsRef<Path>>(&self, filename: P) -> Result<String, Self::FSError> {
43 fs::read_to_string(filename)
44 }
45
46 fn write<P: AsRef<Path>, C: AsRef<[u8]>>(
47 &self,
48 filename: P,
49 contents: C,
50 ) -> Result<(), Self::FSError> {
51 fs::write(filename, contents)
52 }
53}
54
55#[derive(Clone)]
56pub struct SymbolicFileSystem(Rc<RefCell<HashMap<String, String>>>);
57
58impl FileSystem for SymbolicFileSystem {
59 type FSError = ();
60
61 fn exists<P: AsRef<Path>>(&self, path: P) -> Result<bool, Self::FSError> {
62 let path_str = path.as_ref().to_str().unwrap_or("").to_string();
63 Ok(self.0.borrow().contains_key(&path_str))
64 }
65
66 fn read<P: AsRef<Path>>(&self, filename: P) -> Result<String, Self::FSError> {
67 let path_str = filename.as_ref().to_str().unwrap_or("").to_string();
68 Ok(self.0.borrow().get(&path_str).cloned().unwrap_or_default())
69 }
70
71 fn write<P: AsRef<Path>, C: AsRef<[u8]>>(
72 &self,
73 filename: P,
74 contents: C,
75 ) -> Result<(), Self::FSError> {
76 let path_str = filename.as_ref().to_str().unwrap_or("").to_string();
77 let content_str = String::from_utf8_lossy(contents.as_ref()).to_string();
78 self.0.borrow_mut().insert(path_str, content_str);
79 Ok(())
80 }
81}
82
83impl Display for SymbolicFileSystem {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 writeln!(f, "SymbolicFileSystem {{")?;
86 for (key, value) in self.0.borrow().iter() {
87 writeln!(f, "file \"{}\": {{|", key)?;
88 writeln!(f, "{}", value)?;
89 writeln!(f, "|}}")?;
90 }
91 writeln!(f, "}}")
92 }
93}
94
95impl SymbolicFileSystem {
96 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
97 let mut map = HashMap::new();
98 let mut to_visit = vec![path.as_ref().to_path_buf()];
99
100 while let Some(current_path) = to_visit.pop() {
101 let metadata = fs::metadata(¤t_path)?;
102 if metadata.is_dir() {
103 let dir_entries = fs::read_dir(¤t_path)?;
104 for entry in dir_entries {
105 let entry = entry?;
106 to_visit.push(entry.path());
107 }
108 } else {
109 let contents = fs::read_to_string(¤t_path)?;
110 let canonical_path = fs::canonicalize(¤t_path)?.to_str().unwrap().to_string();
111 map.insert(canonical_path, contents);
112 }
113 }
114
115 Ok(SymbolicFileSystem(Rc::new(RefCell::new(map))))
116 }
117
118 pub fn get(&self, path: &str) -> String {
119 let path_buf = PathBuf::from(path);
120 let canonical_path = fs::canonicalize(&path_buf)
121 .ok()
122 .and_then(|p| p.to_str().map(String::from))
123 .unwrap_or_else(|| path.to_string());
124 self.0.borrow().get(&canonical_path).cloned().unwrap_or_default()
125 }
126}
127
128#[derive(Debug, Clone)]
129pub struct FileLoader<T: FileSystem>(T);
130
131unsafe impl<T: FileSystem> Send for FileLoader<T> {}
132unsafe impl<T: FileSystem> Sync for FileLoader<T> {}
133
134impl<T: FileSystem> FileLoader<T> {
135 pub fn new(fs: T) -> Self {
136 FileLoader(fs)
137 }
138
139 pub fn file_exists(&self, path: &Path) -> bool {
140 self.0.exists(path).unwrap_or(false)
141 }
142
143 pub fn read_file(&self, path: &Path) -> io::Result<String> {
144 self.0.read(path).map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
145 }
146
147 pub fn read_binary_file(&self, path: &Path) -> io::Result<Arc<[u8]>> {
148 let content = self.read_file(path)?;
149 Ok(Arc::from(content.into_bytes().as_slice()))
150 }
151}