| 1 | use std::fs;
|
| 2 | use std::io::{self, Read, Seek, Write};
|
| 3 | use std::path::{Path, PathBuf};
|
| 4 |
|
| 5 | use crate::errors::{Error, ErrorKind};
|
| 6 |
|
| 7 | /// Wrapper around [`std::fs::File`][std::fs::File] which adds more helpful
|
| 8 | /// information to all errors.
|
| 9 | ///
|
| 10 | /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
|
| 11 | #[derive (Debug)]
|
| 12 | pub struct File {
|
| 13 | file: fs::File,
|
| 14 | path: PathBuf,
|
| 15 | }
|
| 16 |
|
| 17 | // Opens a std File and returns it or an error generator which only needs the path to produce the error.
|
| 18 | // Exists for the `crate::read*` functions so they don't unconditionally build a PathBuf.
|
| 19 | pub(crate) fn open(path: &Path) -> Result<std::fs::File, impl FnOnce(PathBuf) -> io::Error> {
|
| 20 | fs::File::open(path).map_err(|err: Error| |path: PathBuf| Error::build(source:err, kind:ErrorKind::OpenFile, path))
|
| 21 | }
|
| 22 |
|
| 23 | // like `open()` but for `crate::write`
|
| 24 | pub(crate) fn create(path: &Path) -> Result<std::fs::File, impl FnOnce(PathBuf) -> io::Error> {
|
| 25 | fs::File::create(path).map_err(|err: Error| |path: PathBuf| Error::build(source:err, kind:ErrorKind::CreateFile, path))
|
| 26 | }
|
| 27 |
|
| 28 | /// Wrappers for methods from [`std::fs::File`][std::fs::File].
|
| 29 | ///
|
| 30 | /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
|
| 31 | impl File {
|
| 32 | /// Attempts to open a file in read-only mode.
|
| 33 | ///
|
| 34 | /// Wrapper for [`File::open`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.open).
|
| 35 | pub fn open<P>(path: P) -> Result<Self, io::Error>
|
| 36 | where
|
| 37 | P: Into<PathBuf>,
|
| 38 | {
|
| 39 | let path = path.into();
|
| 40 | match open(&path) {
|
| 41 | Ok(file) => Ok(File::from_parts(file, path)),
|
| 42 | Err(err_gen) => Err(err_gen(path)),
|
| 43 | }
|
| 44 | }
|
| 45 |
|
| 46 | /// Opens a file in write-only mode.
|
| 47 | ///
|
| 48 | /// Wrapper for [`File::create`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.create).
|
| 49 | pub fn create<P>(path: P) -> Result<Self, io::Error>
|
| 50 | where
|
| 51 | P: Into<PathBuf>,
|
| 52 | {
|
| 53 | let path = path.into();
|
| 54 | match create(&path) {
|
| 55 | Ok(file) => Ok(File::from_parts(file, path)),
|
| 56 | Err(err_gen) => Err(err_gen(path)),
|
| 57 | }
|
| 58 | }
|
| 59 |
|
| 60 | /// Wrapper for [`OpenOptions::open`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html#method.open).
|
| 61 | ///
|
| 62 | /// This takes [`&std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html),
|
| 63 | /// not [`crate::OpenOptions`].
|
| 64 | #[deprecated = "use fs_err::OpenOptions::open instead" ]
|
| 65 | pub fn from_options<P>(path: P, options: &fs::OpenOptions) -> Result<Self, io::Error>
|
| 66 | where
|
| 67 | P: Into<PathBuf>,
|
| 68 | {
|
| 69 | let path = path.into();
|
| 70 | match options.open(&path) {
|
| 71 | Ok(file) => Ok(File::from_parts(file, path)),
|
| 72 | Err(source) => Err(Error::build(source, ErrorKind::OpenFile, path)),
|
| 73 | }
|
| 74 | }
|
| 75 |
|
| 76 | /// Attempts to sync all OS-internal metadata to disk.
|
| 77 | ///
|
| 78 | /// Wrapper for [`File::sync_all`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.sync_all).
|
| 79 | pub fn sync_all(&self) -> Result<(), io::Error> {
|
| 80 | self.file
|
| 81 | .sync_all()
|
| 82 | .map_err(|source| self.error(source, ErrorKind::SyncFile))
|
| 83 | }
|
| 84 |
|
| 85 | /// This function is similar to [`sync_all`], except that it might not synchronize file metadata to the filesystem.
|
| 86 | ///
|
| 87 | /// Wrapper for [`File::sync_data`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.sync_data).
|
| 88 | pub fn sync_data(&self) -> Result<(), io::Error> {
|
| 89 | self.file
|
| 90 | .sync_data()
|
| 91 | .map_err(|source| self.error(source, ErrorKind::SyncFile))
|
| 92 | }
|
| 93 |
|
| 94 | /// Truncates or extends the underlying file, updating the size of this file to become `size`.
|
| 95 | ///
|
| 96 | /// Wrapper for [`File::set_len`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_len).
|
| 97 | pub fn set_len(&self, size: u64) -> Result<(), io::Error> {
|
| 98 | self.file
|
| 99 | .set_len(size)
|
| 100 | .map_err(|source| self.error(source, ErrorKind::SetLen))
|
| 101 | }
|
| 102 |
|
| 103 | /// Queries metadata about the underlying file.
|
| 104 | ///
|
| 105 | /// Wrapper for [`File::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.metadata).
|
| 106 | pub fn metadata(&self) -> Result<fs::Metadata, io::Error> {
|
| 107 | self.file
|
| 108 | .metadata()
|
| 109 | .map_err(|source| self.error(source, ErrorKind::Metadata))
|
| 110 | }
|
| 111 |
|
| 112 | /// Creates a new `File` instance that shares the same underlying file handle as the
|
| 113 | /// existing `File` instance. Reads, writes, and seeks will affect both `File`
|
| 114 | /// instances simultaneously.
|
| 115 | ///
|
| 116 | /// Wrapper for [`File::try_clone`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_clone).
|
| 117 | pub fn try_clone(&self) -> Result<Self, io::Error> {
|
| 118 | self.file
|
| 119 | .try_clone()
|
| 120 | .map(|file| File {
|
| 121 | file,
|
| 122 | path: self.path.clone(),
|
| 123 | })
|
| 124 | .map_err(|source| self.error(source, ErrorKind::Clone))
|
| 125 | }
|
| 126 |
|
| 127 | /// Changes the permissions on the underlying file.
|
| 128 | ///
|
| 129 | /// Wrapper for [`File::set_permissions`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_permissions).
|
| 130 | pub fn set_permissions(&self, perm: fs::Permissions) -> Result<(), io::Error> {
|
| 131 | self.file
|
| 132 | .set_permissions(perm)
|
| 133 | .map_err(|source| self.error(source, ErrorKind::SetPermissions))
|
| 134 | }
|
| 135 | }
|
| 136 |
|
| 137 | /// Methods added by fs-err that are not available on
|
| 138 | /// [`std::fs::File`][std::fs::File].
|
| 139 | ///
|
| 140 | /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
|
| 141 | impl File {
|
| 142 | /// Creates a [`File`](struct.File.html) from a raw file and its path.
|
| 143 | pub fn from_parts<P>(file: fs::File, path: P) -> Self
|
| 144 | where
|
| 145 | P: Into<PathBuf>,
|
| 146 | {
|
| 147 | File {
|
| 148 | file,
|
| 149 | path: path.into(),
|
| 150 | }
|
| 151 | }
|
| 152 |
|
| 153 | /// Extract the raw file and its path from this [`File`](struct.File.html)
|
| 154 | pub fn into_parts(self) -> (fs::File, PathBuf) {
|
| 155 | (self.file, self.path)
|
| 156 | }
|
| 157 |
|
| 158 | /// Returns a reference to the underlying [`std::fs::File`][std::fs::File].
|
| 159 | ///
|
| 160 | /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
|
| 161 | pub fn file(&self) -> &fs::File {
|
| 162 | &self.file
|
| 163 | }
|
| 164 |
|
| 165 | /// Returns a mutable reference to the underlying [`std::fs::File`][std::fs::File].
|
| 166 | ///
|
| 167 | /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
|
| 168 | pub fn file_mut(&mut self) -> &mut fs::File {
|
| 169 | &mut self.file
|
| 170 | }
|
| 171 |
|
| 172 | /// Returns a reference to the path that this file was created with.
|
| 173 | pub fn path(&self) -> &Path {
|
| 174 | &self.path
|
| 175 | }
|
| 176 |
|
| 177 | /// Wrap the error in information specific to this `File` object.
|
| 178 | fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
|
| 179 | Error::build(source, kind, &self.path)
|
| 180 | }
|
| 181 | }
|
| 182 |
|
| 183 | impl Read for File {
|
| 184 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
| 185 | self.file
|
| 186 | .read(buf)
|
| 187 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Read))
|
| 188 | }
|
| 189 |
|
| 190 | fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
|
| 191 | self.file
|
| 192 | .read_vectored(bufs)
|
| 193 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Read))
|
| 194 | }
|
| 195 | }
|
| 196 |
|
| 197 | impl<'a> Read for &'a File {
|
| 198 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
| 199 | (&self.file)
|
| 200 | .read(buf)
|
| 201 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Read))
|
| 202 | }
|
| 203 |
|
| 204 | fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
|
| 205 | (&self.file)
|
| 206 | .read_vectored(bufs)
|
| 207 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Read))
|
| 208 | }
|
| 209 | }
|
| 210 |
|
| 211 | impl From<File> for fs::File {
|
| 212 | fn from(file: File) -> Self {
|
| 213 | file.into_parts().0
|
| 214 | }
|
| 215 | }
|
| 216 |
|
| 217 | impl Seek for File {
|
| 218 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
| 219 | self.file
|
| 220 | .seek(pos)
|
| 221 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Seek))
|
| 222 | }
|
| 223 | }
|
| 224 |
|
| 225 | impl<'a> Seek for &'a File {
|
| 226 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
| 227 | (&self.file)
|
| 228 | .seek(pos)
|
| 229 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Seek))
|
| 230 | }
|
| 231 | }
|
| 232 |
|
| 233 | impl Write for File {
|
| 234 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
| 235 | self.file
|
| 236 | .write(buf)
|
| 237 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
| 238 | }
|
| 239 |
|
| 240 | fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
|
| 241 | self.file
|
| 242 | .write_vectored(bufs)
|
| 243 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
| 244 | }
|
| 245 |
|
| 246 | fn flush(&mut self) -> std::io::Result<()> {
|
| 247 | self.file
|
| 248 | .flush()
|
| 249 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Flush))
|
| 250 | }
|
| 251 | }
|
| 252 |
|
| 253 | impl<'a> Write for &'a File {
|
| 254 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
| 255 | (&self.file)
|
| 256 | .write(buf)
|
| 257 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
| 258 | }
|
| 259 |
|
| 260 | fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
|
| 261 | (&self.file)
|
| 262 | .write_vectored(bufs)
|
| 263 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
| 264 | }
|
| 265 |
|
| 266 | fn flush(&mut self) -> std::io::Result<()> {
|
| 267 | (&self.file)
|
| 268 | .flush()
|
| 269 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Flush))
|
| 270 | }
|
| 271 | }
|
| 272 |
|
| 273 | #[cfg (unix)]
|
| 274 | mod unix {
|
| 275 | use crate::os::unix::fs::FileExt;
|
| 276 | use crate::ErrorKind;
|
| 277 | use std::io;
|
| 278 | use std::os::unix::fs::FileExt as _;
|
| 279 | use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
| 280 |
|
| 281 | impl AsRawFd for crate::File {
|
| 282 | fn as_raw_fd(&self) -> RawFd {
|
| 283 | self.file().as_raw_fd()
|
| 284 | }
|
| 285 | }
|
| 286 |
|
| 287 | impl IntoRawFd for crate::File {
|
| 288 | fn into_raw_fd(self) -> RawFd {
|
| 289 | self.file.into_raw_fd()
|
| 290 | }
|
| 291 | }
|
| 292 |
|
| 293 | impl FileExt for crate::File {
|
| 294 | fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
| 295 | self.file()
|
| 296 | .read_at(buf, offset)
|
| 297 | .map_err(|err| self.error(err, ErrorKind::ReadAt))
|
| 298 | }
|
| 299 | fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
| 300 | self.file()
|
| 301 | .write_at(buf, offset)
|
| 302 | .map_err(|err| self.error(err, ErrorKind::WriteAt))
|
| 303 | }
|
| 304 | }
|
| 305 |
|
| 306 | #[cfg (feature = "io_safety" )]
|
| 307 | mod io_safety {
|
| 308 | use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
|
| 309 |
|
| 310 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
| 311 | impl AsFd for crate::File {
|
| 312 | fn as_fd(&self) -> BorrowedFd<'_> {
|
| 313 | self.file().as_fd()
|
| 314 | }
|
| 315 | }
|
| 316 |
|
| 317 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
| 318 | impl From<crate::File> for OwnedFd {
|
| 319 | fn from(file: crate::File) -> Self {
|
| 320 | file.into_parts().0.into()
|
| 321 | }
|
| 322 | }
|
| 323 | }
|
| 324 | }
|
| 325 |
|
| 326 | #[cfg (windows)]
|
| 327 | mod windows {
|
| 328 | use crate::os::windows::fs::FileExt;
|
| 329 | use crate::ErrorKind;
|
| 330 | use std::io;
|
| 331 | use std::os::windows::{
|
| 332 | fs::FileExt as _,
|
| 333 | io::{AsRawHandle, IntoRawHandle, RawHandle},
|
| 334 | };
|
| 335 |
|
| 336 | impl FileExt for crate::File {
|
| 337 | fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
| 338 | self.file()
|
| 339 | .seek_read(buf, offset)
|
| 340 | .map_err(|err| self.error(err, ErrorKind::SeekRead))
|
| 341 | }
|
| 342 |
|
| 343 | fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
| 344 | self.file()
|
| 345 | .seek_write(buf, offset)
|
| 346 | .map_err(|err| self.error(err, ErrorKind::SeekWrite))
|
| 347 | }
|
| 348 | }
|
| 349 |
|
| 350 | impl AsRawHandle for crate::File {
|
| 351 | fn as_raw_handle(&self) -> RawHandle {
|
| 352 | self.file().as_raw_handle()
|
| 353 | }
|
| 354 | }
|
| 355 |
|
| 356 | // can't be implemented, because the trait doesn't give us a Path
|
| 357 | // impl std::os::windows::io::FromRawHandle for crate::File {
|
| 358 | // }
|
| 359 |
|
| 360 | impl IntoRawHandle for crate::File {
|
| 361 | fn into_raw_handle(self) -> RawHandle {
|
| 362 | self.file.into_raw_handle()
|
| 363 | }
|
| 364 | }
|
| 365 |
|
| 366 | #[cfg (feature = "io_safety" )]
|
| 367 | mod io_safety {
|
| 368 | use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};
|
| 369 |
|
| 370 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
| 371 | impl AsHandle for crate::File {
|
| 372 | fn as_handle(&self) -> BorrowedHandle<'_> {
|
| 373 | self.file().as_handle()
|
| 374 | }
|
| 375 | }
|
| 376 |
|
| 377 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
| 378 | impl From<crate::File> for OwnedHandle {
|
| 379 | fn from(file: crate::File) -> Self {
|
| 380 | file.into_parts().0.into()
|
| 381 | }
|
| 382 | }
|
| 383 | }
|
| 384 | }
|
| 385 | |