luminol_filesystem/
list.rs1use crate::{erased::ErasedFilesystem, DirEntry, Error, File, Metadata, OpenFlags, Result};
19use color_eyre::eyre::WrapErr;
20use itertools::Itertools;
21
22#[derive(Default)]
23pub struct FileSystem {
24 filesystems: Vec<Box<dyn ErasedFilesystem>>,
25}
26
27impl FileSystem {
28 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn push(&mut self, fs: impl crate::FileSystem + 'static) {
33 self.filesystems.push(Box::new(fs))
34 }
35}
36
37impl crate::FileSystem for FileSystem {
38 type File = Box<dyn File>;
39
40 fn open_file(
41 &self,
42 path: impl AsRef<camino::Utf8Path>,
43 flags: OpenFlags,
44 ) -> Result<Self::File> {
45 let path = path.as_ref();
46 let c = format!("While opening file {path:?} in a list filesystem");
47 let parent = path.parent().unwrap_or(path);
48 for fs in self.filesystems.iter() {
49 if fs.exists(path).wrap_err_with(|| c.clone())?
50 || (flags.contains(OpenFlags::Create)
51 && fs.exists(parent).wrap_err_with(|| c.clone())?)
52 {
53 return fs.open_file(path, flags).wrap_err_with(|| c.clone());
54 }
55 }
56 Err(Error::NotExist).wrap_err_with(|| c.clone())
57 }
58
59 fn metadata(&self, path: impl AsRef<camino::Utf8Path>) -> Result<Metadata> {
60 let path = path.as_ref();
61 let c = format!("While getting metadata for {path:?} in a list filesystem");
62 for fs in self.filesystems.iter() {
63 if fs.exists(path).wrap_err_with(|| c.clone())? {
64 return fs.metadata(path).wrap_err_with(|| c.clone());
65 }
66 }
67 Err(Error::NotExist).wrap_err_with(|| c.clone())
68 }
69
70 fn rename(
71 &self,
72 from: impl AsRef<camino::Utf8Path>,
73 to: impl AsRef<camino::Utf8Path>,
74 ) -> Result<()> {
75 let from = from.as_ref();
76 let to = to.as_ref();
77 let c = format!("While renaming {from:?} to {to:?} in a list filesystem");
78 for fs in self.filesystems.iter() {
79 if fs.exists(from).wrap_err_with(|| c.clone())? {
80 return fs.rename(from, to.as_ref()).wrap_err_with(|| c.clone());
81 }
82 }
83 Err(Error::NotExist).wrap_err_with(|| c.clone())
84 }
85
86 fn exists(&self, path: impl AsRef<camino::Utf8Path>) -> Result<bool> {
87 let path = path.as_ref();
88 let c = format!("While checking if {path:?} exists in a list filesystem");
89 for fs in self.filesystems.iter() {
90 if fs.exists(path).wrap_err_with(|| c.clone())? {
91 return Ok(true);
92 }
93 }
94 Ok(false)
95 }
96
97 fn create_dir(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
98 let path = path.as_ref();
99 let c = format!("While creating a directory at {path:?} in a list filesystem");
100 let fs = self
101 .filesystems
102 .first()
103 .ok_or(Error::NoFilesystems)
104 .wrap_err_with(|| c.clone())?;
105 fs.create_dir(path).wrap_err_with(|| c.clone())
106 }
107
108 fn remove_dir(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
109 let path = path.as_ref();
110 let c = format!("While removing a directory at {path:?} in a list filesystem");
111 let fs = self
112 .filesystems
113 .first()
114 .ok_or(Error::NoFilesystems)
115 .wrap_err_with(|| c.clone())?;
116 fs.remove_dir(path).wrap_err_with(|| c.clone())
117 }
118
119 fn remove_file(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
120 let path = path.as_ref();
121 let c = format!("While removing a file at {path:?} in a list filesystem");
122 let fs = self
123 .filesystems
124 .first()
125 .ok_or(Error::NoFilesystems)
126 .wrap_err_with(|| c.clone())?;
127 fs.remove_file(path).wrap_err_with(|| c.clone())
128 }
129
130 fn remove(&self, path: impl AsRef<camino::Utf8Path>) -> Result<()> {
131 let path = path.as_ref();
132 let c = format!("While removing {path:?} in a list filesystem");
133 let fs = self
134 .filesystems
135 .first()
136 .ok_or(Error::NoFilesystems)
137 .wrap_err_with(|| c.clone())?;
138 fs.remove(path).wrap_err_with(|| c.clone())
139 }
140
141 fn read_dir(&self, path: impl AsRef<camino::Utf8Path>) -> Result<Vec<DirEntry>> {
142 let path = path.as_ref();
143 let c =
144 format!("While reading the contents of the directory {path:?} in a list filesystem");
145
146 let mut entries = Vec::new();
147 for fs in self.filesystems.iter() {
148 if fs.exists(path).wrap_err_with(|| c.clone())? {
149 entries.extend(fs.read_dir(path).wrap_err_with(|| c.clone())?)
150 }
151 }
152 let entries = entries.into_iter().unique().collect_vec();
154
155 Ok(entries)
156 }
157
158 fn read(&self, path: impl AsRef<camino::Utf8Path>) -> Result<Vec<u8>> {
159 let path = path.as_ref();
160 let c = format!("While reading from the file {path:?} in a list filesystem");
161 for fs in self.filesystems.iter() {
162 if fs.exists(path).wrap_err_with(|| c.clone())? {
163 return fs.read(path).wrap_err_with(|| c.clone());
164 }
165 }
166 Err(Error::NotExist).wrap_err_with(|| c.clone())
167 }
168
169 fn read_to_string(&self, path: impl AsRef<camino::Utf8Path>) -> Result<String> {
170 let path = path.as_ref();
171 let c = format!("While reading from the file {path:?} in a list filesystem");
172 for fs in self.filesystems.iter() {
173 if fs.exists(path).wrap_err_with(|| c.clone())? {
174 return fs.read_to_string(path).wrap_err_with(|| c.clone());
175 }
176 }
177 Err(Error::NotExist).wrap_err_with(|| c.clone())
178 }
179
180 fn write(&self, path: impl AsRef<camino::Utf8Path>, data: impl AsRef<[u8]>) -> Result<()> {
181 let path = path.as_ref();
182 let c = format!("While writing to the file {path:?} in a list filesystem");
183 for fs in self.filesystems.iter() {
184 if fs.exists(path).wrap_err_with(|| c.clone())? {
185 return fs.write(path, data.as_ref()).wrap_err_with(|| c.clone());
186 }
187 }
188 Err(Error::NotExist).wrap_err_with(|| c.clone())
189 }
190}