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::new(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::new(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::new(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)) } /// 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(), } } } /// 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 { /// 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 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::new(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 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(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() } } }