1#[cfg(reifydb_target = "host")]
5pub mod host;
6pub mod memory;
7#[cfg(feature = "testing")]
8pub mod testing;
9
10use std::{
11 error::Error as StdError,
12 fmt,
13 fmt::Display,
14 io,
15 path::{Path, PathBuf},
16};
17
18#[cfg(reifydb_target = "host")]
19use crate::io::fs::host::{HostFile, HostFileMut, HostFs};
20use crate::io::fs::memory::{MemoryFile, MemoryFileMut, MemoryFs};
21#[cfg(feature = "testing")]
22use crate::io::fs::testing::{TestingFile, TestingFileMut, TestingFs};
23
24pub type Result<T> = std::result::Result<T, FsError>;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum FsError {
28 NotFound(PathBuf),
29 AlreadyExists(PathBuf),
30 NoSpace(PathBuf),
31 NotADirectory(PathBuf),
32 Io {
33 path: PathBuf,
34 message: String,
35 },
36}
37
38impl FsError {
39 pub fn from_io(path: &Path, err: io::Error) -> Self {
40 match err.kind() {
41 io::ErrorKind::NotFound => FsError::NotFound(path.to_path_buf()),
42 io::ErrorKind::AlreadyExists => FsError::AlreadyExists(path.to_path_buf()),
43 io::ErrorKind::StorageFull => FsError::NoSpace(path.to_path_buf()),
44 io::ErrorKind::NotADirectory => FsError::NotADirectory(path.to_path_buf()),
45 _ => FsError::Io {
46 path: path.to_path_buf(),
47 message: err.to_string(),
48 },
49 }
50 }
51
52 pub fn path(&self) -> &Path {
53 match self {
54 FsError::NotFound(path) => path,
55 FsError::AlreadyExists(path) => path,
56 FsError::NoSpace(path) => path,
57 FsError::NotADirectory(path) => path,
58 FsError::Io {
59 path,
60 ..
61 } => path,
62 }
63 }
64}
65
66impl Display for FsError {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 FsError::NotFound(path) => write!(f, "not found: {}", path.display()),
70 FsError::AlreadyExists(path) => write!(f, "already exists: {}", path.display()),
71 FsError::NoSpace(path) => write!(f, "out of space: {}", path.display()),
72 FsError::NotADirectory(path) => write!(f, "not a directory: {}", path.display()),
73 FsError::Io {
74 path,
75 message,
76 } => write!(f, "io error on {}: {}", path.display(), message),
77 }
78 }
79}
80
81impl StdError for FsError {}
82
83pub trait Filesystem {
84 type File: Pread + Len;
85 type FileMut: Pread + Pwrite + SyncData + Truncate + Len;
86}
87
88pub trait Mkdir: Filesystem {
89 fn mkdir(&self, path: &Path) -> Result<()>;
90}
91
92pub trait Create: Filesystem {
93 fn create(&self, path: &Path, len: u64) -> Result<Self::FileMut>;
94}
95
96pub trait Open: Filesystem {
97 fn open(&self, path: &Path) -> Result<Self::File>;
98}
99
100pub trait OpenMut: Filesystem {
101 fn open_mut(&self, path: &Path) -> Result<Self::FileMut>;
102}
103
104pub trait ReadDir: Filesystem {
105 fn read_dir(&self, path: &Path) -> Result<Vec<PathBuf>>;
106}
107
108pub trait Rename: Filesystem {
109 fn rename(&self, from: &Path, to: &Path) -> Result<()>;
110}
111
112pub trait Unlink: Filesystem {
113 fn unlink(&self, path: &Path) -> Result<()>;
114}
115
116pub trait RemoveDir: Filesystem {
117 fn remove_dir(&self, path: &Path) -> Result<()>;
118}
119
120pub trait SyncDir: Filesystem {
121 fn sync_dir(&self, path: &Path) -> Result<()>;
122}
123
124pub trait Pread {
125 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize>;
126}
127
128pub trait Pwrite {
129 fn pwrite(&self, offset: u64, buf: &[u8]) -> Result<usize>;
130}
131
132pub trait SyncData {
133 fn sync_data(&self) -> Result<()>;
134}
135
136pub trait Truncate {
137 fn truncate(&self, len: u64) -> Result<()>;
138}
139
140pub trait Len {
141 fn len(&self) -> Result<u64>;
142
143 fn is_empty(&self) -> Result<bool> {
144 self.len().map(|len| len == 0)
145 }
146}
147
148#[derive(Clone)]
149pub enum Fs {
150 #[cfg(reifydb_target = "host")]
151 Host(HostFs),
152 Memory(MemoryFs),
153 #[cfg(feature = "testing")]
154 Testing(TestingFs),
155}
156
157pub enum File {
158 #[cfg(reifydb_target = "host")]
159 Host(HostFile),
160 Memory(MemoryFile),
161 #[cfg(feature = "testing")]
162 Testing(TestingFile),
163}
164
165pub enum FileMut {
166 #[cfg(reifydb_target = "host")]
167 Host(HostFileMut),
168 Memory(MemoryFileMut),
169 #[cfg(feature = "testing")]
170 Testing(TestingFileMut),
171}
172
173impl Filesystem for Fs {
174 type File = File;
175 type FileMut = FileMut;
176}
177
178impl Mkdir for Fs {
179 fn mkdir(&self, path: &Path) -> Result<()> {
180 match self {
181 #[cfg(reifydb_target = "host")]
182 Fs::Host(fs) => fs.mkdir(path),
183 Fs::Memory(fs) => fs.mkdir(path),
184 #[cfg(feature = "testing")]
185 Fs::Testing(fs) => fs.mkdir(path),
186 }
187 }
188}
189
190impl Create for Fs {
191 fn create(&self, path: &Path, len: u64) -> Result<FileMut> {
192 match self {
193 #[cfg(reifydb_target = "host")]
194 Fs::Host(fs) => fs.create(path, len).map(FileMut::Host),
195 Fs::Memory(fs) => fs.create(path, len).map(FileMut::Memory),
196 #[cfg(feature = "testing")]
197 Fs::Testing(fs) => fs.create(path, len).map(FileMut::Testing),
198 }
199 }
200}
201
202impl Open for Fs {
203 fn open(&self, path: &Path) -> Result<File> {
204 match self {
205 #[cfg(reifydb_target = "host")]
206 Fs::Host(fs) => fs.open(path).map(File::Host),
207 Fs::Memory(fs) => fs.open(path).map(File::Memory),
208 #[cfg(feature = "testing")]
209 Fs::Testing(fs) => fs.open(path).map(File::Testing),
210 }
211 }
212}
213
214impl OpenMut for Fs {
215 fn open_mut(&self, path: &Path) -> Result<FileMut> {
216 match self {
217 #[cfg(reifydb_target = "host")]
218 Fs::Host(fs) => fs.open_mut(path).map(FileMut::Host),
219 Fs::Memory(fs) => fs.open_mut(path).map(FileMut::Memory),
220 #[cfg(feature = "testing")]
221 Fs::Testing(fs) => fs.open_mut(path).map(FileMut::Testing),
222 }
223 }
224}
225
226impl ReadDir for Fs {
227 fn read_dir(&self, path: &Path) -> Result<Vec<PathBuf>> {
228 match self {
229 #[cfg(reifydb_target = "host")]
230 Fs::Host(fs) => fs.read_dir(path),
231 Fs::Memory(fs) => fs.read_dir(path),
232 #[cfg(feature = "testing")]
233 Fs::Testing(fs) => fs.read_dir(path),
234 }
235 }
236}
237
238impl Rename for Fs {
239 fn rename(&self, from: &Path, to: &Path) -> Result<()> {
240 match self {
241 #[cfg(reifydb_target = "host")]
242 Fs::Host(fs) => fs.rename(from, to),
243 Fs::Memory(fs) => fs.rename(from, to),
244 #[cfg(feature = "testing")]
245 Fs::Testing(fs) => fs.rename(from, to),
246 }
247 }
248}
249
250impl Unlink for Fs {
251 fn unlink(&self, path: &Path) -> Result<()> {
252 match self {
253 #[cfg(reifydb_target = "host")]
254 Fs::Host(fs) => fs.unlink(path),
255 Fs::Memory(fs) => fs.unlink(path),
256 #[cfg(feature = "testing")]
257 Fs::Testing(fs) => fs.unlink(path),
258 }
259 }
260}
261
262impl RemoveDir for Fs {
263 fn remove_dir(&self, path: &Path) -> Result<()> {
264 match self {
265 #[cfg(reifydb_target = "host")]
266 Fs::Host(fs) => fs.remove_dir(path),
267 Fs::Memory(fs) => fs.remove_dir(path),
268 #[cfg(feature = "testing")]
269 Fs::Testing(fs) => fs.remove_dir(path),
270 }
271 }
272}
273
274impl SyncDir for Fs {
275 fn sync_dir(&self, path: &Path) -> Result<()> {
276 match self {
277 #[cfg(reifydb_target = "host")]
278 Fs::Host(fs) => fs.sync_dir(path),
279 Fs::Memory(fs) => fs.sync_dir(path),
280 #[cfg(feature = "testing")]
281 Fs::Testing(fs) => fs.sync_dir(path),
282 }
283 }
284}
285
286impl Pread for File {
287 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
288 match self {
289 #[cfg(reifydb_target = "host")]
290 File::Host(file) => file.pread(offset, buf),
291 File::Memory(file) => file.pread(offset, buf),
292 #[cfg(feature = "testing")]
293 File::Testing(file) => file.pread(offset, buf),
294 }
295 }
296}
297
298impl Len for File {
299 fn len(&self) -> Result<u64> {
300 match self {
301 #[cfg(reifydb_target = "host")]
302 File::Host(file) => file.len(),
303 File::Memory(file) => file.len(),
304 #[cfg(feature = "testing")]
305 File::Testing(file) => file.len(),
306 }
307 }
308}
309
310impl Pread for FileMut {
311 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
312 match self {
313 #[cfg(reifydb_target = "host")]
314 FileMut::Host(file) => file.pread(offset, buf),
315 FileMut::Memory(file) => file.pread(offset, buf),
316 #[cfg(feature = "testing")]
317 FileMut::Testing(file) => file.pread(offset, buf),
318 }
319 }
320}
321
322impl Pwrite for FileMut {
323 fn pwrite(&self, offset: u64, buf: &[u8]) -> Result<usize> {
324 match self {
325 #[cfg(reifydb_target = "host")]
326 FileMut::Host(file) => file.pwrite(offset, buf),
327 FileMut::Memory(file) => file.pwrite(offset, buf),
328 #[cfg(feature = "testing")]
329 FileMut::Testing(file) => file.pwrite(offset, buf),
330 }
331 }
332}
333
334impl SyncData for FileMut {
335 fn sync_data(&self) -> Result<()> {
336 match self {
337 #[cfg(reifydb_target = "host")]
338 FileMut::Host(file) => file.sync_data(),
339 FileMut::Memory(file) => file.sync_data(),
340 #[cfg(feature = "testing")]
341 FileMut::Testing(file) => file.sync_data(),
342 }
343 }
344}
345
346impl Truncate for FileMut {
347 fn truncate(&self, len: u64) -> Result<()> {
348 match self {
349 #[cfg(reifydb_target = "host")]
350 FileMut::Host(file) => file.truncate(len),
351 FileMut::Memory(file) => file.truncate(len),
352 #[cfg(feature = "testing")]
353 FileMut::Testing(file) => file.truncate(len),
354 }
355 }
356}
357
358impl Len for FileMut {
359 fn len(&self) -> Result<u64> {
360 match self {
361 #[cfg(reifydb_target = "host")]
362 FileMut::Host(file) => file.len(),
363 FileMut::Memory(file) => file.len(),
364 #[cfg(feature = "testing")]
365 FileMut::Testing(file) => file.len(),
366 }
367 }
368}