diff --git a/README.md b/README.md index e5dc156..e08cdc8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Provides injectable Filesystem and Network resources to make code more testable. - [x] `std::fs::remove_file` - `file(path).remove()` - Removes a file from the filesystem. - [x] `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. -- [ ] `std::fs::symlink_metadata` - `link(path).metadata()` - Query the metadata about a file without following symlinks. +- [x] `std::fs::symlink_metadata` - `path(path).symlink_metadata()` - Query the metadata about a file without following symlinks. - [x] `std::fs::write` - `file(path).write()` - Write a slice as the entire contents of a file. ### Network diff --git a/src/fs/path.rs b/src/fs/path.rs index 58937ff..d284cc0 100644 --- a/src/fs/path.rs +++ b/src/fs/path.rs @@ -302,6 +302,24 @@ impl<'base, 'path, T: PathType> PathReal<'base, 'path, T> { self.check_error()?; self.as_pathbuf().canonicalize().map_err(Error::Io) } + + /// Returns the metadata for a path without following symlinks. + /// + /// Wrapper for [std::fs::symlink_metadata] + /// + /// ``` + /// # fn try_main() -> kxio::fs::Result<()> { + /// let fs = kxio::fs::temp()?; + /// let path = fs.base().join("foo"); + /// let file = fs.file(&path); + /// let metadata = file.symlink_metadata()?; + /// # Ok(()) + /// # } + /// ``` + pub fn symlink_metadata(&self) -> Result { + self.check_error()?; + std::fs::symlink_metadata(self.as_pathbuf()).map_err(Error::Io) + } } impl<'base, 'path> From> for PathBuf { fn from(path: PathReal) -> Self { diff --git a/tests/fs.rs b/tests/fs.rs index cb2899d..c47b662 100644 --- a/tests/fs.rs +++ b/tests/fs.rs @@ -418,6 +418,61 @@ mod file { Ok(()) } } + mod symlink_metadata { + use super::*; + + #[test] + fn should_return_metadata_for_a_file() -> TestResult { + let fs = fs::temp().expect("temp fs"); + let path = fs.base().join("foo"); + let file = fs.file(&path); + file.write("bar").expect("write"); + let md = file.symlink_metadata().expect("metadata"); + assert!(md.is_file()); + Ok(()) + } + + #[test] + fn should_return_metadata_for_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"); + let md = dir.symlink_metadata().expect("metadata"); + assert!(md.is_dir()); + Ok(()) + } + + #[test] + fn should_return_metadata_for_a_symlink() -> TestResult { + let fs = fs::temp().expect("temp fs"); + let file_path = fs.base().join("foo"); + let file = fs.file(&file_path); + file.write("bar").expect("write"); + + let link_path = fs.base().join("bar"); + let link = fs.path(&link_path); + file.soft_link(&link).expect("soft_link"); + + let md = link.symlink_metadata().expect("metadata"); + assert!(md.is_symlink()); + 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).symlink_metadata() + ); + + Ok(()) + } + } mod reader { use super::*; mod to_string {