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.
open(path: &Path) -> Result<std::fs::File, impl FnOnce(PathBuf) -> io::Error>19 pub(crate) fn open(path: &Path) -> Result<std::fs::File, impl FnOnce(PathBuf) -> io::Error> {
20     fs::File::open(path).map_err(|err| |path| Error::build(err, ErrorKind::OpenFile, path))
21 }
22 
23 // like `open()` but for `crate::write`
create(path: &Path) -> Result<std::fs::File, impl FnOnce(PathBuf) -> io::Error>24 pub(crate) fn create(path: &Path) -> Result<std::fs::File, impl FnOnce(PathBuf) -> io::Error> {
25     fs::File::create(path).map_err(|err| |path| Error::build(err, 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).
open<P>(path: P) -> Result<Self, io::Error> where P: Into<PathBuf>,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).
create<P>(path: P) -> Result<Self, io::Error> where P: Into<PathBuf>,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"]
from_options<P>(path: P, options: &fs::OpenOptions) -> Result<Self, io::Error> where P: Into<PathBuf>,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).
sync_all(&self) -> Result<(), io::Error>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).
sync_data(&self) -> Result<(), io::Error>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).
set_len(&self, size: u64) -> Result<(), io::Error>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).
metadata(&self) -> Result<fs::Metadata, io::Error>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).
try_clone(&self) -> Result<Self, io::Error>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).
set_permissions(&self, perm: fs::Permissions) -> Result<(), io::Error>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.
from_parts<P>(file: fs::File, path: P) -> Self where P: Into<PathBuf>,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)
into_parts(self) -> (fs::File, PathBuf)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
file(&self) -> &fs::File161     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
file_mut(&mut self) -> &mut fs::File168     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.
path(&self) -> &Path173     pub fn path(&self) -> &Path {
174         &self.path
175     }
176 
177     /// Wrap the error in information specific to this `File` object.
error(&self, source: io::Error, kind: ErrorKind) -> io::Error178     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 {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>184     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
185         self.file
186             .read(buf)
187             .map_err(|source| self.error(source, ErrorKind::Read))
188     }
189 
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>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| self.error(source, ErrorKind::Read))
194     }
195 }
196 
197 impl<'a> Read for &'a File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>198     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
199         (&self.file)
200             .read(buf)
201             .map_err(|source| self.error(source, ErrorKind::Read))
202     }
203 
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>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| self.error(source, ErrorKind::Read))
208     }
209 }
210 
211 impl From<File> for fs::File {
from(file: File) -> Self212     fn from(file: File) -> Self {
213         file.into_parts().0
214     }
215 }
216 
217 impl Seek for File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>218     fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
219         self.file
220             .seek(pos)
221             .map_err(|source| self.error(source, ErrorKind::Seek))
222     }
223 }
224 
225 impl<'a> Seek for &'a File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>226     fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
227         (&self.file)
228             .seek(pos)
229             .map_err(|source| self.error(source, ErrorKind::Seek))
230     }
231 }
232 
233 impl Write for File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>234     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
235         self.file
236             .write(buf)
237             .map_err(|source| self.error(source, ErrorKind::Write))
238     }
239 
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>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| self.error(source, ErrorKind::Write))
244     }
245 
flush(&mut self) -> std::io::Result<()>246     fn flush(&mut self) -> std::io::Result<()> {
247         self.file
248             .flush()
249             .map_err(|source| self.error(source, ErrorKind::Flush))
250     }
251 }
252 
253 impl<'a> Write for &'a File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>254     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
255         (&self.file)
256             .write(buf)
257             .map_err(|source| self.error(source, ErrorKind::Write))
258     }
259 
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>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| self.error(source, ErrorKind::Write))
264     }
265 
flush(&mut self) -> std::io::Result<()>266     fn flush(&mut self) -> std::io::Result<()> {
267         (&self.file)
268             .flush()
269             .map_err(|source| self.error(source, 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 {
as_raw_fd(&self) -> RawFd282         fn as_raw_fd(&self) -> RawFd {
283             self.file().as_raw_fd()
284         }
285     }
286 
287     impl IntoRawFd for crate::File {
into_raw_fd(self) -> RawFd288         fn into_raw_fd(self) -> RawFd {
289             self.file.into_raw_fd()
290         }
291     }
292 
293     impl FileExt for crate::File {
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>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         }
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>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 {
as_fd(&self) -> BorrowedFd<'_>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 {
from(file: crate::File) -> Self319             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 {
seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>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 
seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>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 {
as_raw_handle(&self) -> RawHandle351         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 {
into_raw_handle(self) -> RawHandle361         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 {
as_handle(&self) -> BorrowedHandle<'_>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 {
from(file: crate::File) -> Self379             fn from(file: crate::File) -> Self {
380                 file.into_parts().0.into()
381             }
382         }
383     }
384 }
385