diff --git a/justfile b/justfile index e01dfe3..a6a5136 100644 --- a/justfile +++ b/justfile @@ -1,7 +1,8 @@ build: + cargo fmt --check + cargo hack --feature-powerset clippy cargo hack --feature-powerset build cargo hack --feature-powerset test - cargo hack --feature-powerset clippy install-hooks: @echo "Installing git hooks" diff --git a/src/fs/real.rs b/src/fs/real.rs deleted file mode 100644 index 710a2e1..0000000 --- a/src/fs/real.rs +++ /dev/null @@ -1,246 +0,0 @@ -use std::{ - fmt::Display, - path::{Path, PathBuf}, -}; - -use crate::fs::DirItem; - -use super::{DirItemIterator, Result}; - -#[derive(Clone, Debug)] -pub struct FileSystem { - base: PathBuf, -} -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 { - let path_of = self.base.as_path().join(path); - self.validate(&path_of)?; - Ok(path_of) - } - - pub fn dir<'base, 'path>(&'base self, path: &'path Path) -> DirReal<'base, 'path> { - DirReal::new(self.base(), path) - } - - pub fn file<'base, 'path>(&'base self, path: &'path Path) -> FileReal<'base, 'path> { - FileReal::new(self.base(), path) - } - - pub fn path<'base, 'path>(&'base self, path: &'path Path) -> PathReal<'base, 'path> { - 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(super::Error::PathTraversal { - base: self.base.clone(), - path, - }); - } - Ok(()) - } - - fn clean_path(&self, path: &Path) -> Result { - // 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()?.join(path) - } - .clean(); - Ok(abs_path) - } -} - -pub struct DirReal<'base, 'path> { - path: PathReal<'base, 'path>, -} -impl<'base, 'path> DirReal<'base, 'path> { - fn new(base: &'base Path, path: &'path Path) -> Self { - let mut path = PathReal::new(base, path); - if path.error.is_none() { - if let Ok(exists) = path.exists() { - if exists { - if let Ok(is_dir) = path.is_dir() { - if !is_dir { - path.put(super::Error::NotADirectory { - path: path.full_path(), - }) - } - } - } - } - } - Self { path } - } - - pub fn create(&mut self) -> Result<()> { - self.path.check_error()?; - std::fs::create_dir(self.path.full_path()).map_err(Into::into) - } - - pub fn create_all(&mut self) -> Result<()> { - self.path.check_error()?; - std::fs::create_dir_all(self.path.full_path()).map_err(Into::into) - } - - pub fn read(&mut self) -> Result>>> { - self.path.check_error()?; - let read_dir = std::fs::read_dir(self.path.full_path())?; - Ok(Box::new(DirItemIterator::new(read_dir))) - } - - pub fn exists(&mut self) -> Result { - self.path.check_error()?; - self.path.exists() - } - - pub fn is_dir(&mut self) -> Result { - self.path.check_error()?; - Ok(true) - } - - pub fn is_file(&mut self) -> Result { - self.path.check_error()?; - Ok(false) - } -} -pub struct FileReal<'base, 'path> { - path: PathReal<'base, 'path>, -} -impl<'base, 'path> FileReal<'base, 'path> { - fn new(base: &'base Path, path: &'path Path) -> Self { - Self { - path: PathReal::new(base, path), - } - } - - pub fn reader(&mut self) -> Result { - self.path.check_error()?; - ReaderReal::new(&self.path.full_path()) - } - - pub fn write(&mut self, contents: &str) -> Result<()> { - self.path.check_error()?; - std::fs::write(self.path.full_path(), contents).map_err(Into::into) - } - - pub fn exists(&mut self) -> Result { - self.path.check_error()?; - self.path.exists() - } - - pub fn is_dir(&mut self) -> Result { - self.path.check_error()?; - Ok(false) - } - - pub fn is_file(&mut self) -> Result { - self.path.check_error()?; - Ok(true) - } -} -#[derive(Debug)] -pub struct PathReal<'base, 'path> { - base: &'base Path, - path: &'path Path, - error: Option, -} -impl<'base, 'path> PathReal<'base, 'path> { - fn full_path(&self) -> PathBuf { - self.base.join(self.path) - } - fn put(&mut self, error: super::Error) { - if self.error.is_none() { - self.error.replace(error); - } - } - fn validate(base: &Path, path: &Path) -> Option { - match PathReal::clean_path(path) { - Err(error) => Some(error), - Ok(path) => { - if !path.starts_with(base) { - return Some(super::Error::PathTraversal { - base: base.to_path_buf(), - path, - }); - } - None - } - } - } - - fn clean_path(path: &Path) -> Result { - // 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) - } - - fn new(base: &'base Path, path: &'path Path) -> Self { - Self { - base, - path, - error: PathReal::validate(base, path), - } - } - - pub fn exists(&mut self) -> Result { - self.check_error()?; - Ok(self.full_path().exists()) - } - - fn check_error(&mut self) -> Result<()> { - if let Some(error) = self.error.take() { - return Err(error); - } - Ok(()) - } - - pub fn is_dir(&mut self) -> Result { - self.check_error()?; - Ok(self.full_path().is_dir()) - } - - pub fn is_file(&mut self) -> Result { - self.check_error()?; - Ok(self.full_path().is_file()) - } -} -impl From> for PathBuf { - fn from(path: PathReal) -> Self { - path.base.join(path.path) - } -} -pub struct ReaderReal { - contents: String, -} -impl ReaderReal { - fn new(path: &Path) -> Result { - let contents = std::fs::read_to_string(path)?; - Ok(Self { contents }) - } - - pub fn as_str(&self) -> &str { - &self.contents - } -} -impl Display for ReaderReal { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.contents) - } -} diff --git a/src/fs/real/dir.rs b/src/fs/real/dir.rs new file mode 100644 index 0000000..4a1349c --- /dev/null +++ b/src/fs/real/dir.rs @@ -0,0 +1,60 @@ +// +use std::path::Path; + +use crate::fs::{DirItem, DirItemIterator, Error, Result}; + +use super::path::PathReal; + +pub struct DirReal<'base, 'path> { + path: PathReal<'base, 'path>, +} +impl<'base, 'path> DirReal<'base, 'path> { + pub(super) fn new(base: &'base Path, path: &'path Path) -> Self { + let mut path = PathReal::new(base, path); + if path.error.is_none() { + if let Ok(exists) = path.exists() { + if exists { + if let Ok(is_dir) = path.is_dir() { + if !is_dir { + path.put(Error::NotADirectory { + path: path.full_path(), + }) + } + } + } + } + } + Self { path } + } + + pub fn create(&mut self) -> Result<()> { + self.path.check_error()?; + std::fs::create_dir(self.path.full_path()).map_err(Into::into) + } + + pub fn create_all(&mut self) -> Result<()> { + self.path.check_error()?; + std::fs::create_dir_all(self.path.full_path()).map_err(Into::into) + } + + pub fn read(&mut self) -> Result>>> { + self.path.check_error()?; + let read_dir = std::fs::read_dir(self.path.full_path())?; + Ok(Box::new(DirItemIterator::new(read_dir))) + } + + pub fn exists(&mut self) -> Result { + self.path.check_error()?; + self.path.exists() + } + + pub fn is_dir(&mut self) -> Result { + self.path.check_error()?; + Ok(true) + } + + pub fn is_file(&mut self) -> Result { + self.path.check_error()?; + Ok(false) + } +} diff --git a/src/fs/real/file.rs b/src/fs/real/file.rs new file mode 100644 index 0000000..4aef521 --- /dev/null +++ b/src/fs/real/file.rs @@ -0,0 +1,42 @@ +// +use std::path::Path; + +use crate::fs::Result; + +use super::{path::PathReal, reader::ReaderReal}; + +pub struct FileReal<'base, 'path> { + path: PathReal<'base, 'path>, +} +impl<'base, 'path> FileReal<'base, 'path> { + pub(super) fn new(base: &'base Path, path: &'path Path) -> Self { + Self { + path: PathReal::new(base, path), + } + } + + pub fn reader(&mut self) -> Result { + self.path.check_error()?; + ReaderReal::new(&self.path.full_path()) + } + + pub fn write(&mut self, contents: &str) -> Result<()> { + self.path.check_error()?; + std::fs::write(self.path.full_path(), contents).map_err(Into::into) + } + + pub fn exists(&mut self) -> Result { + self.path.check_error()?; + self.path.exists() + } + + pub fn is_dir(&mut self) -> Result { + self.path.check_error()?; + Ok(false) + } + + pub fn is_file(&mut self) -> Result { + self.path.check_error()?; + Ok(true) + } +} diff --git a/src/fs/real/mod.rs b/src/fs/real/mod.rs new file mode 100644 index 0000000..10408cc --- /dev/null +++ b/src/fs/real/mod.rs @@ -0,0 +1,8 @@ +// +mod dir; +mod file; +mod path; +mod reader; +mod system; + +pub use system::FileSystem; diff --git a/src/fs/real/path.rs b/src/fs/real/path.rs new file mode 100644 index 0000000..94e0438 --- /dev/null +++ b/src/fs/real/path.rs @@ -0,0 +1,82 @@ +// +use std::path::{Path, PathBuf}; + +use crate::fs::{Error, Result}; + +#[derive(Debug)] +pub struct PathReal<'base, 'path> { + base: &'base Path, + path: &'path Path, + pub(super) error: Option, +} +impl<'base, 'path> PathReal<'base, 'path> { + pub(super) fn full_path(&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 { + match PathReal::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 { + // 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 new(base: &'base Path, path: &'path Path) -> Self { + Self { + base, + path, + error: PathReal::validate(base, path), + } + } + + pub fn exists(&mut self) -> Result { + self.check_error()?; + Ok(self.full_path().exists()) + } + + pub(super) fn check_error(&mut self) -> Result<()> { + if let Some(error) = self.error.take() { + return Err(error); + } + Ok(()) + } + + pub fn is_dir(&mut self) -> Result { + self.check_error()?; + Ok(self.full_path().is_dir()) + } + + pub fn is_file(&mut self) -> Result { + self.check_error()?; + Ok(self.full_path().is_file()) + } +} +impl From> for PathBuf { + fn from(path: PathReal) -> Self { + path.base.join(path.path) + } +} diff --git a/src/fs/real/reader.rs b/src/fs/real/reader.rs new file mode 100644 index 0000000..a524edd --- /dev/null +++ b/src/fs/real/reader.rs @@ -0,0 +1,23 @@ +// +use std::{fmt::Display, path::Path}; + +use crate::fs::Result; + +pub struct ReaderReal { + contents: String, +} +impl ReaderReal { + pub(super) fn new(path: &Path) -> Result { + let contents = std::fs::read_to_string(path)?; + Ok(Self { contents }) + } + + pub fn as_str(&self) -> &str { + &self.contents + } +} +impl Display for ReaderReal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.contents) + } +} diff --git a/src/fs/real/system.rs b/src/fs/real/system.rs new file mode 100644 index 0000000..829f1e7 --- /dev/null +++ b/src/fs/real/system.rs @@ -0,0 +1,60 @@ +// +use std::path::{Path, PathBuf}; + +use crate::fs::{Error, Result}; + +use super::{dir::DirReal, file::FileReal, path::PathReal}; + +#[derive(Clone, Debug)] +pub struct FileSystem { + base: PathBuf, +} +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 { + let path_of = self.base.as_path().join(path); + self.validate(&path_of)?; + Ok(path_of) + } + + pub fn dir<'base, 'path>(&'base self, path: &'path Path) -> DirReal<'base, 'path> { + DirReal::new(self.base(), path) + } + + pub fn file<'base, 'path>(&'base self, path: &'path Path) -> FileReal<'base, 'path> { + FileReal::new(self.base(), path) + } + + pub fn path<'base, 'path>(&'base self, path: &'path Path) -> PathReal<'base, 'path> { + 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 { + // 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()?.join(path) + } + .clean(); + Ok(abs_path) + } +}