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     /// 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>,119     pub fn from_parts<P>(file: fs::File, path: P) -> Self
120     where
121         P: Into<PathBuf>,
122     {
123         File {
124             file,
125             path: path.into(),
126         }
127     }
128 }
129 
130 /// Methods added by fs-err that are not available on
131 /// [`std::fs::File`][std::fs::File].
132 ///
133 /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
134 impl File {
135     /// Returns a reference to the underlying [`std::fs::File`][std::fs::File].
136     ///
137     /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
file(&self) -> &fs::File138     pub fn file(&self) -> &fs::File {
139         &self.file
140     }
141 
142     /// Returns a reference to the path that this file was created with.
path(&self) -> &Path143     pub fn path(&self) -> &Path {
144         &self.path
145     }
146 
147     /// Wrap the error in information specific to this `File` object.
error(&self, source: io::Error, kind: ErrorKind) -> io::Error148     fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
149         Error::new(source, kind, &self.path)
150     }
151 }
152 
153 impl Read for File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>154     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
155         self.file
156             .read(buf)
157             .map_err(|source| self.error(source, ErrorKind::Read))
158     }
159 
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>160     fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
161         self.file
162             .read_vectored(bufs)
163             .map_err(|source| self.error(source, ErrorKind::Read))
164     }
165 }
166 
167 impl<'a> Read for &'a File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>168     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
169         (&(**self).file)
170             .read(buf)
171             .map_err(|source| self.error(source, ErrorKind::Read))
172     }
173 
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>174     fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
175         (&(**self).file)
176             .read_vectored(bufs)
177             .map_err(|source| self.error(source, ErrorKind::Read))
178     }
179 }
180 
181 impl Seek for File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>182     fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
183         self.file
184             .seek(pos)
185             .map_err(|source| self.error(source, ErrorKind::Seek))
186     }
187 }
188 
189 impl<'a> Seek for &'a File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>190     fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
191         (&(**self).file)
192             .seek(pos)
193             .map_err(|source| self.error(source, ErrorKind::Seek))
194     }
195 }
196 
197 impl Write for File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>198     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
199         self.file
200             .write(buf)
201             .map_err(|source| self.error(source, ErrorKind::Write))
202     }
203 
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>204     fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
205         self.file
206             .write_vectored(bufs)
207             .map_err(|source| self.error(source, ErrorKind::Write))
208     }
209 
flush(&mut self) -> std::io::Result<()>210     fn flush(&mut self) -> std::io::Result<()> {
211         self.file
212             .flush()
213             .map_err(|source| self.error(source, ErrorKind::Flush))
214     }
215 }
216 
217 impl<'a> Write for &'a File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>218     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
219         (&(**self).file)
220             .write(buf)
221             .map_err(|source| self.error(source, ErrorKind::Write))
222     }
223 
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>224     fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
225         (&(**self).file)
226             .write_vectored(bufs)
227             .map_err(|source| self.error(source, ErrorKind::Write))
228     }
229 
flush(&mut self) -> std::io::Result<()>230     fn flush(&mut self) -> std::io::Result<()> {
231         (&(**self).file)
232             .flush()
233             .map_err(|source| self.error(source, ErrorKind::Flush))
234     }
235 }
236 
237 #[cfg(unix)]
238 mod unix {
239     use crate::os::unix::fs::FileExt;
240     use crate::ErrorKind;
241     use std::io;
242     use std::os::unix::fs::FileExt as _;
243     use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
244 
245     impl AsRawFd for crate::File {
as_raw_fd(&self) -> RawFd246         fn as_raw_fd(&self) -> RawFd {
247             self.file().as_raw_fd()
248         }
249     }
250 
251     impl IntoRawFd for crate::File {
into_raw_fd(self) -> RawFd252         fn into_raw_fd(self) -> RawFd {
253             self.file.into_raw_fd()
254         }
255     }
256 
257     impl FileExt for crate::File {
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>258         fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
259             self.file()
260                 .read_at(buf, offset)
261                 .map_err(|err| self.error(err, ErrorKind::ReadAt))
262         }
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>263         fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
264             self.file()
265                 .write_at(buf, offset)
266                 .map_err(|err| self.error(err, ErrorKind::WriteAt))
267         }
268     }
269 }
270 
271 #[cfg(windows)]
272 mod windows {
273     use crate::os::windows::fs::FileExt;
274     use crate::ErrorKind;
275     use std::io;
276     use std::os::windows::{
277         fs::FileExt as _,
278         io::{AsRawHandle, IntoRawHandle, RawHandle},
279     };
280 
281     impl FileExt for crate::File {
seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>282         fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
283             self.file()
284                 .seek_read(buf, offset)
285                 .map_err(|err| self.error(err, ErrorKind::SeekRead))
286         }
287 
seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>288         fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
289             self.file()
290                 .seek_write(buf, offset)
291                 .map_err(|err| self.error(err, ErrorKind::SeekWrite))
292         }
293     }
294 
295     impl AsRawHandle for crate::File {
as_raw_handle(&self) -> RawHandle296         fn as_raw_handle(&self) -> RawHandle {
297             self.file().as_raw_handle()
298         }
299     }
300 
301     // can't be implemented, because the trait doesn't give us a Path
302     // impl std::os::windows::io::FromRawHandle for crate::File {
303     // }
304 
305     impl IntoRawHandle for crate::File {
into_raw_handle(self) -> RawHandle306         fn into_raw_handle(self) -> RawHandle {
307             self.file.into_raw_handle()
308         }
309     }
310 }
311