feat(fs): add tracing
Some checks are pending
Test / build (map[name:nightly]) (push) Successful in 3m30s
Test / build (map[name:stable]) (push) Successful in 4m8s
Release Please / Release-plz (push) Waiting to run

This commit is contained in:
Paul Campbell 2024-12-04 22:38:44 +00:00
parent c6f167dc16
commit 6e5ea556a9
9 changed files with 127 additions and 20 deletions

View file

@ -27,9 +27,11 @@ reqwest = { version = "0.12", features = [ "json" ] }
url = "2.5"
tempfile = "3.10"
tokio = { version = "1.41", features = [ "sync" ] }
tracing = "0.1"
[dev-dependencies]
assert2 = "0.3"
mutants = "0.0"
pretty_assertions = "1.4"
test-log = "0.2"
tokio = { version = "1.41", features = [

View file

@ -1,6 +1,6 @@
//
use std::fmt::Display;
//
use crate::fs::{DirItem, DirItemIterator, Result};
use super::{DirHandle, Error, PathHandle, PathMarker};
@ -19,8 +19,10 @@ impl DirHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn create(&self) -> Result<()> {
self.check_error()?;
tracing::debug!("std::fs::create_dir");
std::fs::create_dir(self.as_pathbuf()).map_err(Error::Io)
}
@ -37,8 +39,10 @@ impl DirHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn create_all(&self) -> Result<()> {
self.check_error()?;
tracing::debug!("std::fs::create_dir_all");
std::fs::create_dir_all(self.as_pathbuf()).map_err(Error::Io)
}
@ -55,8 +59,10 @@ impl DirHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn read(&self) -> Result<Box<dyn Iterator<Item = Result<DirItem>>>> {
self.check_error()?;
tracing::debug!("std::fs::read_dir");
let read_dir = std::fs::read_dir(self.as_pathbuf()).map_err(Error::Io)?;
Ok(Box::new(DirItemIterator::new(read_dir)))
}
@ -73,8 +79,10 @@ impl DirHandle {
/// dir.remove()?;
/// # Ok(())
/// }
#[tracing::instrument]
pub fn remove(&self) -> Result<()> {
self.check_error()?;
tracing::debug!("std::fs::remove");
std::fs::remove_dir(self.as_pathbuf()).map_err(Error::Io)
}
@ -90,8 +98,10 @@ impl DirHandle {
/// dir.remove_all()?;
/// # Ok(())
/// }
#[tracing::instrument]
pub fn remove_all(&self) -> Result<()> {
self.check_error()?;
tracing::debug!("std::fs::remove_all");
std::fs::remove_dir_all(self.as_pathbuf()).map_err(Error::Io)
}
}

View file

@ -28,8 +28,11 @@ impl Iterator for DirItemIterator {
}
fn map_dir_item(item: std::io::Result<DirEntry>) -> super::Result<DirItem> {
tracing::trace!("map_dir_item");
let item = item.map_err(Error::Io)?;
tracing::trace!(?item, "map_dir_item");
let file_type = item.file_type().map_err(Error::Io)?;
tracing::trace!(?file_type, "map_dir_item");
if file_type.is_dir() {
Ok(DirItem::Dir(item.path()))
} else if file_type.is_file() {

View file

@ -17,6 +17,7 @@ impl FileHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn reader(&self) -> Result<Reader> {
self.check_error()?;
Reader::new(&self.as_pathbuf())
@ -35,9 +36,12 @@ impl FileHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn write<C: AsRef<[u8]>>(&self, contents: C) -> Result<()> {
self.check_error()?;
std::fs::write(self.as_pathbuf(), contents).map_err(Error::Io)
let file = self.as_pathbuf();
tracing::debug!(?file, "std::fs::write");
std::fs::write(file, contents).map_err(Error::Io)
}
/// Copies the contents of a file to another file.
@ -56,9 +60,13 @@ impl FileHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn copy(&self, dest: &FileHandle) -> Result<u64> {
self.check_error()?;
std::fs::copy(self.as_pathbuf(), dest.as_pathbuf()).map_err(Error::Io)
let from = self.as_pathbuf();
let to = dest.as_pathbuf();
tracing::debug!(?from, ?to, "std::fs::copy");
std::fs::copy(from, to).map_err(Error::Io)
}
/// Removes a file.
@ -74,9 +82,12 @@ impl FileHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn remove(&self) -> Result<()> {
self.check_error()?;
std::fs::remove_file(self.as_pathbuf()).map_err(Error::Io)
let file = self.as_pathbuf();
tracing::debug!(?file, "std::fs::remove_file");
std::fs::remove_file(file).map_err(Error::Io)
}
/// Creates a hard link on the filesystem.
@ -96,9 +107,13 @@ impl FileHandle {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn hard_link(&self, dest: &FileHandle) -> Result<()> {
self.check_error()?;
std::fs::hard_link(self.as_pathbuf(), dest.as_pathbuf()).map_err(Error::Io)
let original = self.as_pathbuf();
let link = dest.as_pathbuf();
tracing::debug!(?original, ?link, "std::fs::hard_link");
std::fs::hard_link(original, link).map_err(Error::Io)
}
}
impl TryFrom<PathHandle<PathMarker>> for FileHandle {

View file

@ -83,7 +83,9 @@ pub use temp::TempFileSystem;
/// `error::Error::PathTraversal` error when attempting the
/// opertation.
pub fn new(base: impl Into<PathBuf>) -> FileSystem {
FileSystem::new(base.into())
let base = base.into();
tracing::debug!(?base, "new");
FileSystem::new(base)
}
/// Creates a new `TempFileSystem` for a temporary directory.

View file

@ -38,10 +38,12 @@ pub struct PathReal<T: PathType> {
pub(super) error: Option<Error>,
}
impl<T: PathType> PathReal<T> {
#[tracing::instrument(skip_all)]
pub(super) fn new(base: impl Into<PathBuf>, path: impl Into<PathBuf>) -> Self {
let base: PathBuf = base.into();
let path: PathBuf = path.into();
let error = PathReal::<T>::validate(&base, &path);
tracing::debug!(?base, ?path, ?error, "new");
Self {
base,
path,
@ -66,6 +68,7 @@ impl<T: PathType> PathReal<T> {
self.base.join(&self.path)
}
// only stores the first error, subsequent errors are dropped
pub(super) fn put(&mut self, error: Error) {
if self.error.is_none() {
self.error.replace(error);
@ -87,20 +90,25 @@ impl<T: PathType> PathReal<T> {
}
}
#[tracing::instrument]
fn clean_path(path: &Path) -> Result<PathBuf> {
// let path = path.as_ref();
use path_clean::PathClean;
let abs_path = if path.is_absolute() {
tracing::debug!("is absolute");
path.to_path_buf()
} else {
tracing::debug!("std::env::current_dir");
std::env::current_dir().expect("current_dir").join(path)
}
.clean();
tracing::debug!(?abs_path);
Ok(abs_path)
}
#[tracing::instrument(skip_all)]
pub(super) fn check_error(&self) -> Result<()> {
if let Some(error) = &self.error {
tracing::warn!(?error, "error");
Err(error.clone())
} else {
Ok(())
@ -123,9 +131,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn exists(&self) -> Result<bool> {
self.check_error()?;
Ok(self.as_pathbuf().exists())
let path = self.as_pathbuf();
tracing::debug!(?path, "PathBuf::exists");
Ok(path.exists())
}
/// Returns true if the path is a directory.
@ -140,9 +151,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn is_dir(&self) -> Result<bool> {
self.check_error()?;
Ok(self.as_pathbuf().is_dir())
let path = self.as_pathbuf();
tracing::debug!(?path, "PathBuf::is_dir");
Ok(path.is_dir())
}
/// Returns true if the path is a file.
@ -157,9 +171,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn is_file(&self) -> Result<bool> {
self.check_error()?;
Ok(self.as_pathbuf().is_file())
let path = self.as_pathbuf();
tracing::debug!(?path, "PathBuf::is_file");
Ok(path.is_file())
}
/// Returns the path as a directory if it exists and is a directory, otherwise
@ -176,9 +193,13 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn as_dir(&self) -> Result<Option<DirHandle>> {
self.check_error()?;
if self.as_pathbuf().is_dir() {
let path = self.as_pathbuf();
let is_dir = path.is_dir();
tracing::debug!(?path, ?is_dir, "PathBuf::is_dir");
if is_dir {
Ok(Some(PathReal::new(&self.base, &self.path)))
} else {
Ok(None)
@ -199,9 +220,13 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn as_file(&self) -> Result<Option<FileHandle>> {
self.check_error()?;
if self.as_pathbuf().is_file() {
let path = self.as_pathbuf();
let is_file = path.is_file();
tracing::debug!(?path, ?is_file, "PathBuf::is_file");
if is_file {
Ok(Some(PathReal::new(&self.base, &self.path)))
} else {
Ok(None)
@ -225,9 +250,13 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn rename(&self, dest: &PathHandle<T>) -> Result<()> {
self.check_error()?;
std::fs::rename(self.as_pathbuf(), dest.as_pathbuf()).map_err(Error::Io)
let from = self.as_pathbuf();
let to = dest.as_pathbuf();
tracing::debug!(?from, ?to, "std::fs::rename");
std::fs::rename(from, to).map_err(Error::Io)
}
/// Returns the metadata for a path.
@ -243,9 +272,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn metadata(&self) -> Result<std::fs::Metadata> {
self.check_error()?;
std::fs::metadata(self.as_pathbuf()).map_err(Error::Io)
let path = self.as_pathbuf();
tracing::debug!(?path, "std::fs::metadata");
std::fs::metadata(path).map_err(Error::Io)
}
/// Creates a symbolic link to a path.
@ -265,9 +297,13 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn soft_link(&self, link: &PathReal<PathMarker>) -> Result<()> {
self.check_error()?;
std::os::unix::fs::symlink(self.as_pathbuf(), link.as_pathbuf()).map_err(Error::Io)
let original = self.as_pathbuf();
let link = link.as_pathbuf();
tracing::debug!(?original, ?link, "std::os::unix::fs::symlink");
std::os::unix::fs::symlink(original, link).map_err(Error::Io)
}
/// Returns true if the path is a symbolic link.
@ -283,9 +319,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn is_link(&self) -> Result<bool> {
self.check_error()?;
Ok(self.as_pathbuf().is_symlink())
let path = self.as_pathbuf();
tracing::debug!(?path, "PathBuf::is_symlink");
Ok(path.is_symlink())
}
/// Returns the canonical, absolute form of the path with all intermediate
@ -302,9 +341,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn canonicalize(&self) -> Result<PathBuf> {
self.check_error()?;
self.as_pathbuf().canonicalize().map_err(Error::Io)
let path = self.as_pathbuf();
tracing::debug!(?path, "PathBuf::canonicalize");
path.canonicalize().map_err(Error::Io)
}
/// Returns the metadata for a path without following symlinks.
@ -320,9 +362,12 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn symlink_metadata(&self) -> Result<std::fs::Metadata> {
self.check_error()?;
std::fs::symlink_metadata(self.as_pathbuf()).map_err(Error::Io)
let path = self.as_pathbuf();
tracing::debug!(?path, "std::fs::symlink_metadata");
std::fs::symlink_metadata(path).map_err(Error::Io)
}
/// Sets the permissions of a file or directory.
@ -341,14 +386,20 @@ impl<T: PathType> PathReal<T> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub fn set_permissions(&self, perms: std::fs::Permissions) -> Result<()> {
self.check_error()?;
std::fs::set_permissions(self.as_pathbuf(), perms).map_err(Error::Io)
let path = self.as_pathbuf();
tracing::debug!(?path, ?perms, "std::fs::set_permissions");
std::fs::set_permissions(path, perms).map_err(Error::Io)
}
#[tracing::instrument(skip_all)]
pub fn read_link(&self) -> Result<PathReal<PathMarker>> {
self.check_error()?;
let read_path = std::fs::read_link(self.as_pathbuf()).map_err(Error::Io)?;
let path = self.as_pathbuf();
tracing::debug!(?path, "std::fs::read_link");
let read_path = std::fs::read_link(path).map_err(Error::Io)?;
let path = read_path.strip_prefix(&self.base).unwrap().to_path_buf();
Ok(PathReal::new(&self.base, &path))
}

View file

@ -11,8 +11,11 @@ pub struct Reader {
contents: String,
}
impl Reader {
#[tracing::instrument]
pub(super) fn new(path: &Path) -> Result<Self> {
tracing::debug!("std::fs::read_to_string");
let contents = std::fs::read_to_string(path).map_err(Error::Io)?;
tracing::debug!(len = contents.len(), "contents");
Ok(Self { contents })
}

View file

@ -34,6 +34,7 @@ impl FileSystem {
}
/// Returns a [PathBuf] for the path.
#[tracing::instrument]
pub fn path_of(&self, path: PathBuf) -> Result<PathBuf> {
let path_of = self.base.as_path().join(path);
self.validate(&path_of)?;
@ -55,13 +56,16 @@ impl FileSystem {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn dir(&self, path: &Path) -> DirHandle {
let mut dir = PathReal::new(&self.base, path);
if dir.error.is_none() {
if let Ok(exists) = dir.exists() {
tracing::debug!(?exists);
if exists {
if let Ok(is_dir) = dir.is_dir() {
tracing::debug!(?is_dir);
if !is_dir {
dir.put(Error::NotADirectory {
path: dir.as_pathbuf(),
@ -90,13 +94,16 @@ impl FileSystem {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn file(&self, path: &Path) -> FileHandle {
let mut file = PathReal::new(&self.base, path);
if file.error.is_none() {
if let Ok(exists) = file.exists() {
tracing::debug!(?exists);
if exists {
if let Ok(is_file) = file.is_file() {
tracing::debug!(?is_file);
if !is_file {
file.put(Error::NotAFile {
path: file.as_pathbuf(),
@ -124,10 +131,12 @@ impl FileSystem {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument]
pub fn path(&self, path: &Path) -> PathHandle<PathMarker> {
PathReal::new(&self.base, path)
}
#[tracing::instrument]
fn validate(&self, path: &Path) -> Result<()> {
let path = self.clean_path(path)?;
if !path.starts_with(&self.base) {
@ -139,15 +148,19 @@ impl FileSystem {
Ok(())
}
#[tracing::instrument]
fn clean_path(&self, path: &Path) -> Result<PathBuf> {
// let path = path.as_ref();
use path_clean::PathClean;
let abs_path = if path.is_absolute() {
tracing::debug!("is_absolute");
path.to_path_buf()
} else {
tracing::debug!("std::env::current_dir");
std::env::current_dir().map_err(Error::Io)?.join(path)
}
.clean();
tracing::debug!(?abs_path);
Ok(abs_path)
}
}

View file

@ -10,6 +10,7 @@ pub struct TempFileSystem {
_temp_dir: Arc<Mutex<TempDir>>,
}
impl TempFileSystem {
#[tracing::instrument]
pub fn new() -> super::Result<Self> {
let temp_dir = tempfile::tempdir().map_err(Error::Io)?;
let base = temp_dir.path().to_path_buf();
@ -34,3 +35,10 @@ impl std::ops::Deref for TempFileSystem {
&self.real
}
}
impl Drop for TempFileSystem {
#[cfg_attr(test, mutants::skip)]
#[tracing::instrument]
fn drop(&mut self) {
tracing::debug!("drop");
}
}