diff --git a/Cargo.toml b/Cargo.toml index 6a443b6..ffb247f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ readme = "README.md" exclude = [".github", ".gitignore", "README.tpl"] [dependencies] +tokio = { version = "1.21", optional = true, default_features = false, features = ["fs"] } [dev-dependencies] serde_json = "1.0.64" diff --git a/src/lib.rs b/src/lib.rs index 26d4878..b175d8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,9 @@ mod file; mod open_options; pub mod os; mod path; +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub mod tokio; use std::fs; use std::io::{self, Read, Write}; diff --git a/src/tokio/dir_builder.rs b/src/tokio/dir_builder.rs new file mode 100644 index 0000000..730b1f0 --- /dev/null +++ b/src/tokio/dir_builder.rs @@ -0,0 +1,54 @@ +use crate::errors::{Error, ErrorKind}; +use std::io; +use std::path::Path; + +/// A builder for creating directories in various manners. +/// +/// This is a wrapper around [`tokio::fs::DirBuilder`]. +#[derive(Debug, Default)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub struct DirBuilder { + inner: tokio::fs::DirBuilder, +} + +impl DirBuilder { + /// Creates a new set of options with default mode/security settings for all + /// platforms and also non-recursive. + /// + /// This is a wrapper version of [`tokio::fs::DirBuilder::new`] + /// + /// # Examples + /// + /// ```no_run + /// use fs_err::tokio::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` + pub fn new() -> Self { + Default::default() + } + + /// Wrapper around [`tokio::fs::DirBuilder::recursive`]. + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.inner.recursive(recursive); + self + } + + /// Wrapper around [`tokio::fs::DirBuilder::create`]. + pub async fn create(&self, path: impl AsRef) -> io::Result<()> { + let path = path.as_ref(); + self.inner + .create(path) + .await + .map_err(|err| Error::build(err, ErrorKind::CreateDir, path)) + } +} + +#[cfg(unix)] +impl DirBuilder { + /// Wrapper around [`tokio::fs::DirBuilder::mode`]. + pub fn mode(&mut self, mode: u32) -> &mut Self { + self.inner.mode(mode); + self + } +} diff --git a/src/tokio/file.rs b/src/tokio/file.rs new file mode 100644 index 0000000..2d42364 --- /dev/null +++ b/src/tokio/file.rs @@ -0,0 +1,243 @@ +use crate::errors::{Error, ErrorKind}; +use std::fs::{Metadata, Permissions}; +use std::io; +use std::io::{IoSlice, SeekFrom}; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::task::{ready, Context, Poll}; +use tokio::fs; +use tokio::fs::File as TokioFile; +use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf}; + +/// Wrapper around [`tokio::fs::File`] which adds more helpful +/// information to all errors. +#[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub struct File { + tokio: fs::File, + path: PathBuf, +} + +impl File { + /// Wrapper for [`tokio::fs::File::open`]. + pub async fn open(path: impl Into) -> io::Result { + let path = path.into(); + let f = TokioFile::open(&path) + .await + .map_err(|err| Error::build(err, ErrorKind::OpenFile, &path))?; + Ok(File::from_parts(f, path)) + } + + /// Wrapper for [`tokio::fs::File::create`]. + pub async fn create(path: impl Into) -> io::Result { + let path = path.into(); + match TokioFile::create(&path).await { + Ok(f) => Ok(File::from_parts(f, path)), + Err(err) => Err(Error::build(err, ErrorKind::CreateFile, &path)), + } + } + + /// Wrapper for [`tokio::fs::File::from_std`]. + pub fn from_std(std: crate::File) -> File { + let (std, path) = std.into_parts(); + File::from_parts(TokioFile::from_std(std), path) + } + + /// Wrapper for [`tokio::fs::File::sync_all`]. + pub async fn sync_all(&self) -> io::Result<()> { + self.tokio + .sync_all() + .await + .map_err(|err| self.error(err, ErrorKind::SyncFile)) + } + + /// Wrapper for [`tokio::fs::File::sync_data`]. + pub async fn sync_data(&self) -> io::Result<()> { + self.tokio + .sync_data() + .await + .map_err(|err| self.error(err, ErrorKind::SyncFile)) + } + + /// Wrapper for [`tokio::fs::File::set_len`]. + pub async fn set_len(&self, size: u64) -> io::Result<()> { + self.tokio + .set_len(size) + .await + .map_err(|err| self.error(err, ErrorKind::SetLen)) + } + + /// Wrapper for [`tokio::fs::File::metadata`]. + pub async fn metadata(&self) -> io::Result { + self.tokio + .metadata() + .await + .map_err(|err| self.error(err, ErrorKind::Metadata)) + } + + /// Wrapper for [`tokio::fs::File::try_clone`]. + pub async fn try_clone(&self) -> io::Result { + match self.tokio.try_clone().await { + Ok(file) => Ok(File::from_parts(file, self.path.clone())), + Err(err) => Err(self.error(err, ErrorKind::Clone)), + } + } + + /// Wrapper for [`tokio::fs::File::into_std`]. + pub async fn into_std(self) -> crate::File { + crate::File::from_parts(self.tokio.into_std().await, self.path) + } + + /// Wrapper for [`tokio::fs::File::try_into_std`]. + pub fn try_into_std(self) -> Result { + match self.tokio.try_into_std() { + Ok(f) => Ok(crate::File::from_parts(f, self.path)), + Err(f) => Err(File::from_parts(f, self.path)), + } + } + + /// Wrapper for [`tokio::fs::File::set_permissions`]. + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + self.tokio + .set_permissions(perm) + .await + .map_err(|err| self.error(err, ErrorKind::SetPermissions)) + } +} + +/// Methods added by fs-err that are not available on +/// [`tokio::fs::File`]. +impl File { + /// Creates a [`File`](struct.File.html) from a raw file and its path. + pub fn from_parts

(file: TokioFile, path: P) -> Self + where + P: Into, + { + File { + tokio: file, + path: path.into(), + } + } + + /// Extract the raw file and its path from this [`File`](struct.File.html). + pub fn into_parts(self) -> (TokioFile, PathBuf) { + (self.tokio, self.path) + } + + /// Returns a reference to the underlying [`tokio::fs::File`]. + pub fn file(&self) -> &TokioFile { + &self.tokio + } + + /// Returns a mutable reference to the underlying [`tokio::fs::File`]. + pub fn file_mut(&mut self) -> &mut TokioFile { + &mut self.tokio + } + + /// Returns a reference to the path that this file was created with. + pub fn path(&self) -> &Path { + &self.path + } + + /// Wrap the error in information specific to this `File` object. + fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error { + Error::build(source, kind, &self.path) + } +} + +impl From for File { + fn from(f: crate::File) -> Self { + let (f, path) = f.into_parts(); + File::from_parts(f.into(), path) + } +} + +impl From for TokioFile { + fn from(f: File) -> Self { + f.into_parts().0 + } +} + +#[cfg(unix)] +impl std::os::unix::io::AsRawFd for File { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + self.tokio.as_raw_fd() + } +} + +#[cfg(windows)] +impl std::os::windows::io::AsRawHandle for File { + fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { + self.tokio.as_raw_handle() + } +} + +impl AsyncRead for File { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Poll::Ready( + ready!(Pin::new(&mut self.tokio).poll_read(cx, buf)) + .map_err(|err| self.error(err, ErrorKind::Read)), + ) + } +} + +impl AsyncSeek for File { + fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> { + Pin::new(&mut self.tokio) + .start_seek(position) + .map_err(|err| self.error(err, ErrorKind::Seek)) + } + + fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready( + ready!(Pin::new(&mut self.tokio).poll_complete(cx)) + .map_err(|err| self.error(err, ErrorKind::Seek)), + ) + } +} + +impl AsyncWrite for File { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready( + ready!(Pin::new(&mut self.tokio).poll_write(cx, buf)) + .map_err(|err| self.error(err, ErrorKind::Write)), + ) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready( + ready!(Pin::new(&mut self.tokio).poll_flush(cx)) + .map_err(|err| self.error(err, ErrorKind::Flush)), + ) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready( + ready!(Pin::new(&mut self.tokio).poll_shutdown(cx)) + .map_err(|err| self.error(err, ErrorKind::Flush)), + ) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Poll::Ready( + ready!(Pin::new(&mut self.tokio).poll_write_vectored(cx, bufs)) + .map_err(|err| self.error(err, ErrorKind::Write)), + ) + } + + fn is_write_vectored(&self) -> bool { + self.tokio.is_write_vectored() + } +} diff --git a/src/tokio/mod.rs b/src/tokio/mod.rs new file mode 100644 index 0000000..c2ed00a --- /dev/null +++ b/src/tokio/mod.rs @@ -0,0 +1,189 @@ +//! Tokio-specific wrappers that use `fs_err` error messages. + +use crate::errors::{Error, ErrorKind, SourceDestError, SourceDestErrorKind}; +use std::fs::{Metadata, Permissions}; +use std::path::{Path, PathBuf}; +use tokio::io; +mod dir_builder; +mod file; +mod open_options; +mod read_dir; + +pub use self::open_options::OpenOptions; +pub use self::read_dir::{read_dir, DirEntry, ReadDir}; +pub use dir_builder::DirBuilder; +pub use file::File; + +/// Wrapper for [`tokio::fs::canonicalize`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn canonicalize(path: impl AsRef) -> io::Result { + let path = path.as_ref(); + tokio::fs::canonicalize(path) + .await + .map_err(|err| Error::build(err, ErrorKind::Canonicalize, path)) +} + +/// Wrapper for [`tokio::fs::copy`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn copy(from: impl AsRef, to: impl AsRef) -> Result { + let (from, to) = (from.as_ref(), to.as_ref()); + tokio::fs::copy(from, to) + .await + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Copy, from, to)) +} + +/// Wrapper for [`tokio::fs::create_dir`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn create_dir(path: impl AsRef) -> io::Result<()> { + let path = path.as_ref(); + tokio::fs::create_dir(path) + .await + .map_err(|err| Error::build(err, ErrorKind::CreateDir, path)) +} + +/// Wrapper for [`tokio::fs::create_dir_all`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn create_dir_all(path: impl AsRef) -> io::Result<()> { + let path = path.as_ref(); + tokio::fs::create_dir_all(path) + .await + .map_err(|err| Error::build(err, ErrorKind::CreateDir, path)) +} + +/// Wrapper for [`tokio::fs::hard_link`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn hard_link(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + let (src, dst) = (src.as_ref(), dst.as_ref()); + tokio::fs::hard_link(src, dst) + .await + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::HardLink, src, dst)) +} + +/// Wrapper for [`tokio::fs::metadata`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn metadata(path: impl AsRef) -> io::Result { + let path = path.as_ref(); + tokio::fs::metadata(path) + .await + .map_err(|err| Error::build(err, ErrorKind::Metadata, path)) +} + +/// Wrapper for [`tokio::fs::read`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn read(path: impl AsRef) -> io::Result> { + let path = path.as_ref(); + tokio::fs::read(path) + .await + .map_err(|err| Error::build(err, ErrorKind::Read, path)) +} + +/// Wrapper for [`tokio::fs::read_link`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn read_link(path: impl AsRef) -> io::Result { + let path = path.as_ref(); + tokio::fs::read_link(path) + .await + .map_err(|err| Error::build(err, ErrorKind::ReadLink, path)) +} + +/// Wrapper for [`tokio::fs::read_to_string`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn read_to_string(path: impl AsRef) -> io::Result { + let path = path.as_ref(); + tokio::fs::read_to_string(path) + .await + .map_err(|err| Error::build(err, ErrorKind::Read, path)) +} + +/// Wrapper for [`tokio::fs::remove_dir`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn remove_dir(path: impl AsRef) -> io::Result<()> { + let path = path.as_ref(); + tokio::fs::remove_dir(path) + .await + .map_err(|err| Error::build(err, ErrorKind::RemoveDir, path)) +} + +/// Wrapper for [`tokio::fs::remove_dir_all`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn remove_dir_all(path: impl AsRef) -> io::Result<()> { + let path = path.as_ref(); + tokio::fs::remove_dir_all(path) + .await + .map_err(|err| Error::build(err, ErrorKind::RemoveDir, path)) +} + +/// Wrapper for [`tokio::fs::remove_file`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn remove_file(path: impl AsRef) -> io::Result<()> { + let path = path.as_ref(); + tokio::fs::remove_file(path) + .await + .map_err(|err| Error::build(err, ErrorKind::RemoveFile, path)) +} + +/// Wrapper for [`tokio::fs::rename`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn rename(from: impl AsRef, to: impl AsRef) -> io::Result<()> { + let (from, to) = (from.as_ref(), to.as_ref()); + tokio::fs::rename(from, to) + .await + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Rename, from, to)) +} + +/// Wrapper for [`tokio::fs::set_permissions`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn set_permissions(path: impl AsRef, perm: Permissions) -> io::Result<()> { + let path = path.as_ref(); + tokio::fs::set_permissions(path, perm) + .await + .map_err(|err| Error::build(err, ErrorKind::SetPermissions, path)) +} + +/// Wrapper for [`tokio::fs::symlink_metadata`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn symlink_metadata(path: impl AsRef) -> io::Result { + let path = path.as_ref(); + tokio::fs::symlink_metadata(path) + .await + .map_err(|err| Error::build(err, ErrorKind::SymlinkMetadata, path)) +} + +/// Wrapper for [`tokio::fs::symlink`]. +#[cfg(unix)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn symlink(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + let (src, dst) = (src.as_ref(), dst.as_ref()); + tokio::fs::symlink(src, dst) + .await + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Symlink, src, dst)) +} + +/// Wrapper for [`tokio::fs::symlink_dir`]. +#[cfg(windows)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn symlink(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + let (src, dst) = (src.as_ref(), dst.as_ref()); + tokio::fs::symlink_dir(src, dst) + .await + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkDir, src, dst)) +} + +/// Wrapper for [`tokio::fs::symlink_file`]. +#[cfg(windows)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn symlink_file(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + let (src, dst) = (src.as_ref(), dst.as_ref()); + tokio::fs::symlink_file(src, dst) + .await + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkFile, src, dst)) +} + +/// Wrapper for [`tokio::fs::write`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> io::Result<()> { + let (path, contents) = (path.as_ref(), contents.as_ref()); + tokio::fs::write(path, contents) + .await + .map_err(|err| Error::build(err, ErrorKind::Write, path)) +} diff --git a/src/tokio/open_options.rs b/src/tokio/open_options.rs new file mode 100644 index 0000000..9810de1 --- /dev/null +++ b/src/tokio/open_options.rs @@ -0,0 +1,109 @@ +use crate::errors::{Error, ErrorKind}; +use crate::tokio::File; +use std::io; +use std::path::Path; +use tokio::fs::OpenOptions as TokioOpenOptions; + +/// Options and flags which can be used to configure how a file is opened. +/// +/// This is a wrapper around [`tokio::fs::OpenOptions`]. +#[derive(Clone, Debug, Default)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub struct OpenOptions { + tokio: TokioOpenOptions, +} + +impl OpenOptions { + /// Creates a blank new set of options ready for configuration. + /// + /// All options are initially set to `false`. + /// + /// This is a wrapped version of [`tokio::fs::OpenOptions::new`] + /// + /// # Examples + /// + /// ```no_run + /// use fs_err::tokio::OpenOptions; + /// + /// let mut options = OpenOptions::new(); + /// let future = options.read(true).open("foo.txt"); + /// ``` + pub fn new() -> OpenOptions { + OpenOptions { + tokio: TokioOpenOptions::new(), + } + } + + /// Wrapper for [`tokio::fs::OpenOptions::read`]. + pub fn read(&mut self, read: bool) -> &mut OpenOptions { + self.tokio.read(read); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::write`]. + pub fn write(&mut self, write: bool) -> &mut OpenOptions { + self.tokio.write(write); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::append`]. + pub fn append(&mut self, append: bool) -> &mut OpenOptions { + self.tokio.append(append); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::truncate`]. + pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { + self.tokio.truncate(truncate); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::create`]. + pub fn create(&mut self, create: bool) -> &mut OpenOptions { + self.tokio.create(create); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::create_new`]. + pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { + self.tokio.create_new(create_new); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::open`]. + pub async fn open(&self, path: impl AsRef) -> io::Result { + let path = path.as_ref(); + self.tokio + .open(path) + .await + .map(|f| File::from_parts(f, path)) + .map_err(|err| Error::build(err, ErrorKind::OpenFile, path)) + } +} + +#[cfg(unix)] +impl OpenOptions { + /// Wrapper for [`tokio::fs::OpenOptions::mode`]. + pub fn mode(&mut self, mode: u32) -> &mut OpenOptions { + self.tokio.mode(mode); + self + } + + /// Wrapper for [`tokio::fs::OpenOptions::custom_flags`]. + pub fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.tokio.custom_flags(flags); + self + } +} + +impl From for OpenOptions { + fn from(std: std::fs::OpenOptions) -> Self { + OpenOptions { tokio: std.into() } + } +} + +impl From for OpenOptions { + fn from(tokio: TokioOpenOptions) -> Self { + OpenOptions { tokio } + } +} diff --git a/src/tokio/read_dir.rs b/src/tokio/read_dir.rs new file mode 100644 index 0000000..1528b7c --- /dev/null +++ b/src/tokio/read_dir.rs @@ -0,0 +1,94 @@ +use crate::errors::{Error, ErrorKind}; +use std::ffi::OsString; +use std::fs::{FileType, Metadata}; +use std::io; +use std::path::{Path, PathBuf}; +use std::task::{ready, Context, Poll}; +use tokio::fs; + +/// Wrapper for [`tokio::fs::read_dir`]. +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub async fn read_dir(path: impl AsRef) -> io::Result { + let path = path.as_ref(); + let tokio = fs::read_dir(path) + .await + .map_err(|err| Error::build(err, ErrorKind::ReadDir, path))?; + Ok(ReadDir { + tokio, + path: path.to_owned(), + }) +} + +/// Reads the entries in a directory. +/// +/// This is a wrapper around [`tokio::fs::ReadDir`]. +#[derive(Debug)] +#[must_use = "streams do nothing unless polled"] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub struct ReadDir { + tokio: fs::ReadDir, + path: PathBuf, +} + +impl ReadDir { + /// Wrapper around [`tokio::fs::ReadDir::next_entry`]. + pub async fn next_entry(&mut self) -> io::Result> { + match self.tokio.next_entry().await { + Ok(entry) => Ok(entry.map(|e| DirEntry { tokio: e })), + Err(err) => Err(Error::build(err, ErrorKind::ReadDir, &self.path)), + } + } + + /// Wrapper around [`tokio::fs::ReadDir::poll_next_entry`]. + pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll>> { + Poll::Ready(match ready!(self.tokio.poll_next_entry(cx)) { + Ok(entry) => Ok(entry.map(|e| DirEntry { tokio: e })), + Err(err) => Err(Error::build(err, ErrorKind::ReadDir, &self.path)), + }) + } +} + +/// Entries returned by the [`ReadDir`] stream. +/// +/// This is a wrapper around [`tokio::fs::DirEntry`]. +#[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +pub struct DirEntry { + tokio: fs::DirEntry, +} + +impl DirEntry { + /// Wrapper around [`tokio::fs::DirEntry::path`]. + pub fn path(&self) -> PathBuf { + self.tokio.path() + } + + /// Wrapper around [`tokio::fs::DirEntry::file_name`]. + pub fn file_name(&self) -> OsString { + self.tokio.file_name() + } + + /// Wrapper around [`tokio::fs::DirEntry::metadata`]. + pub async fn metadata(&self) -> io::Result { + self.tokio + .metadata() + .await + .map_err(|err| Error::build(err, ErrorKind::Metadata, self.path())) + } + + /// Wrapper around [`tokio::fs::DirEntry::file_type`]. + pub async fn file_type(&self) -> io::Result { + self.tokio + .file_type() + .await + .map_err(|err| Error::build(err, ErrorKind::Metadata, self.path())) + } +} + +#[cfg(unix)] +impl DirEntry { + /// Wrapper around [`tokio::fs::DirEntry::ino`]. + pub fn ino(&self) -> u64 { + self.tokio.ino() + } +}