kxio/src/fs/path.rs
Paul Campbell d2ee798f25
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 1m59s
Rust / build (map[name:stable]) (push) Successful in 3m25s
Release Please / Release-plz (push) Successful in 34s
refactor: use generics for path type
2024-11-02 12:16:42 +00:00

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)
}
}