feat(fs)!: new fluent API
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 2m5s
Rust / build (map[name:stable]) (push) Successful in 4m31s
Release Please / Release-plz (push) Successful in 1m23s

This commit is contained in:
Paul Campbell 2024-10-30 08:52:42 +00:00
parent 8c76ce49e0
commit a629950d4d
5 changed files with 286 additions and 169 deletions

View file

@ -1,23 +0,0 @@
use crate::fs::DirItem;
use super::Result;
use std::path::{Path, PathBuf};
pub trait FileSystemLike {
fn base(&self) -> &Path;
fn dir_create(&self, path: &Path) -> Result<()>;
fn dir_create_all(&self, path: &Path) -> Result<()>;
/// Reads the items in a directory and returns them as an iterator.
fn dir_read(&self, path: &Path) -> Result<Box<dyn Iterator<Item = Result<DirItem>>>>;
fn file_read_to_string(&self, path: &Path) -> Result<String>;
fn file_write(&self, path: &Path, contents: &str) -> Result<()>;
fn path_exists(&self, path: &Path) -> Result<bool>;
fn path_is_dir(&self, path: &Path) -> Result<bool>;
fn path_is_file(&self, path: &Path) -> Result<bool>;
fn path_of(&self, path: PathBuf) -> Result<PathBuf>;
}

View file

@ -2,15 +2,14 @@ use std::path::PathBuf;
use derive_more::From; use derive_more::From;
use crate::fs::like::FileSystemLike;
mod like;
mod real; mod real;
mod temp; mod temp;
mod dir_item; mod dir_item;
pub use dir_item::DirItem; pub use dir_item::DirItem;
pub use dir_item::DirItemIterator; pub use dir_item::DirItemIterator;
use real::FileSystem;
use temp::TempFileSystem;
#[derive(Debug, From, derive_more::Display)] #[derive(Debug, From, derive_more::Display)]
pub enum Error { pub enum Error {
@ -32,25 +31,9 @@ impl std::error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
pub const fn new(base: PathBuf) -> FileSystem { pub const fn new(base: PathBuf) -> FileSystem {
FileSystem::Real(real::new(base)) real::new(base)
} }
pub fn temp() -> Result<FileSystem> { pub fn temp() -> Result<TempFileSystem> {
temp::new().map(FileSystem::Temp) temp::new()
}
#[derive(Clone, Debug)]
pub enum FileSystem {
Real(real::RealFileSystem),
Temp(temp::TempFileSystem),
}
impl std::ops::Deref for FileSystem {
type Target = dyn FileSystemLike;
fn deref(&self) -> &Self::Target {
match self {
Self::Real(fs) => fs,
Self::Temp(fs) => fs.deref(),
}
}
} }

View file

@ -1,79 +1,44 @@
use std::path::{Path, PathBuf}; use std::{
fmt::Display,
path::{Path, PathBuf},
};
use crate::fs::{DirItem, DirItemIterator}; use crate::fs::DirItem;
pub const fn new(base: PathBuf) -> RealFileSystem { use super::{DirItemIterator, Result};
RealFileSystem { base }
pub const fn new(base: PathBuf) -> FileSystem {
FileSystem { base }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RealFileSystem { pub struct FileSystem {
base: PathBuf, base: PathBuf,
} }
impl FileSystem {
impl super::FileSystemLike for RealFileSystem { pub fn base(&self) -> &Path {
fn base(&self) -> &Path {
&self.base &self.base
} }
fn dir_create(&self, path: &Path) -> super::Result<()> { pub fn path_of(&self, path: PathBuf) -> Result<PathBuf> {
self.validate(path)?;
std::fs::create_dir(path).map_err(Into::into)
}
fn dir_create_all(&self, path: &Path) -> super::Result<()> {
self.validate(path)?;
std::fs::create_dir_all(path).map_err(Into::into)
}
fn dir_read(
&self,
path: &Path,
) -> super::Result<Box<dyn Iterator<Item = super::Result<DirItem>>>> {
self.validate(path)?;
if !self.path_is_dir(path)? {
return Err(super::Error::NotADirectory {
path: path.to_path_buf(),
});
}
let read_dir = std::fs::read_dir(path)?;
Ok(Box::new(DirItemIterator::new(read_dir)))
}
fn file_read_to_string(&self, path: &Path) -> super::Result<String> {
self.validate(path)?;
std::fs::read_to_string(path).map_err(Into::into)
}
fn file_write(&self, path: &Path, contents: &str) -> super::Result<()> {
self.validate(path)?;
std::fs::write(path, contents).map_err(Into::into)
}
fn path_exists(&self, path: &Path) -> super::Result<bool> {
self.validate(path)?;
Ok(path.exists())
}
fn path_is_dir(&self, path: &Path) -> super::Result<bool> {
self.validate(path)?;
Ok(path.is_dir())
}
fn path_is_file(&self, path: &Path) -> super::Result<bool> {
self.validate(path)?;
Ok(path.is_file())
}
fn path_of(&self, path: PathBuf) -> super::Result<PathBuf> {
let path_of = self.base.as_path().join(path); let path_of = self.base.as_path().join(path);
self.validate(&path_of)?; self.validate(&path_of)?;
Ok(path_of) Ok(path_of)
} }
pub fn dir<'base, 'path>(&'base self, path: &'path Path) -> DirReal<'base, 'path> {
DirReal::new(self.base(), path)
} }
impl RealFileSystem { pub fn file<'base, 'path>(&'base self, path: &'path Path) -> FileReal<'base, 'path> {
fn validate(&self, path: &Path) -> super::Result<()> { 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)?; let path = self.clean_path(path)?;
if !path.starts_with(&self.base) { if !path.starts_with(&self.base) {
return Err(super::Error::PathTraversal { return Err(super::Error::PathTraversal {
@ -84,7 +49,7 @@ impl RealFileSystem {
Ok(()) Ok(())
} }
fn clean_path(&self, path: &Path) -> super::Result<PathBuf> { fn clean_path(&self, path: &Path) -> Result<PathBuf> {
// let path = path.as_ref(); // let path = path.as_ref();
use path_clean::PathClean; use path_clean::PathClean;
let abs_path = if path.is_absolute() { let abs_path = if path.is_absolute() {
@ -96,3 +61,187 @@ impl RealFileSystem {
Ok(abs_path) 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)
}
}

View file

@ -2,6 +2,8 @@ use std::sync::{Arc, Mutex};
use tempfile::TempDir; use tempfile::TempDir;
use super::real::FileSystem;
pub(super) fn new() -> super::Result<TempFileSystem> { pub(super) fn new() -> super::Result<TempFileSystem> {
let temp_dir = tempfile::tempdir()?; let temp_dir = tempfile::tempdir()?;
let base = temp_dir.path().to_path_buf(); let base = temp_dir.path().to_path_buf();
@ -16,12 +18,12 @@ pub(super) fn new() -> super::Result<TempFileSystem> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TempFileSystem { pub struct TempFileSystem {
real: super::real::RealFileSystem, real: FileSystem,
_temp_dir: Arc<Mutex<TempDir>>, _temp_dir: Arc<Mutex<TempDir>>,
} }
impl std::ops::Deref for TempFileSystem { impl std::ops::Deref for TempFileSystem {
type Target = dyn super::FileSystemLike; type Target = FileSystem;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.real &self.real

View file

@ -6,9 +6,10 @@ type TestResult = Result<(), fs::Error>;
mod path_of { mod path_of {
use super::*; use super::*;
#[test] #[test]
fn validate_fails_on_path_traversal() -> TestResult { fn validate_fails_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let_assert!(Err(fs::Error::PathTraversal { base, path: _path }) = fs.path_of("..".into())); let_assert!(Err(fs::Error::PathTraversal { base, path: _path }) = fs.path_of("..".into()));
assert_eq!(base, fs.base()); assert_eq!(base, fs.base());
@ -18,10 +19,12 @@ mod path_of {
#[test] #[test]
fn matches_joins() -> TestResult { fn matches_joins() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let joined = fs.base().join("foo").join("bar"); let joined = fs.base().join("foo").join("bar");
let path_of = fs.path_of("foo/bar".into())?; let path_of = fs
.path_of("foo/bar".into())
.expect("parse foo/bar into path");
assert_eq!(joined, path_of); assert_eq!(joined, path_of);
@ -31,23 +34,23 @@ mod path_of {
mod file { mod file {
use super::*; use super::*;
#[test] #[test]
/// Write to a file, read it, verify it exists, is a file and has the expected contents /// Write to a file, read it, verify it exists, is a file and has the expected contents
fn write_read_file_exists() -> TestResult { fn write_read_file_exists() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let pathbuf = fs.base().join("foo"); let pathbuf = fs.base().join("foo");
let_assert!(Ok(_) = fs.file_write(&pathbuf, "content")); let mut file = fs.file(&pathbuf);
let_assert!( file.write("content").expect("write");
Ok(c) = fs.file_read_to_string(&pathbuf), let c = file.reader().expect("reader").to_string();
"file_read_to_string"
);
assert_eq!(c, "content"); assert_eq!(c, "content");
let_assert!(Ok(exists) = fs.path_exists(&pathbuf)); let mut path = fs.path(&pathbuf);
let exists = path.exists().expect("exists");
assert!(exists); assert!(exists);
let_assert!(Ok(is_file) = fs.path_is_file(&pathbuf)); let is_file = path.is_file().expect("is_file");
assert!(is_file); assert!(is_file);
Ok(()) Ok(())
@ -58,28 +61,28 @@ mod dir_create {
use super::*; use super::*;
#[test] #[test]
fn should_create_a_dir() -> TestResult { fn should_create_a_dir() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let pathbuf = fs.base().join("subdir"); let pathbuf = fs.base().join("subdir");
let_assert!(Ok(_) = fs.dir_create(&pathbuf)); fs.dir(&pathbuf).create().expect("create");
let_assert!(Ok(exists) = fs.path_exists(&pathbuf)); let exists = fs.path(&pathbuf).exists().expect("exitss");
assert!(exists); assert!(exists);
let_assert!(Ok(is_dir) = fs.path_is_dir(&pathbuf)); let is_dir = fs.path(&pathbuf).is_dir().expect("is dir");
assert!(is_dir); assert!(is_dir);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_fail_on_path_traversal() -> TestResult { fn should_fail_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("..").join("foo"); let path = fs.base().join("..").join("foo");
let_assert!( let_assert!(
Err(fs::Error::PathTraversal { Err(fs::Error::PathTraversal {
base: _base, base: _base,
path: _path path: _path
}) = fs.dir_create(&path) }) = fs.dir(&path).create()
); );
Ok(()) Ok(())
@ -91,28 +94,29 @@ mod dir_create_all {
#[test] #[test]
fn should_create_a_dir() -> TestResult { fn should_create_a_dir() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let pathbuf = fs.base().join("subdir").join("child"); let pathbuf = fs.base().join("subdir").join("child");
let_assert!(Ok(_) = fs.dir_create_all(&pathbuf)); fs.dir(&pathbuf).create_all().expect("create_all");
let_assert!(Ok(exists) = fs.path_exists(&pathbuf)); let mut path = fs.path(&pathbuf);
let exists = path.exists().expect("exists");
assert!(exists, "path exists"); assert!(exists, "path exists");
let_assert!(Ok(is_dir) = fs.path_is_dir(&pathbuf)); let is_dir = path.is_dir().expect("is_dir");
assert!(is_dir, "path is a directory"); assert!(is_dir, "path is a directory");
Ok(()) Ok(())
} }
#[test] #[test]
fn should_fail_on_path_traversal() -> TestResult { fn should_fail_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("..").join("foo"); let path = fs.base().join("..").join("foo");
let_assert!( let_assert!(
Err(fs::Error::PathTraversal { Err(fs::Error::PathTraversal {
base: _base, base: _base,
path: _path path: _path
}) = fs.dir_create_all(&path) }) = fs.dir(&path).create_all()
); );
Ok(()) Ok(())
@ -126,16 +130,18 @@ mod dir_dir_read {
#[test] #[test]
fn should_return_dir_items() -> TestResult { fn should_return_dir_items() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let file1 = fs.base().join("file-1"); let file1 = fs.base().join("file-1");
let dir = fs.base().join("dir"); let dir = fs.base().join("dir");
let file2 = dir.join("file-2"); let file2 = dir.join("file-2");
fs.file_write(&file1, "file-1")?; fs.file(&file1).write("file-1").expect("write: file-1");
fs.dir_create(&dir)?; fs.dir(&dir).create().expect("create dir");
fs.file_write(&file2, "file-2")?; fs.file(&file2).write("file-2").expect("write: file-2");
let items = fs let items = fs
.dir_read(fs.base())? .dir(fs.base())
.read()
.expect("dir.read")
.filter_map(|i| i.ok()) .filter_map(|i| i.ok())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -147,23 +153,23 @@ mod dir_dir_read {
} }
#[test] #[test]
fn should_fail_on_not_a_dir() -> TestResult { fn should_fail_on_not_a_dir() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("file"); let path = fs.base().join("file");
fs.file_write(&path, "contents")?; fs.file(&path).write("contents").expect("write");
let_assert!(Err(fs::Error::NotADirectory { path: _path }) = fs.dir_read(&path)); let_assert!(Err(fs::Error::NotADirectory { path: _path }) = fs.dir(&path).read());
Ok(()) Ok(())
} }
#[test] #[test]
fn should_fail_on_path_traversal() -> TestResult { fn should_fail_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("..").join("foo"); let path = fs.base().join("..").join("foo");
let_assert!( let_assert!(
Err(fs::Error::PathTraversal { Err(fs::Error::PathTraversal {
base: _base, base: _base,
path: _path path: _path
}) = fs.dir_read(&path) }) = fs.dir(&path).read()
); );
Ok(()) Ok(())
@ -174,32 +180,32 @@ mod path_exists {
use super::*; use super::*;
#[test] #[test]
fn should_be_true_when_it_exists() -> TestResult { fn should_be_true_when_it_exists() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
let_assert!(Ok(_) = fs.file_write(&path, "bar")); fs.file(&path).write("bar").expect("write");
let_assert!(Ok(exists) = fs.path_exists(&path)); let exists = fs.path(&path).exists().expect("exists");
assert!(exists); assert!(exists);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_be_false_when_it_does_not_exist() -> TestResult { fn should_be_false_when_it_does_not_exist() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
let_assert!(Ok(exists) = fs.path_exists(&path)); let exists = fs.path(&path).exists().expect("exists");
assert!(!exists); assert!(!exists);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_fail_on_path_traversal() -> TestResult { fn should_fail_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("..").join("foo"); let path = fs.base().join("..").join("foo");
let_assert!( let_assert!(
Err(fs::Error::PathTraversal { Err(fs::Error::PathTraversal {
base: _base, base: _base,
path: _path path: _path
}) = fs.path_exists(&path) }) = fs.path(&path).exists()
); );
Ok(()) Ok(())
@ -210,20 +216,20 @@ mod path_is_dir {
use super::*; use super::*;
#[test] #[test]
fn should_be_true_when_is_a_dir() -> TestResult { fn should_be_true_when_is_a_dir() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
let_assert!(Ok(_) = fs.dir_create(&path)); fs.dir(&path).create().expect("create");
let_assert!(Ok(is_dir) = fs.path_is_dir(&path)); let is_dir = fs.path(&path).is_dir().expect("is_dir");
assert!(is_dir); assert!(is_dir);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_be_false_when_is_a_file() -> TestResult { fn should_be_false_when_is_a_file() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
let_assert!(Ok(_) = fs.file_write(&path, "bar")); fs.file(&path).write("bar").expect("write");
let_assert!(Ok(is_dir) = fs.path_is_dir(&path)); let is_dir = fs.path(&path).is_dir().expect("is_dir");
assert!(!is_dir); assert!(!is_dir);
Ok(()) Ok(())
@ -231,24 +237,24 @@ mod path_is_dir {
#[test] #[test]
#[ignore] #[ignore]
fn should_be_false_when_is_a_link() -> TestResult { fn should_be_false_when_is_a_link() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
// TODO: (#38) create a link // TODO: (#38) create a link
// let_assert!(Ok(_) = fs.file_write(&path, "bar")); // let_assert!(Ok(_) = fs.file_write(&path, "bar"));
let_assert!(Ok(is_dir) = fs.path_is_dir(&path)); let is_dir = fs.path(&path).is_dir().expect("is_dir");
assert!(!is_dir); assert!(!is_dir);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_fail_on_path_traversal() -> TestResult { fn should_fail_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("..").join("foo"); let path = fs.base().join("..").join("foo");
let_assert!( let_assert!(
Err(fs::Error::PathTraversal { Err(fs::Error::PathTraversal {
base: _base, base: _base,
path: _path path: _path
}) = fs.path_is_dir(&path) }) = fs.dir(&path).is_dir()
); );
Ok(()) Ok(())
@ -259,21 +265,21 @@ mod path_is_file {
use super::*; use super::*;
#[test] #[test]
fn should_be_true_when_is_a_file() -> TestResult { fn should_be_true_when_is_a_file() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
let_assert!(Ok(_) = fs.file_write(&path, "bar")); fs.file(&path).write("bar").expect("write");
let_assert!(Ok(is_file) = fs.path_is_file(&path)); let is_file = fs.path(&path).is_file().expect("is_file");
assert!(is_file); assert!(is_file);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_be_false_when_is_a_dir() -> TestResult { fn should_be_false_when_is_a_dir() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
let_assert!(Ok(_) = fs.dir_create(&path)); fs.dir(&path).create().expect("create");
let_assert!(Ok(is_file) = fs.path_is_file(&path)); let is_file = fs.path(&path).is_file().expect("is_file");
assert!(!is_file); assert!(!is_file);
Ok(()) Ok(())
@ -281,24 +287,24 @@ mod path_is_file {
#[test] #[test]
#[ignore] #[ignore]
fn should_be_false_when_is_a_link() -> TestResult { fn should_be_false_when_is_a_link() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("foo"); let path = fs.base().join("foo");
// TODO: (#38) create a link // TODO: (#38) create a link
// let_assert!(Ok(_) = fs.file_write(&path, "bar")); // let_assert!(Ok(_) = fs.file_write(&path, "bar"));
let_assert!(Ok(is_file) = fs.path_is_file(&path)); let is_file = fs.path(&path).is_file().expect("is_file");
assert!(!is_file); assert!(!is_file);
Ok(()) Ok(())
} }
#[test] #[test]
fn should_fail_on_path_traversal() -> TestResult { fn should_fail_on_path_traversal() -> TestResult {
let fs = fs::temp()?; let fs = fs::temp().expect("temp fs");
let path = fs.base().join("..").join("foo"); let path = fs.base().join("..").join("foo");
let_assert!( let_assert!(
Err(fs::Error::PathTraversal { Err(fs::Error::PathTraversal {
base: _base, base: _base,
path: _path path: _path
}) = fs.path_is_file(&path) }) = fs.file(&path).is_file()
); );
Ok(()) Ok(())