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 SyncDir: Filesystem {
117 fn sync_dir(&self, path: &Path) -> Result<()>;
118}
119
120pub trait Pread {
121 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize>;
122}
123
124pub trait Pwrite {
125 fn pwrite(&self, offset: u64, buf: &[u8]) -> Result<usize>;
126}
127
128pub trait SyncData {
129 fn sync_data(&self) -> Result<()>;
130}
131
132pub trait Truncate {
133 fn truncate(&self, len: u64) -> Result<()>;
134}
135
136pub trait Len {
137 fn len(&self) -> Result<u64>;
138
139 fn is_empty(&self) -> Result<bool> {
140 self.len().map(|len| len == 0)
141 }
142}
143
144#[derive(Clone)]
145pub enum Fs {
146 #[cfg(reifydb_target = "host")]
147 Host(HostFs),
148 Memory(MemoryFs),
149 #[cfg(feature = "testing")]
150 Testing(TestingFs),
151}
152
153pub enum File {
154 #[cfg(reifydb_target = "host")]
155 Host(HostFile),
156 Memory(MemoryFile),
157 #[cfg(feature = "testing")]
158 Testing(TestingFile),
159}
160
161pub enum FileMut {
162 #[cfg(reifydb_target = "host")]
163 Host(HostFileMut),
164 Memory(MemoryFileMut),
165 #[cfg(feature = "testing")]
166 Testing(TestingFileMut),
167}
168
169impl Filesystem for Fs {
170 type File = File;
171 type FileMut = FileMut;
172}
173
174impl Mkdir for Fs {
175 fn mkdir(&self, path: &Path) -> Result<()> {
176 match self {
177 #[cfg(reifydb_target = "host")]
178 Fs::Host(fs) => fs.mkdir(path),
179 Fs::Memory(fs) => fs.mkdir(path),
180 #[cfg(feature = "testing")]
181 Fs::Testing(fs) => fs.mkdir(path),
182 }
183 }
184}
185
186impl Create for Fs {
187 fn create(&self, path: &Path, len: u64) -> Result<FileMut> {
188 match self {
189 #[cfg(reifydb_target = "host")]
190 Fs::Host(fs) => fs.create(path, len).map(FileMut::Host),
191 Fs::Memory(fs) => fs.create(path, len).map(FileMut::Memory),
192 #[cfg(feature = "testing")]
193 Fs::Testing(fs) => fs.create(path, len).map(FileMut::Testing),
194 }
195 }
196}
197
198impl Open for Fs {
199 fn open(&self, path: &Path) -> Result<File> {
200 match self {
201 #[cfg(reifydb_target = "host")]
202 Fs::Host(fs) => fs.open(path).map(File::Host),
203 Fs::Memory(fs) => fs.open(path).map(File::Memory),
204 #[cfg(feature = "testing")]
205 Fs::Testing(fs) => fs.open(path).map(File::Testing),
206 }
207 }
208}
209
210impl OpenMut for Fs {
211 fn open_mut(&self, path: &Path) -> Result<FileMut> {
212 match self {
213 #[cfg(reifydb_target = "host")]
214 Fs::Host(fs) => fs.open_mut(path).map(FileMut::Host),
215 Fs::Memory(fs) => fs.open_mut(path).map(FileMut::Memory),
216 #[cfg(feature = "testing")]
217 Fs::Testing(fs) => fs.open_mut(path).map(FileMut::Testing),
218 }
219 }
220}
221
222impl ReadDir for Fs {
223 fn read_dir(&self, path: &Path) -> Result<Vec<PathBuf>> {
224 match self {
225 #[cfg(reifydb_target = "host")]
226 Fs::Host(fs) => fs.read_dir(path),
227 Fs::Memory(fs) => fs.read_dir(path),
228 #[cfg(feature = "testing")]
229 Fs::Testing(fs) => fs.read_dir(path),
230 }
231 }
232}
233
234impl Rename for Fs {
235 fn rename(&self, from: &Path, to: &Path) -> Result<()> {
236 match self {
237 #[cfg(reifydb_target = "host")]
238 Fs::Host(fs) => fs.rename(from, to),
239 Fs::Memory(fs) => fs.rename(from, to),
240 #[cfg(feature = "testing")]
241 Fs::Testing(fs) => fs.rename(from, to),
242 }
243 }
244}
245
246impl Unlink for Fs {
247 fn unlink(&self, path: &Path) -> Result<()> {
248 match self {
249 #[cfg(reifydb_target = "host")]
250 Fs::Host(fs) => fs.unlink(path),
251 Fs::Memory(fs) => fs.unlink(path),
252 #[cfg(feature = "testing")]
253 Fs::Testing(fs) => fs.unlink(path),
254 }
255 }
256}
257
258impl SyncDir for Fs {
259 fn sync_dir(&self, path: &Path) -> Result<()> {
260 match self {
261 #[cfg(reifydb_target = "host")]
262 Fs::Host(fs) => fs.sync_dir(path),
263 Fs::Memory(fs) => fs.sync_dir(path),
264 #[cfg(feature = "testing")]
265 Fs::Testing(fs) => fs.sync_dir(path),
266 }
267 }
268}
269
270impl Pread for File {
271 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
272 match self {
273 #[cfg(reifydb_target = "host")]
274 File::Host(file) => file.pread(offset, buf),
275 File::Memory(file) => file.pread(offset, buf),
276 #[cfg(feature = "testing")]
277 File::Testing(file) => file.pread(offset, buf),
278 }
279 }
280}
281
282impl Len for File {
283 fn len(&self) -> Result<u64> {
284 match self {
285 #[cfg(reifydb_target = "host")]
286 File::Host(file) => file.len(),
287 File::Memory(file) => file.len(),
288 #[cfg(feature = "testing")]
289 File::Testing(file) => file.len(),
290 }
291 }
292}
293
294impl Pread for FileMut {
295 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
296 match self {
297 #[cfg(reifydb_target = "host")]
298 FileMut::Host(file) => file.pread(offset, buf),
299 FileMut::Memory(file) => file.pread(offset, buf),
300 #[cfg(feature = "testing")]
301 FileMut::Testing(file) => file.pread(offset, buf),
302 }
303 }
304}
305
306impl Pwrite for FileMut {
307 fn pwrite(&self, offset: u64, buf: &[u8]) -> Result<usize> {
308 match self {
309 #[cfg(reifydb_target = "host")]
310 FileMut::Host(file) => file.pwrite(offset, buf),
311 FileMut::Memory(file) => file.pwrite(offset, buf),
312 #[cfg(feature = "testing")]
313 FileMut::Testing(file) => file.pwrite(offset, buf),
314 }
315 }
316}
317
318impl SyncData for FileMut {
319 fn sync_data(&self) -> Result<()> {
320 match self {
321 #[cfg(reifydb_target = "host")]
322 FileMut::Host(file) => file.sync_data(),
323 FileMut::Memory(file) => file.sync_data(),
324 #[cfg(feature = "testing")]
325 FileMut::Testing(file) => file.sync_data(),
326 }
327 }
328}
329
330impl Truncate for FileMut {
331 fn truncate(&self, len: u64) -> Result<()> {
332 match self {
333 #[cfg(reifydb_target = "host")]
334 FileMut::Host(file) => file.truncate(len),
335 FileMut::Memory(file) => file.truncate(len),
336 #[cfg(feature = "testing")]
337 FileMut::Testing(file) => file.truncate(len),
338 }
339 }
340}
341
342impl Len for FileMut {
343 fn len(&self) -> Result<u64> {
344 match self {
345 #[cfg(reifydb_target = "host")]
346 FileMut::Host(file) => file.len(),
347 FileMut::Memory(file) => file.len(),
348 #[cfg(feature = "testing")]
349 FileMut::Testing(file) => file.len(),
350 }
351 }
352}