use std::ffi::OsString;
use std::path::{Path, PathBuf};
use crate::{BufferId, LocationRequestId, NodeId, PreviewRequestId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum EntryKind {
Dir,
File,
Symlink,
}
impl EntryKind {
pub fn sort_rank(self) -> u8 {
match self {
EntryKind::Dir => 0,
EntryKind::File | EntryKind::Symlink => 1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DirEntryInfo {
pub name: OsString,
pub path: PathBuf,
pub kind: EntryKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FsError {
NotFound(PathBuf),
PermissionDenied(PathBuf),
NotADirectory(PathBuf),
AlreadyExists(PathBuf),
Other { path: PathBuf, message: String },
}
impl FsError {
pub fn path(&self) -> &Path {
match self {
FsError::NotFound(p)
| FsError::PermissionDenied(p)
| FsError::NotADirectory(p)
| FsError::AlreadyExists(p)
| FsError::Other { path: p, .. } => p,
}
}
}
impl std::fmt::Display for FsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FsError::NotFound(p) => write!(f, "not found: {}", p.display()),
FsError::PermissionDenied(p) => write!(f, "permission denied: {}", p.display()),
FsError::NotADirectory(p) => write!(f, "not a directory: {}", p.display()),
FsError::AlreadyExists(p) => write!(f, "already exists: {}", p.display()),
FsError::Other { path, message } => write!(f, "{}: {message}", path.display()),
}
}
}
impl std::error::Error for FsError {}
pub type FsResult<T> = Result<T, FsError>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FsRequest {
ReadDir {
id: NodeId,
path: PathBuf,
},
ReadFile {
buffer: BufferId,
path: PathBuf,
},
ReadPreview {
request: PreviewRequestId,
path: PathBuf,
line: usize,
context: usize,
},
ResolvePath {
request: LocationRequestId,
path: PathBuf,
},
WriteFile {
buffer: BufferId,
path: PathBuf,
contents: Vec<u8>,
version: u64,
},
Watch(PathBuf),
CreateFile(PathBuf),
CreateDir(PathBuf),
Rename {
from: PathBuf,
to: PathBuf,
},
Remove {
path: PathBuf,
recursive: bool,
},
Shutdown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FsEvent {
DirLoaded {
id: NodeId,
entries: Vec<DirEntryInfo>,
},
DirFailed {
id: NodeId,
error: FsError,
},
Changed(Vec<PathBuf>),
MutationFailed(FsError),
FileLoaded {
buffer: BufferId,
path: PathBuf,
contents: Vec<u8>,
},
FileSaved {
buffer: BufferId,
version: u64,
},
FileFailed {
buffer: BufferId,
error: FsError,
},
PreviewLoaded {
request: PreviewRequestId,
path: PathBuf,
start_line: usize,
text: String,
},
PreviewFailed {
request: PreviewRequestId,
path: PathBuf,
error: FsError,
},
PathResolved {
request: LocationRequestId,
path: PathBuf,
},
PathResolveFailed {
request: LocationRequestId,
path: PathBuf,
error: FsError,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dirs_outrank_files_and_symlinks() {
assert!(EntryKind::Dir.sort_rank() < EntryKind::File.sort_rank());
assert_eq!(EntryKind::File.sort_rank(), EntryKind::Symlink.sort_rank());
}
#[test]
fn error_carries_the_offending_path() {
let e = FsError::PermissionDenied(PathBuf::from("/root/secret"));
assert_eq!(e.path(), Path::new("/root/secret"));
assert!(e.to_string().contains("permission denied"));
}
}