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::new(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::new(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     /// 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>,33     pub fn open<P>(path: P) -> Result<Self, io::Error>
34     where
35         P: Into<PathBuf>,
36     {
37         let path = path.into();
38         match open(&path) {
39             Ok(file) => Ok(File::from_parts(file, path)),
40             Err(err_gen) => Err(err_gen(path)),
41         }
42     }
43 
44     /// 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>,45     pub fn create<P>(path: P) -> Result<Self, io::Error>
46     where
47         P: Into<PathBuf>,
48     {
49         let path = path.into();
50         match create(&path) {
51             Ok(file) => Ok(File::from_parts(file, path)),
52             Err(err_gen) => Err(err_gen(path)),
53         }
54     }
55 
56     /// Wrapper for [`OpenOptions::open`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html#method.open).
57     ///
58     /// This takes [`&std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html),
59     /// not [`crate::OpenOptions`].
60     #[deprecated = "use fs_err::OpenOptions::open instead"]
from_options<P>(path: P, options: &fs::OpenOptions) -> Result<Self, io::Error> where P: Into<PathBuf>,61     pub fn from_options<P>(path: P, options: &fs::OpenOptions) -> Result<Self, io::Error>
62     where
63         P: Into<PathBuf>,
64     {
65         let path = path.into();
66         match options.open(&path) {
67             Ok(file) => Ok(File::from_parts(file, path)),
68             Err(source) => Err(Error::new(source, ErrorKind::OpenFile, path)),
69         }
70     }
71 
72     /// 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>73     pub fn sync_all(&self) -> Result<(), io::Error> {
74         self.file
75             .sync_all()
76             .map_err(|source| self.error(source, ErrorKind::SyncFile))
77     }
78 
79     /// 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>80     pub fn sync_data(&self) -> Result<(), io::Error> {
81         self.file
82             .sync_data()
83             .map_err(|source| self.error(source, ErrorKind::SyncFile))
84     }
85 
86     /// 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>87     pub fn set_len(&self, size: u64) -> Result<(), io::Error> {
88         self.file
89             .set_len(size)
90             .map_err(|source| self.error(source, ErrorKind::SetLen))
91     }
92 
93     /// Wrapper for [`File::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.metadata).
metadata(&self) -> Result<fs::Metadata, io::Error>94     pub fn metadata(&self) -> Result<fs::Metadata, io::Error> {
95         self.file
96             .metadata()
97             .map_err(|source| self.error(source, ErrorKind::Metadata))
98     }
99 
100     /// 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>101     pub fn try_clone(&self) -> Result<Self, io::Error> {
102         self.file
103             .try_clone()
104             .map(|file| File {
105                 file,
106                 path: self.path.clone(),
107             })
108             .map_err(|source| self.error(source, ErrorKind::Clone))
109     }
110 
111     /// 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>112     pub fn set_permissions(&self, perm: fs::Permissions) -> Result<(), io::Error> {
113         self.file
114             .set_permissions(perm)
115             .map_err(|source| self.error(source, ErrorKind::SetPermissions))
116     }
117 }
118 
119 /// Methods added by fs-err that are not available on
120 /// [`std::fs::File`][std::fs::File].
121 ///
122 /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
123 impl File {
124     /// 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>,125     pub fn from_parts<P>(file: fs::File, path: P) -> Self
126     where
127         P: Into<PathBuf>,
128     {
129         File {
130             file,
131             path: path.into(),
132         }
133     }
134 
135     /// Extract the raw file and its path from this [`File`](struct.File.html)
into_parts(self) -> (fs::File, PathBuf)136     pub fn into_parts(self) -> (fs::File, PathBuf) {
137         (self.file, self.path)
138     }
139 
140     /// Returns a reference to the underlying [`std::fs::File`][std::fs::File].
141     ///
142     /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
file(&self) -> &fs::File143     pub fn file(&self) -> &fs::File {
144         &self.file
145     }
146 
147     /// Returns a mutable reference to the underlying [`std::fs::File`][std::fs::File].
148     ///
149     /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
file_mut(&mut self) -> &mut fs::File150     pub fn file_mut(&mut self) -> &mut fs::File {
151         &mut self.file
152     }
153 
154     /// Returns a reference to the path that this file was created with.
path(&self) -> &Path155     pub fn path(&self) -> &Path {
156         &self.path
157     }
158 
159     /// Wrap the error in information specific to this `File` object.
error(&self, source: io::Error, kind: ErrorKind) -> io::Error160     fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
161         Error::new(source, kind, &self.path)
162     }
163 }
164 
165 impl Read for File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>166     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
167         self.file
168             .read(buf)
169             .map_err(|source| self.error(source, ErrorKind::Read))
170     }
171 
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>172     fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
173         self.file
174             .read_vectored(bufs)
175             .map_err(|source| self.error(source, ErrorKind::Read))
176     }
177 }
178 
179 impl<'a> Read for &'a File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>180     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
181         (&(**self).file)
182             .read(buf)
183             .map_err(|source| self.error(source, ErrorKind::Read))
184     }
185 
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>186     fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
187         (&(**self).file)
188             .read_vectored(bufs)
189             .map_err(|source| self.error(source, ErrorKind::Read))
190     }
191 }
192 
193 impl Seek for File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>194     fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
195         self.file
196             .seek(pos)
197             .map_err(|source| self.error(source, ErrorKind::Seek))
198     }
199 }
200 
201 impl<'a> Seek for &'a File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>202     fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
203         (&(**self).file)
204             .seek(pos)
205             .map_err(|source| self.error(source, ErrorKind::Seek))
206     }
207 }
208 
209 impl Write for File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>210     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
211         self.file
212             .write(buf)
213             .map_err(|source| self.error(source, ErrorKind::Write))
214     }
215 
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>216     fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
217         self.file
218             .write_vectored(bufs)
219             .map_err(|source| self.error(source, ErrorKind::Write))
220     }
221 
flush(&mut self) -> std::io::Result<()>222     fn flush(&mut self) -> std::io::Result<()> {
223         self.file
224             .flush()
225             .map_err(|source| self.error(source, ErrorKind::Flush))
226     }
227 }
228 
229 impl<'a> Write for &'a File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>230     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
231         (&(**self).file)
232             .write(buf)
233             .map_err(|source| self.error(source, ErrorKind::Write))
234     }
235 
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>236     fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
237         (&(**self).file)
238             .write_vectored(bufs)
239             .map_err(|source| self.error(source, ErrorKind::Write))
240     }
241 
flush(&mut self) -> std::io::Result<()>242     fn flush(&mut self) -> std::io::Result<()> {
243         (&(**self).file)
244             .flush()
245             .map_err(|source| self.error(source, ErrorKind::Flush))
246     }
247 }
248 
249 #[cfg(unix)]
250 mod unix {
251     use crate::os::unix::fs::FileExt;
252     use crate::ErrorKind;
253     use std::io;
254     use std::os::unix::fs::FileExt as _;
255     use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
256 
257     impl AsRawFd for crate::File {
as_raw_fd(&self) -> RawFd258         fn as_raw_fd(&self) -> RawFd {
259             self.file().as_raw_fd()
260         }
261     }
262 
263     impl IntoRawFd for crate::File {
into_raw_fd(self) -> RawFd264         fn into_raw_fd(self) -> RawFd {
265             self.file.into_raw_fd()
266         }
267     }
268 
269     impl FileExt for crate::File {
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>270         fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
271             self.file()
272                 .read_at(buf, offset)
273                 .map_err(|err| self.error(err, ErrorKind::ReadAt))
274         }
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>275         fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
276             self.file()
277                 .write_at(buf, offset)
278                 .map_err(|err| self.error(err, ErrorKind::WriteAt))
279         }
280     }
281 }
282 
283 #[cfg(windows)]
284 mod windows {
285     use crate::os::windows::fs::FileExt;
286     use crate::ErrorKind;
287     use std::io;
288     use std::os::windows::{
289         fs::FileExt as _,
290         io::{AsRawHandle, IntoRawHandle, RawHandle},
291     };
292 
293     impl FileExt for crate::File {
seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>294         fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
295             self.file()
296                 .seek_read(buf, offset)
297                 .map_err(|err| self.error(err, ErrorKind::SeekRead))
298         }
299 
seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>300         fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
301             self.file()
302                 .seek_write(buf, offset)
303                 .map_err(|err| self.error(err, ErrorKind::SeekWrite))
304         }
305     }
306 
307     impl AsRawHandle for crate::File {
as_raw_handle(&self) -> RawHandle308         fn as_raw_handle(&self) -> RawHandle {
309             self.file().as_raw_handle()
310         }
311     }
312 
313     // can't be implemented, because the trait doesn't give us a Path
314     // impl std::os::windows::io::FromRawHandle for crate::File {
315     // }
316 
317     impl IntoRawHandle for crate::File {
into_raw_handle(self) -> RawHandle318         fn into_raw_handle(self) -> RawHandle {
319             self.file.into_raw_handle()
320         }
321     }
322 }
323