118 lines
2.9 KiB
Rust
118 lines
2.9 KiB
Rust
//
|
|
use std::{
|
|
marker::PhantomData,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use crate::fs::{Error, Result};
|
|
|
|
pub trait PathType {}
|
|
|
|
pub struct PathG;
|
|
impl PathType for PathG {}
|
|
|
|
pub struct FileG;
|
|
impl PathType for FileG {}
|
|
|
|
pub struct DirG;
|
|
impl PathType for DirG {}
|
|
|
|
#[derive(Debug)]
|
|
pub struct PathReal<'base, 'path, T: PathType> {
|
|
base: &'base Path,
|
|
path: &'path Path,
|
|
_phanton: PhantomData<T>,
|
|
pub(super) error: Option<Error>,
|
|
}
|
|
impl<'base, 'path, T: PathType> PathReal<'base, 'path, T> {
|
|
pub(super) fn new(base: &'base Path, path: &'path Path) -> Self {
|
|
Self {
|
|
base,
|
|
path,
|
|
_phanton: PhantomData::<T>,
|
|
error: PathReal::<T>::validate(base, path),
|
|
}
|
|
}
|
|
|
|
pub fn as_pathbuf(&self) -> PathBuf {
|
|
self.base.join(self.path)
|
|
}
|
|
|
|
pub(super) fn put(&mut self, error: Error) {
|
|
if self.error.is_none() {
|
|
self.error.replace(error);
|
|
}
|
|
}
|
|
|
|
fn validate(base: &Path, path: &Path) -> Option<Error> {
|
|
match PathReal::<PathG>::clean_path(path) {
|
|
Err(error) => Some(error),
|
|
Ok(path) => {
|
|
if !path.starts_with(base) {
|
|
return Some(Error::PathTraversal {
|
|
base: base.to_path_buf(),
|
|
path,
|
|
});
|
|
}
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn clean_path(path: &Path) -> Result<PathBuf> {
|
|
// let path = path.as_ref();
|
|
use path_clean::PathClean;
|
|
let abs_path = if path.is_absolute() {
|
|
path.to_path_buf()
|
|
} else {
|
|
std::env::current_dir().expect("current_dir").join(path)
|
|
}
|
|
.clean();
|
|
Ok(abs_path)
|
|
}
|
|
|
|
pub(super) fn check_error(&mut self) -> Result<()> {
|
|
if let Some(error) = self.error.take() {
|
|
return Err(error);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn exists(&mut self) -> Result<bool> {
|
|
self.check_error()?;
|
|
Ok(self.as_pathbuf().exists())
|
|
}
|
|
|
|
pub fn is_dir(&mut self) -> Result<bool> {
|
|
self.check_error()?;
|
|
Ok(self.as_pathbuf().is_dir())
|
|
}
|
|
|
|
pub fn is_file(&mut self) -> Result<bool> {
|
|
self.check_error()?;
|
|
Ok(self.as_pathbuf().is_file())
|
|
}
|
|
|
|
pub fn as_dir(&mut self) -> Result<Option<PathReal<'base, 'path, DirG>>> {
|
|
self.check_error()?;
|
|
if self.as_pathbuf().is_dir() {
|
|
Ok(Some(PathReal::new(self.base, self.path)))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
pub fn as_file(&mut self) -> Result<Option<PathReal<'base, 'path, FileG>>> {
|
|
self.check_error()?;
|
|
if self.as_pathbuf().is_file() {
|
|
Ok(Some(PathReal::new(self.base, self.path)))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|
|
impl From<PathReal<'_, '_, PathG>> for PathBuf {
|
|
fn from(path: PathReal<PathG>) -> Self {
|
|
path.base.join(path.path)
|
|
}
|
|
}
|