| 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}; | |
| use super::OpenOptions; | |
| /// 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 { | |
| /// Attempts to open a file in read-only mode. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::open`]. | |
| pub async fn open(path: impl Into<PathBuf>) -> io::Result<File> { | |
| 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)) | |
| } | |
| /// Opens a file in write-only mode. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::create`]. | |
| pub async fn create(path: impl Into<PathBuf>) -> io::Result<File> { | |
| 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)), | |
| } | |
| } | |
| /// Opens a file in read-write mode. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::create_new`]. | |
| pub async fn create_new(path: impl Into<PathBuf>) -> Result<Self, io::Error> { | |
| let path = path.into(); | |
| match fs::File::create_new(&path).await { | |
| Ok(file) => Ok(File::from_parts(file, path)), | |
| Err(err) => Err(Error::build(err, ErrorKind::CreateFile, path)), | |
| } | |
| } | |
| /// Returns a new `OpenOptions` object. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::options`]. | |
| pub fn options() -> OpenOptions { | |
| OpenOptions::new() | |
| } | |
| /// Converts a [`crate::File`] to a [`tokio::fs::File`]. | |
| /// | |
| /// 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) | |
| } | |
| /// Attempts to sync all OS-internal metadata to disk. | |
| /// | |
| /// 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)) | |
| } | |
| /// This function is similar to `sync_all`, except that it may not | |
| /// synchronize file metadata to the filesystem. | |
| /// | |
| /// 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)) | |
| } | |
| /// Truncates or extends the underlying file, updating the size of this file to become size. | |
| /// | |
| /// 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)) | |
| } | |
| /// Queries metadata about the underlying file. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::metadata`]. | |
| pub async fn metadata(&self) -> io::Result<Metadata> { | |
| self.tokio | |
| .metadata() | |
| .await | |
| .map_err(|err| self.error(err, ErrorKind::Metadata)) | |
| } | |
| /// Creates a new `File` instance that shares the same underlying file handle | |
| /// as the existing `File` instance. Reads, writes, and seeks will affect both | |
| /// `File` instances simultaneously. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::try_clone`]. | |
| pub async fn try_clone(&self) -> io::Result<File> { | |
| match self.tokio.try_clone().await { | |
| Ok(file) => Ok(File::from_parts(file, self.path.clone())), | |
| Err(err) => Err(self.error(err, ErrorKind::Clone)), | |
| } | |
| } | |
| /// Destructures `File` into a [`crate::File`]. This function is async to allow any | |
| /// in-flight operations to complete. | |
| /// | |
| /// 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) | |
| } | |
| /// Tries to immediately destructure `File` into a [`crate::File`]. | |
| /// | |
| /// Wrapper for [`tokio::fs::File::try_into_std`]. | |
| pub fn try_into_std(self) -> Result<crate::File, File> { | |
| 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)), | |
| } | |
| } | |
| /// Changes the permissions on the underlying file. | |
| /// | |
| /// 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<P>(file: TokioFile, path: P) -> Self | |
| where | |
| P: Into<PathBuf>, | |
| { | |
| 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<crate::File> for File { | |
| fn from(f: crate::File) -> Self { | |
| let (f, path) = f.into_parts(); | |
| File::from_parts(f.into(), path) | |
| } | |
| } | |
| impl From<File> 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<io::Result<()>> { | |
| 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<io::Result<u64>> { | |
| 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<io::Result<usize>> { | |
| 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<io::Result<()>> { | |
| 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<io::Result<()>> { | |
| 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<io::Result<usize>> { | |
| 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() | |
| } | |
| } |