refactor: split real module into sub-modules
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 2m9s
Rust / build (map[name:stable]) (push) Successful in 4m7s
Release Please / Release-plz (push) Successful in 38s

This commit is contained in:
Paul Campbell 2024-11-01 19:08:56 +00:00
parent 791fa74e78
commit d50164931d
8 changed files with 277 additions and 247 deletions

View file

@ -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"

View file

@ -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<PathBuf> {
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<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()?.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<Box<dyn Iterator<Item = Result<DirItem>>>> {
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<bool> {
self.path.check_error()?;
self.path.exists()
}
pub fn is_dir(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(true)
}
pub fn is_file(&mut self) -> Result<bool> {
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<ReaderReal> {
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<bool> {
self.path.check_error()?;
self.path.exists()
}
pub fn is_dir(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(false)
}
pub fn is_file(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(true)
}
}
#[derive(Debug)]
pub struct PathReal<'base, 'path> {
base: &'base Path,
path: &'path Path,
error: Option<super::Error>,
}
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<super::Error> {
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<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)
}
fn new(base: &'base Path, path: &'path Path) -> Self {
Self {
base,
path,
error: PathReal::validate(base, path),
}
}
pub fn exists(&mut self) -> Result<bool> {
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<bool> {
self.check_error()?;
Ok(self.full_path().is_dir())
}
pub fn is_file(&mut self) -> Result<bool> {
self.check_error()?;
Ok(self.full_path().is_file())
}
}
impl From<PathReal<'_, '_>> for PathBuf {
fn from(path: PathReal) -> Self {
path.base.join(path.path)
}
}
pub struct ReaderReal {
contents: String,
}
impl ReaderReal {
fn new(path: &Path) -> Result<Self> {
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)
}
}

60
src/fs/real/dir.rs Normal file
View file

@ -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<Box<dyn Iterator<Item = Result<DirItem>>>> {
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<bool> {
self.path.check_error()?;
self.path.exists()
}
pub fn is_dir(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(true)
}
pub fn is_file(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(false)
}
}

42
src/fs/real/file.rs Normal file
View file

@ -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<ReaderReal> {
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<bool> {
self.path.check_error()?;
self.path.exists()
}
pub fn is_dir(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(false)
}
pub fn is_file(&mut self) -> Result<bool> {
self.path.check_error()?;
Ok(true)
}
}

8
src/fs/real/mod.rs Normal file
View file

@ -0,0 +1,8 @@
//
mod dir;
mod file;
mod path;
mod reader;
mod system;
pub use system::FileSystem;

82
src/fs/real/path.rs Normal file
View file

@ -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<Error>,
}
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<Error> {
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<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 new(base: &'base Path, path: &'path Path) -> Self {
Self {
base,
path,
error: PathReal::validate(base, path),
}
}
pub fn exists(&mut self) -> Result<bool> {
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<bool> {
self.check_error()?;
Ok(self.full_path().is_dir())
}
pub fn is_file(&mut self) -> Result<bool> {
self.check_error()?;
Ok(self.full_path().is_file())
}
}
impl From<PathReal<'_, '_>> for PathBuf {
fn from(path: PathReal) -> Self {
path.base.join(path.path)
}
}

23
src/fs/real/reader.rs Normal file
View file

@ -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<Self> {
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)
}
}

60
src/fs/real/system.rs Normal file
View file

@ -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<PathBuf> {
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<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()?.join(path)
}
.clean();
Ok(abs_path)
}
}