| 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<std::fs::File, impl FnOnce(PathBuf) -> 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<std::fs::File, impl FnOnce(PathBuf) -> 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 { | |
| /// Attempts to open a file in read-only mode. | |
| /// | |
| /// Wrapper for [`File::open`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.open). | |
| pub fn open<P>(path: P) -> Result<Self, io::Error> | |
| where | |
| P: Into<PathBuf>, | |
| { | |
| let path = path.into(); | |
| match open(&path) { | |
| Ok(file) => Ok(File::from_parts(file, path)), | |
| Err(err_gen) => Err(err_gen(path)), | |
| } | |
| } | |
| /// Opens a file in write-only mode. | |
| /// | |
| /// Wrapper for [`File::create`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.create). | |
| pub fn create<P>(path: P) -> Result<Self, io::Error> | |
| where | |
| P: Into<PathBuf>, | |
| { | |
| 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<P>(path: P, options: &fs::OpenOptions) -> Result<Self, io::Error> | |
| where | |
| P: Into<PathBuf>, | |
| { | |
| 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)), | |
| } | |
| } | |
| /// Attempts to sync all OS-internal metadata to disk. | |
| /// | |
| /// 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)) | |
| } | |
| /// This function is similar to [`sync_all`], except that it might not synchronize file metadata to the filesystem. | |
| /// | |
| /// 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)) | |
| } | |
| /// Truncates or extends the underlying file, updating the size of this file to become `size`. | |
| /// | |
| /// 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)) | |
| } | |
| /// Queries metadata about the underlying file. | |
| /// | |
| /// Wrapper for [`File::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.metadata). | |
| pub fn metadata(&self) -> Result<fs::Metadata, io::Error> { | |
| self.file | |
| .metadata() | |
| .map_err(|source| self.error(source, 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 [`File::try_clone`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_clone). | |
| pub fn try_clone(&self) -> Result<Self, io::Error> { | |
| self.file | |
| .try_clone() | |
| .map(|file| File { | |
| file, | |
| path: self.path.clone(), | |
| }) | |
| .map_err(|source| self.error(source, ErrorKind::Clone)) | |
| } | |
| /// Changes the permissions on the underlying file. | |
| /// | |
| /// 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<P>(file: fs::File, path: P) -> Self | |
| where | |
| P: Into<PathBuf>, | |
| { | |
| 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<usize> { | |
| 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<usize> { | |
| 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<usize> { | |
| (&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<usize> { | |
| (&self.file) | |
| .read_vectored(bufs) | |
| .map_err(|source| self.error(source, ErrorKind::Read)) | |
| } | |
| } | |
| impl From<File> 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<u64> { | |
| 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<u64> { | |
| (&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<usize> { | |
| self.file | |
| .write(buf) | |
| .map_err(|source| self.error(source, ErrorKind::Write)) | |
| } | |
| fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { | |
| 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<usize> { | |
| (&self.file) | |
| .write(buf) | |
| .map_err(|source| self.error(source, ErrorKind::Write)) | |
| } | |
| fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { | |
| (&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<usize> { | |
| self.file() | |
| .read_at(buf, offset) | |
| .map_err(|err| self.error(err, ErrorKind::ReadAt)) | |
| } | |
| fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { | |
| 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<crate::File> 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<usize> { | |
| self.file() | |
| .seek_read(buf, offset) | |
| .map_err(|err| self.error(err, ErrorKind::SeekRead)) | |
| } | |
| fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> { | |
| 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<crate::File> for OwnedHandle { | |
| fn from(file: crate::File) -> Self { | |
| file.into_parts().0.into() | |
| } | |
| } | |
| } | |
| } |