diff --git a/README.md b/README.md index 8c95a80..7d036c4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Provides injectable Filesystem and Network resources to make code more testable. - [ ] `std::fs::read_link` - `link(path).read()` - Reads a symbolic link, returning the file that the link points to. - [x] `std::fs::read_to_string` - `file(path).reader().to_string()` - Read the entire contents of a file into a string. - [x] `std::fs::remove_dir` - `dir(path).remove()` - Removes an empty directory. -- [ ] `std::fs::remove_dir_all` - `dir(path).remove_all()` - Removes a directory at this path, after removing all its contents. Use carefully! +- [x] `std::fs::remove_dir_all` - `dir(path).remove_all()` - Removes a directory at this path, after removing all its contents. Use carefully! - [ ] `std::fs::remove_file` - `file(path).remove()` - Removes a file from the filesystem. - [ ] `std::fs::rename` - `path(path).rename()` - Rename a file or directory to a new name, replacing the original file if to already exists. - [ ] `std::fs::set_permissions` - `path(path).set_permissions()` - Changes the permissions found on a file or a directory. diff --git a/src/fs/dir.rs b/src/fs/dir.rs index 5c49cab..4e9efc0 100644 --- a/src/fs/dir.rs +++ b/src/fs/dir.rs @@ -75,6 +75,23 @@ impl<'base, 'path> DirHandle<'base, 'path> { self.check_error()?; std::fs::remove_dir(self.as_pathbuf()).map_err(Error::Io) } + + /// Recursively remove a directory and all of its contents. + /// + /// Wrapper for [std::fs::remove_dir_all] + /// + /// ``` + /// # fn try_main() -> kxio::fs::Result<()> { + /// let fs = kxio::fs::temp()?; + /// let path = fs.base().join("foo").join("bar"); + /// let dir = fs.dir(&path); + /// dir.remove_all()?; + /// # Ok(()) + /// } + pub fn remove_all(&self) -> Result<()> { + self.check_error()?; + std::fs::remove_dir_all(self.as_pathbuf()).map_err(Error::Io) + } } impl<'base, 'path> TryFrom> for FileHandle<'base, 'path> { type Error = crate::fs::Error; diff --git a/tests/fs.rs b/tests/fs.rs index 05ee9ea..a18f9e8 100644 --- a/tests/fs.rs +++ b/tests/fs.rs @@ -263,6 +263,55 @@ mod dir_remove { 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).remove() + ); + + Ok(()) + } +} + +mod dir_remove_all { + use super::*; + #[test] + fn should_remove_a_dir() -> TestResult { + let fs = fs::temp().expect("temp fs"); + let path = fs.base().join("foo"); + let dir = fs.dir(&path); + dir.create().expect("create dir"); + let sub_path = path.join("sub"); + let sub = fs.dir(&sub_path); + sub.create().expect("create sub"); + let file = sub_path.join("file"); + fs.file(&file).write("contents").expect("write"); + + dir.remove_all().expect("remove"); + let exists = dir.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.dir(&path).remove_all() + ); + + Ok(()) + } } mod path_exists {