From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- third_party/rust/fs-err/src/dir.rs | 90 ++++++ third_party/rust/fs-err/src/errors.rs | 198 ++++++++++++ third_party/rust/fs-err/src/file.rs | 366 ++++++++++++++++++++++ third_party/rust/fs-err/src/lib.rs | 250 +++++++++++++++ third_party/rust/fs-err/src/open_options.rs | 134 ++++++++ third_party/rust/fs-err/src/os.rs | 11 + third_party/rust/fs-err/src/os/unix.rs | 38 +++ third_party/rust/fs-err/src/os/windows.rs | 49 +++ third_party/rust/fs-err/src/path.rs | 43 +++ third_party/rust/fs-err/src/tokio/dir_builder.rs | 54 ++++ third_party/rust/fs-err/src/tokio/file.rs | 243 ++++++++++++++ third_party/rust/fs-err/src/tokio/mod.rs | 189 +++++++++++ third_party/rust/fs-err/src/tokio/open_options.rs | 109 +++++++ third_party/rust/fs-err/src/tokio/read_dir.rs | 94 ++++++ 14 files changed, 1868 insertions(+) create mode 100644 third_party/rust/fs-err/src/dir.rs create mode 100644 third_party/rust/fs-err/src/errors.rs create mode 100644 third_party/rust/fs-err/src/file.rs create mode 100644 third_party/rust/fs-err/src/lib.rs create mode 100644 third_party/rust/fs-err/src/open_options.rs create mode 100644 third_party/rust/fs-err/src/os.rs create mode 100644 third_party/rust/fs-err/src/os/unix.rs create mode 100644 third_party/rust/fs-err/src/os/windows.rs create mode 100644 third_party/rust/fs-err/src/path.rs create mode 100644 third_party/rust/fs-err/src/tokio/dir_builder.rs create mode 100644 third_party/rust/fs-err/src/tokio/file.rs create mode 100644 third_party/rust/fs-err/src/tokio/mod.rs create mode 100644 third_party/rust/fs-err/src/tokio/open_options.rs create mode 100644 third_party/rust/fs-err/src/tokio/read_dir.rs (limited to 'third_party/rust/fs-err/src') diff --git a/third_party/rust/fs-err/src/dir.rs b/third_party/rust/fs-err/src/dir.rs new file mode 100644 index 0000000000..6efa58da61 --- /dev/null +++ b/third_party/rust/fs-err/src/dir.rs @@ -0,0 +1,90 @@ +use std::ffi::OsString; +use std::fs; +use std::io; +use std::path::PathBuf; + +use crate::errors::{Error, ErrorKind}; + +/// Wrapper for [`fs::read_dir`](https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html). +pub fn read_dir>(path: P) -> io::Result { + let path = path.into(); + + match fs::read_dir(&path) { + Ok(inner) => Ok(ReadDir { inner, path }), + Err(source) => Err(Error::build(source, ErrorKind::ReadDir, path)), + } +} + +/// Wrapper around [`std::fs::ReadDir`][std::fs::ReadDir] which adds more +/// helpful information to all errors. +/// +/// This struct is created via [`fs_err::read_dir`][fs_err::read_dir]. +/// +/// [std::fs::ReadDir]: https://doc.rust-lang.org/stable/std/fs/struct.ReadDir.html +/// [fs_err::read_dir]: fn.read_dir.html +#[derive(Debug)] +pub struct ReadDir { + inner: fs::ReadDir, + path: PathBuf, +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option { + Some( + self.inner + .next()? + .map_err(|source| Error::build(source, ErrorKind::ReadDir, &self.path)) + .map(|inner| DirEntry { inner }), + ) + } +} + +/// Wrapper around [`std::fs::DirEntry`][std::fs::DirEntry] which adds more +/// helpful information to all errors. +/// +/// [std::fs::DirEntry]: https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html +#[derive(Debug)] +pub struct DirEntry { + inner: fs::DirEntry, +} + +impl DirEntry { + /// Wrapper for [`DirEntry::path`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.path). + pub fn path(&self) -> PathBuf { + self.inner.path() + } + + /// Wrapper for [`DirEntry::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.metadata). + pub fn metadata(&self) -> io::Result { + self.inner + .metadata() + .map_err(|source| Error::build(source, ErrorKind::Metadata, self.path())) + } + + /// Wrapper for [`DirEntry::file_type`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.file_type). + pub fn file_type(&self) -> io::Result { + self.inner + .file_type() + .map_err(|source| Error::build(source, ErrorKind::Metadata, self.path())) + } + + /// Wrapper for [`DirEntry::file_name`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.file_name). + pub fn file_name(&self) -> OsString { + self.inner.file_name() + } +} + +#[cfg(unix)] +mod unix { + use std::os::unix::fs::DirEntryExt; + + use super::*; + + impl DirEntryExt for DirEntry { + fn ino(&self) -> u64 { + self.inner.ino() + } + } +} diff --git a/third_party/rust/fs-err/src/errors.rs b/third_party/rust/fs-err/src/errors.rs new file mode 100644 index 0000000000..43bc4ba420 --- /dev/null +++ b/third_party/rust/fs-err/src/errors.rs @@ -0,0 +1,198 @@ +use std::error::Error as StdError; +use std::fmt; +use std::io; +use std::path::PathBuf; + +#[derive(Debug, Clone, Copy)] +pub(crate) enum ErrorKind { + OpenFile, + CreateFile, + CreateDir, + SyncFile, + SetLen, + Metadata, + Clone, + SetPermissions, + Read, + Seek, + Write, + Flush, + ReadDir, + RemoveFile, + RemoveDir, + Canonicalize, + ReadLink, + SymlinkMetadata, + + #[cfg(windows)] + SeekRead, + #[cfg(windows)] + SeekWrite, + + #[cfg(unix)] + ReadAt, + #[cfg(unix)] + WriteAt, +} + +/// Contains an IO error that has a file path attached. +/// +/// This type is never returned directly, but is instead wrapped inside yet +/// another IO error. +#[derive(Debug)] +pub(crate) struct Error { + kind: ErrorKind, + source: io::Error, + path: PathBuf, +} + +impl Error { + pub fn build(source: io::Error, kind: ErrorKind, path: impl Into) -> io::Error { + io::Error::new( + source.kind(), + Self { + kind, + source, + path: path.into(), + }, + ) + } +} + +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + use ErrorKind::*; + + let path = self.path.display(); + + match self.kind { + OpenFile => write!(formatter, "failed to open file `{}`", path), + CreateFile => write!(formatter, "failed to create file `{}`", path), + CreateDir => write!(formatter, "failed to create directory `{}`", path), + SyncFile => write!(formatter, "failed to sync file `{}`", path), + SetLen => write!(formatter, "failed to set length of file `{}`", path), + Metadata => write!(formatter, "failed to query metadata of file `{}`", path), + Clone => write!(formatter, "failed to clone handle for file `{}`", path), + SetPermissions => write!(formatter, "failed to set permissions for file `{}`", path), + Read => write!(formatter, "failed to read from file `{}`", path), + Seek => write!(formatter, "failed to seek in file `{}`", path), + Write => write!(formatter, "failed to write to file `{}`", path), + Flush => write!(formatter, "failed to flush file `{}`", path), + ReadDir => write!(formatter, "failed to read directory `{}`", path), + RemoveFile => write!(formatter, "failed to remove file `{}`", path), + RemoveDir => write!(formatter, "failed to remove directory `{}`", path), + Canonicalize => write!(formatter, "failed to canonicalize path `{}`", path), + ReadLink => write!(formatter, "failed to read symbolic link `{}`", path), + SymlinkMetadata => write!(formatter, "failed to query metadata of symlink `{}`", path), + + #[cfg(windows)] + SeekRead => write!(formatter, "failed to seek and read from `{}`", path), + #[cfg(windows)] + SeekWrite => write!(formatter, "failed to seek and write to `{}`", path), + + #[cfg(unix)] + ReadAt => write!(formatter, "failed to read with offset from `{}`", path), + #[cfg(unix)] + WriteAt => write!(formatter, "failed to write with offset to `{}`", path), + } + } +} + +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + self.source() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum SourceDestErrorKind { + Copy, + HardLink, + Rename, + SoftLink, + + #[cfg(unix)] + Symlink, + + #[cfg(windows)] + SymlinkDir, + #[cfg(windows)] + SymlinkFile, +} + +/// Error type used by functions like `fs::copy` that holds two paths. +#[derive(Debug)] +pub(crate) struct SourceDestError { + kind: SourceDestErrorKind, + source: io::Error, + from_path: PathBuf, + to_path: PathBuf, +} + +impl SourceDestError { + pub fn build( + source: io::Error, + kind: SourceDestErrorKind, + from_path: impl Into, + to_path: impl Into, + ) -> io::Error { + io::Error::new( + source.kind(), + Self { + kind, + source, + from_path: from_path.into(), + to_path: to_path.into(), + }, + ) + } +} + +impl fmt::Display for SourceDestError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let from = self.from_path.display(); + let to = self.to_path.display(); + match self.kind { + SourceDestErrorKind::Copy => { + write!(formatter, "failed to copy file from {} to {}", from, to) + } + SourceDestErrorKind::HardLink => { + write!(formatter, "failed to hardlink file from {} to {}", from, to) + } + SourceDestErrorKind::Rename => { + write!(formatter, "failed to rename file from {} to {}", from, to) + } + SourceDestErrorKind::SoftLink => { + write!(formatter, "failed to softlink file from {} to {}", from, to) + } + + #[cfg(unix)] + SourceDestErrorKind::Symlink => { + write!(formatter, "failed to symlink file from {} to {}", from, to) + } + + #[cfg(windows)] + SourceDestErrorKind::SymlinkFile => { + write!(formatter, "failed to symlink file from {} to {}", from, to) + } + #[cfg(windows)] + SourceDestErrorKind::SymlinkDir => { + write!(formatter, "failed to symlink dir from {} to {}", from, to) + } + } + } +} + +impl StdError for SourceDestError { + fn cause(&self) -> Option<&dyn StdError> { + self.source() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } +} diff --git a/third_party/rust/fs-err/src/file.rs b/third_party/rust/fs-err/src/file.rs new file mode 100644 index 0000000000..fcf54fe3cf --- /dev/null +++ b/third_party/rust/fs-err/src/file.rs @@ -0,0 +1,366 @@ +use std::fs; +use std::io::{self, Read, Seek, Write}; +use std::path::{Path, PathBuf}; + +use crate::errors::{Error, ErrorKind}; + +/// Wrapper around [`std::fs::File`][std::fs::File] which adds more helpful +/// information to all errors. +/// +/// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html +#[derive(Debug)] +pub struct File { + file: fs::File, + path: PathBuf, +} + +// Opens a std File and returns it or an error generator which only needs the path to produce the error. +// Exists for the `crate::read*` functions so they don't unconditionally build a PathBuf. +pub(crate) fn open(path: &Path) -> Result io::Error> { + fs::File::open(&path).map_err(|err| |path| Error::build(err, ErrorKind::OpenFile, path)) +} + +// like `open()` but for `crate::write` +pub(crate) fn create(path: &Path) -> Result io::Error> { + fs::File::create(&path).map_err(|err| |path| Error::build(err, ErrorKind::CreateFile, path)) +} + +/// Wrappers for methods from [`std::fs::File`][std::fs::File]. +/// +/// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html +impl File { + /// Wrapper for [`File::open`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.open). + pub fn open

(path: P) -> Result + where + P: Into, + { + let path = path.into(); + match open(&path) { + Ok(file) => Ok(File::from_parts(file, path)), + Err(err_gen) => Err(err_gen(path)), + } + } + + /// Wrapper for [`File::create`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.create). + pub fn create

(path: P) -> Result + where + P: Into, + { + let path = path.into(); + match create(&path) { + Ok(file) => Ok(File::from_parts(file, path)), + Err(err_gen) => Err(err_gen(path)), + } + } + + /// Wrapper for [`OpenOptions::open`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html#method.open). + /// + /// This takes [`&std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html), + /// not [`crate::OpenOptions`]. + #[deprecated = "use fs_err::OpenOptions::open instead"] + pub fn from_options

(path: P, options: &fs::OpenOptions) -> Result + where + P: Into, + { + let path = path.into(); + match options.open(&path) { + Ok(file) => Ok(File::from_parts(file, path)), + Err(source) => Err(Error::build(source, ErrorKind::OpenFile, path)), + } + } + + /// Wrapper for [`File::sync_all`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.sync_all). + pub fn sync_all(&self) -> Result<(), io::Error> { + self.file + .sync_all() + .map_err(|source| self.error(source, ErrorKind::SyncFile)) + } + + /// Wrapper for [`File::sync_data`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.sync_data). + pub fn sync_data(&self) -> Result<(), io::Error> { + self.file + .sync_data() + .map_err(|source| self.error(source, ErrorKind::SyncFile)) + } + + /// Wrapper for [`File::set_len`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_len). + pub fn set_len(&self, size: u64) -> Result<(), io::Error> { + self.file + .set_len(size) + .map_err(|source| self.error(source, ErrorKind::SetLen)) + } + + /// Wrapper for [`File::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.metadata). + pub fn metadata(&self) -> Result { + self.file + .metadata() + .map_err(|source| self.error(source, ErrorKind::Metadata)) + } + + /// Wrapper for [`File::try_clone`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_clone). + pub fn try_clone(&self) -> Result { + self.file + .try_clone() + .map(|file| File { + file, + path: self.path.clone(), + }) + .map_err(|source| self.error(source, ErrorKind::Clone)) + } + + /// Wrapper for [`File::set_permissions`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_permissions). + pub fn set_permissions(&self, perm: fs::Permissions) -> Result<(), io::Error> { + self.file + .set_permissions(perm) + .map_err(|source| self.error(source, ErrorKind::SetPermissions)) + } +} + +/// Methods added by fs-err that are not available on +/// [`std::fs::File`][std::fs::File]. +/// +/// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html +impl File { + /// Creates a [`File`](struct.File.html) from a raw file and its path. + pub fn from_parts

(file: fs::File, path: P) -> Self + where + P: Into, + { + File { + file, + path: path.into(), + } + } + + /// Extract the raw file and its path from this [`File`](struct.File.html) + pub fn into_parts(self) -> (fs::File, PathBuf) { + (self.file, self.path) + } + + /// Returns a reference to the underlying [`std::fs::File`][std::fs::File]. + /// + /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html + pub fn file(&self) -> &fs::File { + &self.file + } + + /// Returns a mutable reference to the underlying [`std::fs::File`][std::fs::File]. + /// + /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html + pub fn file_mut(&mut self) -> &mut fs::File { + &mut self.file + } + + /// 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 Read for File { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.file + .read(buf) + .map_err(|source| self.error(source, ErrorKind::Read)) + } + + fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { + self.file + .read_vectored(bufs) + .map_err(|source| self.error(source, ErrorKind::Read)) + } +} + +impl<'a> Read for &'a File { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + (&(**self).file) + .read(buf) + .map_err(|source| self.error(source, ErrorKind::Read)) + } + + fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { + (&(**self).file) + .read_vectored(bufs) + .map_err(|source| self.error(source, ErrorKind::Read)) + } +} + +impl From for fs::File { + fn from(file: File) -> Self { + file.into_parts().0 + } +} + +impl Seek for File { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + self.file + .seek(pos) + .map_err(|source| self.error(source, ErrorKind::Seek)) + } +} + +impl<'a> Seek for &'a File { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + (&(**self).file) + .seek(pos) + .map_err(|source| self.error(source, ErrorKind::Seek)) + } +} + +impl Write for File { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.file + .write(buf) + .map_err(|source| self.error(source, ErrorKind::Write)) + } + + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + self.file + .write_vectored(bufs) + .map_err(|source| self.error(source, ErrorKind::Write)) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.file + .flush() + .map_err(|source| self.error(source, ErrorKind::Flush)) + } +} + +impl<'a> Write for &'a File { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + (&(**self).file) + .write(buf) + .map_err(|source| self.error(source, ErrorKind::Write)) + } + + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + (&(**self).file) + .write_vectored(bufs) + .map_err(|source| self.error(source, ErrorKind::Write)) + } + + fn flush(&mut self) -> std::io::Result<()> { + (&(**self).file) + .flush() + .map_err(|source| self.error(source, ErrorKind::Flush)) + } +} + +#[cfg(unix)] +mod unix { + use crate::os::unix::fs::FileExt; + use crate::ErrorKind; + use std::io; + use std::os::unix::fs::FileExt as _; + use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; + + impl AsRawFd for crate::File { + fn as_raw_fd(&self) -> RawFd { + self.file().as_raw_fd() + } + } + + impl IntoRawFd for crate::File { + fn into_raw_fd(self) -> RawFd { + self.file.into_raw_fd() + } + } + + impl FileExt for crate::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.file() + .read_at(buf, offset) + .map_err(|err| self.error(err, ErrorKind::ReadAt)) + } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.file() + .write_at(buf, offset) + .map_err(|err| self.error(err, ErrorKind::WriteAt)) + } + } + + #[cfg(feature = "io_safety")] + mod io_safety { + use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] + impl AsFd for crate::File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.file().as_fd() + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] + impl From for OwnedFd { + fn from(file: crate::File) -> Self { + file.into_parts().0.into() + } + } + } +} + +#[cfg(windows)] +mod windows { + use crate::os::windows::fs::FileExt; + use crate::ErrorKind; + use std::io; + use std::os::windows::{ + fs::FileExt as _, + io::{AsRawHandle, IntoRawHandle, RawHandle}, + }; + + impl FileExt for crate::File { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.file() + .seek_read(buf, offset) + .map_err(|err| self.error(err, ErrorKind::SeekRead)) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + self.file() + .seek_write(buf, offset) + .map_err(|err| self.error(err, ErrorKind::SeekWrite)) + } + } + + impl AsRawHandle for crate::File { + fn as_raw_handle(&self) -> RawHandle { + self.file().as_raw_handle() + } + } + + // can't be implemented, because the trait doesn't give us a Path + // impl std::os::windows::io::FromRawHandle for crate::File { + // } + + impl IntoRawHandle for crate::File { + fn into_raw_handle(self) -> RawHandle { + self.file.into_raw_handle() + } + } + + #[cfg(feature = "io_safety")] + mod io_safety { + use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle}; + + #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] + impl AsHandle for crate::File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.file().as_handle() + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] + impl From for OwnedHandle { + fn from(file: crate::File) -> Self { + file.into_parts().0.into() + } + } + } +} diff --git a/third_party/rust/fs-err/src/lib.rs b/third_party/rust/fs-err/src/lib.rs new file mode 100644 index 0000000000..b1ceae529a --- /dev/null +++ b/third_party/rust/fs-err/src/lib.rs @@ -0,0 +1,250 @@ +/*! +fs-err is a drop-in replacement for [`std::fs`][std::fs] that provides more +helpful messages on errors. Extra information includes which operations was +attempted and any involved paths. + +# Error Messages + +Using [`std::fs`][std::fs], if this code fails: + +```no_run +# use std::fs::File; +let file = File::open("does not exist.txt")?; +# Ok::<(), std::io::Error>(()) +``` + +The error message that Rust gives you isn't very useful: + +```txt +The system cannot find the file specified. (os error 2) +``` + +...but if we use fs-err instead, our error contains more actionable information: + +```txt +failed to open file `does not exist.txt` + caused by: The system cannot find the file specified. (os error 2) +``` + +# Usage + +fs-err's API is the same as [`std::fs`][std::fs], so migrating code to use it is easy. + +```no_run +// use std::fs; +use fs_err as fs; + +let contents = fs::read_to_string("foo.txt")?; + +println!("Read foo.txt: {}", contents); + +# Ok::<(), std::io::Error>(()) +``` + +fs-err uses [`std::io::Error`][std::io::Error] for all errors. This helps fs-err +compose well with traits from the standard library like +[`std::io::Read`][std::io::Read] and crates that use them like +[`serde_json`][serde_json]: + +```no_run +use fs_err::File; + +let file = File::open("my-config.json")?; + +// If an I/O error occurs inside serde_json, the error will include a file path +// as well as what operation was being performed. +let decoded: Vec = serde_json::from_reader(file)?; + +println!("Program config: {:?}", decoded); + +# Ok::<(), Box>(()) +``` + +[std::fs]: https://doc.rust-lang.org/stable/std/fs/ +[std::io::Error]: https://doc.rust-lang.org/stable/std/io/struct.Error.html +[std::io::Read]: https://doc.rust-lang.org/stable/std/io/trait.Read.html +[serde_json]: https://crates.io/crates/serde_json +*/ + +#![doc(html_root_url = "https://docs.rs/fs-err/2.9.0")] +#![deny(missing_debug_implementations, missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +mod dir; +mod errors; +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}; +use std::path::{Path, PathBuf}; + +use errors::{Error, ErrorKind, SourceDestError, SourceDestErrorKind}; + +pub use dir::*; +pub use file::*; +pub use open_options::OpenOptions; +pub use path::PathExt; + +/// Wrapper for [`fs::read`](https://doc.rust-lang.org/stable/std/fs/fn.read.html). +pub fn read>(path: P) -> io::Result> { + let path = path.as_ref(); + let mut file = file::open(path).map_err(|err_gen| err_gen(path.to_path_buf()))?; + let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); + file.read_to_end(&mut bytes) + .map_err(|err| Error::build(err, ErrorKind::Read, path))?; + Ok(bytes) +} + +/// Wrapper for [`fs::read_to_string`](https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html). +pub fn read_to_string>(path: P) -> io::Result { + let path = path.as_ref(); + let mut file = file::open(path).map_err(|err_gen| err_gen(path.to_path_buf()))?; + let mut string = String::with_capacity(initial_buffer_size(&file)); + file.read_to_string(&mut string) + .map_err(|err| Error::build(err, ErrorKind::Read, path))?; + Ok(string) +} + +/// Wrapper for [`fs::write`](https://doc.rust-lang.org/stable/std/fs/fn.write.html). +pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + let path = path.as_ref(); + file::create(path) + .map_err(|err_gen| err_gen(path.to_path_buf()))? + .write_all(contents.as_ref()) + .map_err(|err| Error::build(err, ErrorKind::Write, path)) +} + +/// Wrapper for [`fs::copy`](https://doc.rust-lang.org/stable/std/fs/fn.copy.html). +pub fn copy(from: P, to: Q) -> io::Result +where + P: AsRef, + Q: AsRef, +{ + let from = from.as_ref(); + let to = to.as_ref(); + fs::copy(from, to) + .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::Copy, from, to)) +} + +/// Wrapper for [`fs::create_dir`](https://doc.rust-lang.org/stable/std/fs/fn.create_dir.html). +pub fn create_dir

(path: P) -> io::Result<()> +where + P: AsRef, +{ + let path = path.as_ref(); + fs::create_dir(path).map_err(|source| Error::build(source, ErrorKind::CreateDir, path)) +} + +/// Wrapper for [`fs::create_dir_all`](https://doc.rust-lang.org/stable/std/fs/fn.create_dir_all.html). +pub fn create_dir_all

(path: P) -> io::Result<()> +where + P: AsRef, +{ + let path = path.as_ref(); + fs::create_dir_all(path).map_err(|source| Error::build(source, ErrorKind::CreateDir, path)) +} + +/// Wrapper for [`fs::remove_dir`](https://doc.rust-lang.org/stable/std/fs/fn.remove_dir.html). +pub fn remove_dir

(path: P) -> io::Result<()> +where + P: AsRef, +{ + let path = path.as_ref(); + fs::remove_dir(path).map_err(|source| Error::build(source, ErrorKind::RemoveDir, path)) +} + +/// Wrapper for [`fs::remove_dir_all`](https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html). +pub fn remove_dir_all

(path: P) -> io::Result<()> +where + P: AsRef, +{ + let path = path.as_ref(); + fs::remove_dir_all(path).map_err(|source| Error::build(source, ErrorKind::RemoveDir, path)) +} + +/// Wrapper for [`fs::remove_file`](https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html). +pub fn remove_file

(path: P) -> io::Result<()> +where + P: AsRef, +{ + let path = path.as_ref(); + fs::remove_file(path).map_err(|source| Error::build(source, ErrorKind::RemoveFile, path)) +} + +/// Wrapper for [`fs::metadata`](https://doc.rust-lang.org/stable/std/fs/fn.metadata.html). +pub fn metadata>(path: P) -> io::Result { + let path = path.as_ref(); + fs::metadata(path).map_err(|source| Error::build(source, ErrorKind::Metadata, path)) +} + +/// Wrapper for [`fs::canonicalize`](https://doc.rust-lang.org/stable/std/fs/fn.canonicalize.html). +pub fn canonicalize>(path: P) -> io::Result { + let path = path.as_ref(); + fs::canonicalize(path).map_err(|source| Error::build(source, ErrorKind::Canonicalize, path)) +} + +/// Wrapper for [`fs::hard_link`](https://doc.rust-lang.org/stable/std/fs/fn.hard_link.html). +pub fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + fs::hard_link(src, dst) + .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::HardLink, src, dst)) +} + +/// Wrapper for [`fs::read_link`](https://doc.rust-lang.org/stable/std/fs/fn.read_link.html). +pub fn read_link>(path: P) -> io::Result { + let path = path.as_ref(); + fs::read_link(path).map_err(|source| Error::build(source, ErrorKind::ReadLink, path)) +} + +/// Wrapper for [`fs::rename`](https://doc.rust-lang.org/stable/std/fs/fn.rename.html). +pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { + let from = from.as_ref(); + let to = to.as_ref(); + fs::rename(from, to) + .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::Rename, from, to)) +} + +/// Wrapper for [`fs::soft_link`](https://doc.rust-lang.org/stable/std/fs/fn.soft_link.html). +#[deprecated = "replaced with std::os::unix::fs::symlink and \ +std::os::windows::fs::{symlink_file, symlink_dir}"] +pub fn soft_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + #[allow(deprecated)] + fs::soft_link(src, dst) + .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::SoftLink, src, dst)) +} + +/// Wrapper for [`fs::symlink_metadata`](https://doc.rust-lang.org/stable/std/fs/fn.symlink_metadata.html). +pub fn symlink_metadata>(path: P) -> io::Result { + let path = path.as_ref(); + fs::symlink_metadata(path) + .map_err(|source| Error::build(source, ErrorKind::SymlinkMetadata, path)) +} + +/// Wrapper for [`fs::set_permissions`](https://doc.rust-lang.org/stable/std/fs/fn.set_permissions.html). +pub fn set_permissions>(path: P, perm: fs::Permissions) -> io::Result<()> { + let path = path.as_ref(); + fs::set_permissions(path, perm) + .map_err(|source| Error::build(source, ErrorKind::SetPermissions, path)) +} + +fn initial_buffer_size(file: &std::fs::File) -> usize { + file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) +} + +pub(crate) use private::Sealed; +mod private { + pub trait Sealed {} + + impl Sealed for crate::File {} + impl Sealed for std::path::Path {} + impl Sealed for crate::OpenOptions {} +} diff --git a/third_party/rust/fs-err/src/open_options.rs b/third_party/rust/fs-err/src/open_options.rs new file mode 100644 index 0000000000..557fa7abe0 --- /dev/null +++ b/third_party/rust/fs-err/src/open_options.rs @@ -0,0 +1,134 @@ +use std::{fs, io, path::PathBuf}; +#[derive(Clone, Debug)] +/// Wrapper around [`std::fs::OptionOptions`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html) +pub struct OpenOptions(fs::OpenOptions); + +impl OpenOptions { + /// Wrapper for [`std::fs::OpenOptions::new`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.new) + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + OpenOptions(fs::OpenOptions::new()) + } + + /// Wrapper for [`std::fs::OpenOptions::read`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read) + pub fn read(&mut self, read: bool) -> &mut Self { + self.0.read(read); + self + } + + /// Wrapper for [`std::fs::OpenOptions::write`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write) + pub fn write(&mut self, write: bool) -> &mut Self { + self.0.write(write); + self + } + + /// Wrapper for [`std::fs::OpenOptions::append`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append) + pub fn append(&mut self, append: bool) -> &mut Self { + self.0.append(append); + self + } + + /// Wrapper for [`std::fs::OpenOptions::truncate`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate) + pub fn truncate(&mut self, truncate: bool) -> &mut Self { + self.0.truncate(truncate); + self + } + + /// Wrapper for [`std::fs::OpenOptions::create`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create) + pub fn create(&mut self, create: bool) -> &mut Self { + self.0.create(create); + self + } + + /// Wrapper for [`std::fs::OpenOptions::create_new`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new) + pub fn create_new(&mut self, create_new: bool) -> &mut Self { + self.0.create_new(create_new); + self + } + + /// Wrapper for [`std::fs::OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open) + pub fn open

(&self, path: P) -> io::Result + where + P: Into, + { + // We have to either duplicate the logic or call the deprecated method here. + // We can't let the deprecated function call this method, because we can't construct + // `&fs_err::OpenOptions` from `&fs::OpenOptions` without cloning + // (although cloning would probably be cheap). + #[allow(deprecated)] + crate::File::from_options(path.into(), self.options()) + } +} + +/// Methods added by fs-err that are not available on +/// [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html). +impl OpenOptions { + /// Constructs `Self` from [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html) + pub fn from_options(options: fs::OpenOptions) -> Self { + Self(options) + } + + /// Returns a reference to the underlying [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html). + /// + /// Note that calling `open()` on this reference will NOT give you the improved errors from fs-err. + pub fn options(&self) -> &fs::OpenOptions { + &self.0 + } + + /// Returns a mutable reference to the underlying [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html). + /// + /// This allows you to change settings that don't yet have wrappers in fs-err. + /// Note that calling `open()` on this reference will NOT give you the improved errors from fs-err. + pub fn options_mut(&mut self) -> &mut fs::OpenOptions { + &mut self.0 + } +} + +#[cfg(unix)] +mod unix { + use crate::os::unix::fs::OpenOptionsExt; + use std::os::unix::fs::OpenOptionsExt as _; + impl OpenOptionsExt for crate::OpenOptions { + fn mode(&mut self, mode: u32) -> &mut Self { + self.options_mut().mode(mode); + self + } + + fn custom_flags(&mut self, flags: i32) -> &mut Self { + self.options_mut().custom_flags(flags); + self + } + } +} + +#[cfg(windows)] +mod windows { + use crate::os::windows::fs::OpenOptionsExt; + use std::os::windows::fs::OpenOptionsExt as _; + + impl OpenOptionsExt for crate::OpenOptions { + fn access_mode(&mut self, access: u32) -> &mut Self { + self.options_mut().access_mode(access); + self + } + + fn share_mode(&mut self, val: u32) -> &mut Self { + self.options_mut().share_mode(val); + self + } + fn custom_flags(&mut self, flags: u32) -> &mut Self { + self.options_mut().custom_flags(flags); + self + } + + fn attributes(&mut self, val: u32) -> &mut Self { + self.options_mut().attributes(val); + self + } + + fn security_qos_flags(&mut self, flags: u32) -> &mut Self { + self.options_mut().security_qos_flags(flags); + self + } + } +} diff --git a/third_party/rust/fs-err/src/os.rs b/third_party/rust/fs-err/src/os.rs new file mode 100644 index 0000000000..b801e60763 --- /dev/null +++ b/third_party/rust/fs-err/src/os.rs @@ -0,0 +1,11 @@ +//! OS-specific functionality. + +// The std-library has a couple more platforms than just `unix` for which these apis +// are defined, but we're using just `unix` here. We can always expand later. +#[cfg(unix)] +/// Platform-specific extensions for Unix platforms. +pub mod unix; + +#[cfg(windows)] +/// Platform-specific extensions for Windows. +pub mod windows; diff --git a/third_party/rust/fs-err/src/os/unix.rs b/third_party/rust/fs-err/src/os/unix.rs new file mode 100644 index 0000000000..ad7c488c4d --- /dev/null +++ b/third_party/rust/fs-err/src/os/unix.rs @@ -0,0 +1,38 @@ +/// Unix-specific extensions to wrappers in `fs_err` for `std::fs` types. +pub mod fs { + use std::io; + use std::path::Path; + + use crate::SourceDestError; + use crate::SourceDestErrorKind; + + /// Wrapper for [`std::os::unix::fs::symlink`](https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html) + pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + std::os::unix::fs::symlink(src, dst) + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Symlink, src, dst)) + } + + /// Wrapper for [`std::os::unix::fs::FileExt`](https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html). + /// + /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). + /// This trait is sealed and can not be implemented by other crates. + pub trait FileExt: crate::Sealed { + /// Wrapper for [`FileExt::read_at`](https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#tymethod.read_at) + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; + /// Wrapper for [`FileExt::write_at`](https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#tymethod.write_at) + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; + } + + /// Wrapper for [`std::os::unix::fs::OpenOptionsExt`](https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html) + /// + /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). + /// This trait is sealed and can not be implemented by other crates. + pub trait OpenOptionsExt: crate::Sealed { + /// Wrapper for [`OpenOptionsExt::mode`](https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html#tymethod.mode) + fn mode(&mut self, mode: u32) -> &mut Self; + /// Wrapper for [`OpenOptionsExt::custom_flags`](https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html#tymethod.custom_flags) + fn custom_flags(&mut self, flags: i32) -> &mut Self; + } +} diff --git a/third_party/rust/fs-err/src/os/windows.rs b/third_party/rust/fs-err/src/os/windows.rs new file mode 100644 index 0000000000..f559856237 --- /dev/null +++ b/third_party/rust/fs-err/src/os/windows.rs @@ -0,0 +1,49 @@ +/// Windows-specific extensions to wrappers in `fs_err` for `std::fs` types. +pub mod fs { + use crate::{SourceDestError, SourceDestErrorKind}; + use std::io; + use std::path::Path; + /// Wrapper for [std::os::windows::fs::symlink_dir](https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html) + pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + std::os::windows::fs::symlink_dir(src, dst) + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkDir, src, dst)) + } + + /// Wrapper for [std::os::windows::fs::symlink_file](https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html) + pub fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + std::os::windows::fs::symlink_file(src, dst) + .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkFile, src, dst)) + } + + /// Wrapper for [`std::os::windows::fs::FileExt`](https://doc.rust-lang.org/std/os/windows/fs/trait.FileExt.html). + /// + /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). + /// This trait is sealed and can not be implemented by other crates. + pub trait FileExt: crate::Sealed { + /// Wrapper for [`FileExt::seek_read`](https://doc.rust-lang.org/std/os/windows/fs/trait.FileExt.html#tymethod.seek_read) + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + /// Wrapper for [`FileExt::seek_wriite`](https://doc.rust-lang.org/std/os/windows/fs/trait.FileExt.html#tymethod.seek_write) + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; + } + + /// Wrapper for [`std::os::windows::fs::OpenOptionsExt`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html) + /// + /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). + /// This trait is sealed and can not be implemented by other crates. + pub trait OpenOptionsExt: crate::Sealed { + /// Wrapper for [`OpenOptionsExt::access_mode`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.access_mode) + fn access_mode(&mut self, access: u32) -> &mut Self; + /// Wrapper for [`OpenOptionsExt::share_mode`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.share_mode) + fn share_mode(&mut self, val: u32) -> &mut Self; + /// Wrapper for [`OpenOptionsExt::custom_flags`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.custom_flags) + fn custom_flags(&mut self, flags: u32) -> &mut Self; + /// Wrapper for [`OpenOptionsExt::attributes`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.attributes) + fn attributes(&mut self, val: u32) -> &mut Self; + /// Wrapper for [`OpenOptionsExt::security_qos_flags`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags) + fn security_qos_flags(&mut self, flags: u32) -> &mut Self; + } +} diff --git a/third_party/rust/fs-err/src/path.rs b/third_party/rust/fs-err/src/path.rs new file mode 100644 index 0000000000..28549a31cf --- /dev/null +++ b/third_party/rust/fs-err/src/path.rs @@ -0,0 +1,43 @@ +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; + +/// Defines aliases on [`Path`](https://doc.rust-lang.org/std/path/struct.Path.html) for `fs_err` functions. +/// +/// This trait is sealed and can not be implemented by other crates. +// +// Because no one else can implement it, we can add methods backwards-compatibly. +pub trait PathExt: crate::Sealed { + /// Wrapper for [`crate::metadata`]. + fn fs_err_metadata(&self) -> io::Result; + /// Wrapper for [`crate::symlink_metadata`]. + fn fs_err_symlink_metadata(&self) -> io::Result; + /// Wrapper for [`crate::canonicalize`]. + fn fs_err_canonicalize(&self) -> io::Result; + /// Wrapper for [`crate::read_link`]. + fn fs_err_read_link(&self) -> io::Result; + /// Wrapper for [`crate::read_dir`]. + fn fs_err_read_dir(&self) -> io::Result; +} + +impl PathExt for Path { + fn fs_err_metadata(&self) -> io::Result { + crate::metadata(self) + } + + fn fs_err_symlink_metadata(&self) -> io::Result { + crate::symlink_metadata(self) + } + + fn fs_err_canonicalize(&self) -> io::Result { + crate::canonicalize(self) + } + + fn fs_err_read_link(&self) -> io::Result { + crate::read_link(self) + } + + fn fs_err_read_dir(&self) -> io::Result { + crate::read_dir(self) + } +} diff --git a/third_party/rust/fs-err/src/tokio/dir_builder.rs b/third_party/rust/fs-err/src/tokio/dir_builder.rs new file mode 100644 index 0000000000..0615999ee1 --- /dev/null +++ b/third_party/rust/fs-err/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/third_party/rust/fs-err/src/tokio/file.rs b/third_party/rust/fs-err/src/tokio/file.rs new file mode 100644 index 0000000000..c0dac4ca47 --- /dev/null +++ b/third_party/rust/fs-err/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/third_party/rust/fs-err/src/tokio/mod.rs b/third_party/rust/fs-err/src/tokio/mod.rs new file mode 100644 index 0000000000..bf4ac94ad2 --- /dev/null +++ b/third_party/rust/fs-err/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/third_party/rust/fs-err/src/tokio/open_options.rs b/third_party/rust/fs-err/src/tokio/open_options.rs new file mode 100644 index 0000000000..6a2b5bf35d --- /dev/null +++ b/third_party/rust/fs-err/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/third_party/rust/fs-err/src/tokio/read_dir.rs b/third_party/rust/fs-err/src/tokio/read_dir.rs new file mode 100644 index 0000000000..2594edfa92 --- /dev/null +++ b/third_party/rust/fs-err/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() + } +} -- cgit v1.2.3