Compare commits
4 commits
4f45383ff7
...
db28ab982a
Author | SHA1 | Date | |
---|---|---|---|
db28ab982a | |||
623616e73c | |||
09e1d91a9e | |||
d50164931d |
11 changed files with 384 additions and 562 deletions
3
justfile
3
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"
|
||||
|
|
246
src/fs/real.rs
246
src/fs/real.rs
|
@ -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)
|
||||
}
|
||||
}
|
64
src/fs/real/dir.rs
Normal file
64
src/fs/real/dir.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
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 path(&self) -> PathBuf {
|
||||
self.path.full_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)
|
||||
}
|
||||
}
|
46
src/fs/real/file.rs
Normal file
46
src/fs/real/file.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
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 path(&self) -> PathBuf {
|
||||
self.path.full_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
8
src/fs/real/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
mod dir;
|
||||
mod file;
|
||||
mod path;
|
||||
mod reader;
|
||||
mod system;
|
||||
|
||||
pub use system::FileSystem;
|
102
src/fs/real/path.rs
Normal file
102
src/fs/real/path.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::fs::{Error, Result};
|
||||
|
||||
use super::{dir::DirReal, file::FileReal};
|
||||
|
||||
#[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())
|
||||
}
|
||||
|
||||
pub fn as_dir(&mut self) -> Result<Option<DirReal<'base, 'path>>> {
|
||||
self.check_error()?;
|
||||
if self.full_path().is_dir() {
|
||||
Ok(Some(DirReal::new(self.base, self.path)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_file(&mut self) -> Result<Option<FileReal<'base, 'path>>> {
|
||||
self.check_error()?;
|
||||
if self.full_path().is_file() {
|
||||
Ok(Some(FileReal::new(self.base, self.path)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<PathReal<'_, '_>> for PathBuf {
|
||||
fn from(path: PathReal) -> Self {
|
||||
path.base.join(path.path)
|
||||
}
|
||||
}
|
27
src/fs/real/reader.rs
Normal file
27
src/fs/real/reader.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
use std::{fmt::Display, path::Path, str::Lines};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn lines(&self) -> Lines<'_> {
|
||||
self.contents.lines()
|
||||
}
|
||||
}
|
||||
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
60
src/fs/real/system.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
//
|
||||
pub mod fs;
|
||||
pub mod network;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
312
src/tests/fs.rs
312
src/tests/fs.rs
|
@ -1,312 +0,0 @@
|
|||
use assert2::let_assert;
|
||||
|
||||
use crate::fs;
|
||||
|
||||
type TestResult = Result<(), fs::Error>;
|
||||
|
||||
mod path_of {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn validate_fails_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let_assert!(Err(fs::Error::PathTraversal { base, path: _path }) = fs.path_of("..".into()));
|
||||
assert_eq!(base, fs.base());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_joins() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let joined = fs.base().join("foo").join("bar");
|
||||
let path_of = fs
|
||||
.path_of("foo/bar".into())
|
||||
.expect("parse foo/bar into path");
|
||||
|
||||
assert_eq!(joined, path_of);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod file {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
/// Write to a file, read it, verify it exists, is a file and has the expected contents
|
||||
fn write_read_file_exists() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let pathbuf = fs.base().join("foo");
|
||||
|
||||
let mut file = fs.file(&pathbuf);
|
||||
file.write("content").expect("write");
|
||||
let c = file.reader().expect("reader").to_string();
|
||||
assert_eq!(c, "content");
|
||||
|
||||
let mut path = fs.path(&pathbuf);
|
||||
let exists = path.exists().expect("exists");
|
||||
assert!(exists);
|
||||
|
||||
let is_file = path.is_file().expect("is_file");
|
||||
assert!(is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod dir_create {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_create_a_dir() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let pathbuf = fs.base().join("subdir");
|
||||
|
||||
fs.dir(&pathbuf).create().expect("create");
|
||||
|
||||
let exists = fs.path(&pathbuf).exists().expect("exitss");
|
||||
assert!(exists);
|
||||
|
||||
let is_dir = fs.path(&pathbuf).is_dir().expect("is dir");
|
||||
assert!(is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.dir(&path).create()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod dir_create_all {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_create_a_dir() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let pathbuf = fs.base().join("subdir").join("child");
|
||||
|
||||
fs.dir(&pathbuf).create_all().expect("create_all");
|
||||
|
||||
let mut path = fs.path(&pathbuf);
|
||||
let exists = path.exists().expect("exists");
|
||||
assert!(exists, "path exists");
|
||||
|
||||
let is_dir = path.is_dir().expect("is_dir");
|
||||
assert!(is_dir, "path is a directory");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.dir(&path).create_all()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod dir_dir_read {
|
||||
use crate::fs::DirItem;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_return_dir_items() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let file1 = fs.base().join("file-1");
|
||||
let dir = fs.base().join("dir");
|
||||
let file2 = dir.join("file-2");
|
||||
fs.file(&file1).write("file-1").expect("write: file-1");
|
||||
fs.dir(&dir).create().expect("create dir");
|
||||
fs.file(&file2).write("file-2").expect("write: file-2");
|
||||
|
||||
let items = fs
|
||||
.dir(fs.base())
|
||||
.read()
|
||||
.expect("dir.read")
|
||||
.filter_map(|i| i.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(items.len(), 2);
|
||||
assert!(items.contains(&DirItem::File(file1)));
|
||||
assert!(items.contains(&DirItem::Dir(dir)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_not_a_dir() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("file");
|
||||
fs.file(&path).write("contents").expect("write");
|
||||
|
||||
let_assert!(Err(fs::Error::NotADirectory { path: _path }) = fs.dir(&path).read());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.dir(&path).read()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod path_exists {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_be_true_when_it_exists() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
fs.file(&path).write("bar").expect("write");
|
||||
let exists = fs.path(&path).exists().expect("exists");
|
||||
assert!(exists);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_be_false_when_it_does_not_exist() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
let exists = fs.path(&path).exists().expect("exists");
|
||||
assert!(!exists);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.path(&path).exists()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod path_is_dir {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_be_true_when_is_a_dir() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
fs.dir(&path).create().expect("create");
|
||||
let is_dir = fs.path(&path).is_dir().expect("is_dir");
|
||||
assert!(is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_be_false_when_is_a_file() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
fs.file(&path).write("bar").expect("write");
|
||||
let is_dir = fs.path(&path).is_dir().expect("is_dir");
|
||||
assert!(!is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn should_be_false_when_is_a_link() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
// TODO: (#38) create a link
|
||||
// let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
let is_dir = fs.path(&path).is_dir().expect("is_dir");
|
||||
assert!(!is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.dir(&path).is_dir()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod path_is_file {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_be_true_when_is_a_file() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
fs.file(&path).write("bar").expect("write");
|
||||
|
||||
let is_file = fs.path(&path).is_file().expect("is_file");
|
||||
assert!(is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_be_false_when_is_a_dir() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
fs.dir(&path).create().expect("create");
|
||||
let is_file = fs.path(&path).is_file().expect("is_file");
|
||||
assert!(!is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn should_be_false_when_is_a_link() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("foo");
|
||||
// TODO: (#38) create a link
|
||||
// let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
let is_file = fs.path(&path).is_file().expect("is_file");
|
||||
assert!(!is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.file(&path).is_file()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
75
tests/fs.rs
75
tests/fs.rs
|
@ -32,6 +32,65 @@ mod path_of {
|
|||
}
|
||||
}
|
||||
|
||||
mod path {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn path_is_dir_as_dir_some() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let path = fs.base().join("foo");
|
||||
let mut dir = fs.dir(&path);
|
||||
dir.create().expect("create");
|
||||
|
||||
let_assert!(Ok(Some(as_dir)) = fs.path(&path).as_dir());
|
||||
|
||||
assert_eq!(dir.path(), as_dir.path());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_is_file_as_dir_none() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let path = fs.base().join("foo");
|
||||
let mut file = fs.file(&path);
|
||||
file.write("contents").expect("create");
|
||||
|
||||
let_assert!(Ok(Some(mut as_file)) = fs.path(&path).as_file());
|
||||
|
||||
assert_eq!(file.path(), as_file.path());
|
||||
assert_eq!(as_file.reader().expect("reader").to_string(), "contents");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn path_is_dir_as_file_none() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let path = fs.base().join("foo");
|
||||
let mut dir = fs.dir(&path);
|
||||
dir.create().expect("create");
|
||||
|
||||
let_assert!(Ok(None) = fs.path(&path).as_file());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn path_is_file_as_file_some() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let path = fs.base().join("foo");
|
||||
let mut file = fs.file(&path);
|
||||
file.write("contents").expect("create");
|
||||
|
||||
let_assert!(Ok(None) = fs.path(&path).as_dir());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod file {
|
||||
use super::*;
|
||||
|
||||
|
@ -55,6 +114,22 @@ mod file {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_file_lines() -> TestResult {
|
||||
let fs = fs::temp().expect("temp fs");
|
||||
|
||||
let path = fs.base().join("foo");
|
||||
let mut file = fs.file(&path);
|
||||
file.write("line 1\nline 2").expect("write");
|
||||
|
||||
let reader = file.reader().expect("reader");
|
||||
let lines = reader.lines().collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(lines, vec!["line 1", "line 2"]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod dir_create {
|
||||
|
|
Loading…
Reference in a new issue