use std::path::{Path, PathBuf};
use crate::fs::{Error, Result};
use super::{
path::{DirMarker, FileMarker, PathReal},
PathMarker,
};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FileSystem {
base: PathBuf,
}
pub type DirHandle = PathReal<DirMarker>;
pub type FileHandle = PathReal<FileMarker>;
pub type PathHandle<T> = PathReal<T>;
impl FileSystem {
pub const fn new(base: PathBuf) -> Self {
Self { base }
}
pub fn base(&self) -> &Path {
&self.base
}
pub fn path_of(&self, path: PathBuf) -> Result<PathBuf> {
let path_of = self.base.as_path().join(path);
self.validate(&path_of)?;
Ok(path_of)
}
pub fn dir(&self, path: &Path) -> DirHandle {
let mut dir = PathReal::new(&self.base, path);
if dir.error.is_none() {
if let Ok(exists) = dir.exists() {
if exists {
if let Ok(is_dir) = dir.is_dir() {
if !is_dir {
dir.put(Error::NotADirectory {
path: dir.as_pathbuf(),
})
}
}
}
}
}
dir
}
pub fn file(&self, path: &Path) -> FileHandle {
let mut file = PathReal::new(&self.base, path);
if file.error.is_none() {
if let Ok(exists) = file.exists() {
if exists {
if let Ok(is_file) = file.is_file() {
if !is_file {
file.put(Error::NotAFile {
path: file.as_pathbuf(),
})
}
}
}
}
}
file
}
pub fn path(&self, path: &Path) -> PathHandle<PathMarker> {
PathReal::new(&self.base, path)
}
fn validate(&self, path: &Path) -> Result<()> {
let path = self.clean_path(path)?;
if !path.starts_with(&self.base) {
return Err(Error::PathTraversal {
base: self.base.clone(),
path,
});
}
Ok(())
}
fn clean_path(&self, path: &Path) -> Result<PathBuf> {
use path_clean::PathClean;
let abs_path = if path.is_absolute() {
path.to_path_buf()
} else {
std::env::current_dir().map_err(Error::Io)?.join(path)
}
.clean();
Ok(abs_path)
}
}