logix_vfs/
lib.rs

1#![deny(warnings, clippy::all)]
2
3use std::{
4    io::ErrorKind,
5    path::{Path, PathBuf},
6};
7
8pub mod mem_fs;
9pub mod rel_fs;
10mod utils;
11
12pub use crate::{mem_fs::MemFs, rel_fs::RelFs};
13
14#[derive(thiserror::Error, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub enum Error {
16    #[error("Failed to locate {path:?}")]
17    NotFound { path: PathBuf },
18
19    #[error("Failed to access {path:?}")]
20    AccessDenied { path: PathBuf },
21
22    #[error("The path {path:?} is outside acceptable bounds")]
23    PathOutsideBounds { path: PathBuf },
24
25    #[error("The path {path:?} is not a directory")]
26    NotADirectory { path: PathBuf },
27
28    /// Used for other errors that is not defined already. Do not depend on this
29    /// for anything other than logging. If you need to check an error that is
30    /// reported as other, please request the error to be added instead.
31    #[error("{0}")]
32    Other(String),
33}
34
35impl Error {
36    pub fn to_io_error(&self) -> std::io::Error {
37        match self {
38            Self::NotFound { .. } => ErrorKind::NotFound.into(),
39            Self::AccessDenied { .. } => ErrorKind::PermissionDenied.into(),
40            Self::PathOutsideBounds { .. } => ErrorKind::InvalidInput.into(),
41            Self::NotADirectory { .. } => {
42                // TODO(2024.02): Once rust-lang/#86442 is stabilized, this can use ErrorKind::NotADirectory
43                std::io::Error::new(ErrorKind::Other, "Not a directory")
44            }
45            Self::Other(message) => std::io::Error::new(ErrorKind::Other, message.as_str()),
46        }
47    }
48
49    pub fn from_io(path: PathBuf, e: std::io::Error) -> Self {
50        match e.kind() {
51            ErrorKind::NotFound => Self::NotFound { path },
52            ErrorKind::PermissionDenied => Self::AccessDenied { path },
53            _ => {
54                let msg = e.to_string();
55                // TODO(2024.02): Once rust-lang/#86442 is stabilized, this work-around can be removed
56                match msg.as_str().split_once(" (os error") {
57                    Some(("Not a directory", _)) => Self::NotADirectory { path },
58                    _ => Self::Other(msg),
59                }
60            }
61        }
62    }
63}
64
65pub trait LogixVfsDirEntry {
66    fn path(&self) -> &Path;
67    fn is_dir(&self) -> bool;
68    fn is_file(&self) -> bool;
69    fn is_symlink(&self) -> bool;
70}
71
72pub trait LogixVfs: std::fmt::Debug + Send + Sync {
73    type RoFile: std::io::Read;
74    type DirEntry: LogixVfsDirEntry;
75    type ReadDir: Iterator<Item = Result<Self::DirEntry, Error>>;
76
77    fn canonicalize_path(&self, path: &Path) -> Result<PathBuf, Error>;
78    fn open_file(&self, path: &Path) -> Result<Self::RoFile, Error>;
79    fn read_dir(&self, path: &Path) -> Result<Self::ReadDir, Error>;
80}