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 | /// Wrapper for [`File::open`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.open).
|
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).
|
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" ]
|
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::build(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).
|
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).
|
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).
|
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).
|
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).
|
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).
|
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.
|
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)
|
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
|
143 | 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
|
150 | 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.
|
155 | pub fn path(&self) -> &Path {
|
156 | &self.path
|
157 | }
|
158 |
|
159 | /// Wrap the error in information specific to this `File` object.
|
160 | fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
|
161 | Error::build(source, kind, &self.path)
|
162 | }
|
163 | }
|
164 |
|
165 | impl Read for File {
|
166 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
167 | self.file
|
168 | .read(buf)
|
169 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Read))
|
170 | }
|
171 |
|
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: Error| self.error(source, kind:ErrorKind::Read))
|
176 | }
|
177 | }
|
178 |
|
179 | impl<'a> Read for &'a File {
|
180 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
181 | (&(**self).file)
|
182 | .read(buf)
|
183 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Read))
|
184 | }
|
185 |
|
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: Error| self.error(source, kind:ErrorKind::Read))
|
190 | }
|
191 | }
|
192 |
|
193 | impl From<File> for fs::File {
|
194 | fn from(file: File) -> Self {
|
195 | file.into_parts().0
|
196 | }
|
197 | }
|
198 |
|
199 | impl Seek for File {
|
200 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
201 | self.file
|
202 | .seek(pos)
|
203 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Seek))
|
204 | }
|
205 | }
|
206 |
|
207 | impl<'a> Seek for &'a File {
|
208 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
209 | (&(**self).file)
|
210 | .seek(pos)
|
211 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Seek))
|
212 | }
|
213 | }
|
214 |
|
215 | impl Write for File {
|
216 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
217 | self.file
|
218 | .write(buf)
|
219 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
220 | }
|
221 |
|
222 | fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
|
223 | self.file
|
224 | .write_vectored(bufs)
|
225 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
226 | }
|
227 |
|
228 | fn flush(&mut self) -> std::io::Result<()> {
|
229 | self.file
|
230 | .flush()
|
231 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Flush))
|
232 | }
|
233 | }
|
234 |
|
235 | impl<'a> Write for &'a File {
|
236 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
237 | (&(**self).file)
|
238 | .write(buf)
|
239 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
240 | }
|
241 |
|
242 | fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
|
243 | (&(**self).file)
|
244 | .write_vectored(bufs)
|
245 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Write))
|
246 | }
|
247 |
|
248 | fn flush(&mut self) -> std::io::Result<()> {
|
249 | (&(**self).file)
|
250 | .flush()
|
251 | .map_err(|source: Error| self.error(source, kind:ErrorKind::Flush))
|
252 | }
|
253 | }
|
254 |
|
255 | #[cfg (unix)]
|
256 | mod unix {
|
257 | use crate::os::unix::fs::FileExt;
|
258 | use crate::ErrorKind;
|
259 | use std::io;
|
260 | use std::os::unix::fs::FileExt as _;
|
261 | use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
262 |
|
263 | impl AsRawFd for crate::File {
|
264 | fn as_raw_fd(&self) -> RawFd {
|
265 | self.file().as_raw_fd()
|
266 | }
|
267 | }
|
268 |
|
269 | impl IntoRawFd for crate::File {
|
270 | fn into_raw_fd(self) -> RawFd {
|
271 | self.file.into_raw_fd()
|
272 | }
|
273 | }
|
274 |
|
275 | impl FileExt for crate::File {
|
276 | fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
277 | self.file()
|
278 | .read_at(buf, offset)
|
279 | .map_err(|err| self.error(err, ErrorKind::ReadAt))
|
280 | }
|
281 | fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
282 | self.file()
|
283 | .write_at(buf, offset)
|
284 | .map_err(|err| self.error(err, ErrorKind::WriteAt))
|
285 | }
|
286 | }
|
287 |
|
288 | #[cfg (feature = "io_safety" )]
|
289 | mod io_safety {
|
290 | use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
|
291 |
|
292 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
293 | impl AsFd for crate::File {
|
294 | fn as_fd(&self) -> BorrowedFd<'_> {
|
295 | self.file().as_fd()
|
296 | }
|
297 | }
|
298 |
|
299 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
300 | impl From<crate::File> for OwnedFd {
|
301 | fn from(file: crate::File) -> Self {
|
302 | file.into_parts().0.into()
|
303 | }
|
304 | }
|
305 | }
|
306 | }
|
307 |
|
308 | #[cfg (windows)]
|
309 | mod windows {
|
310 | use crate::os::windows::fs::FileExt;
|
311 | use crate::ErrorKind;
|
312 | use std::io;
|
313 | use std::os::windows::{
|
314 | fs::FileExt as _,
|
315 | io::{AsRawHandle, IntoRawHandle, RawHandle},
|
316 | };
|
317 |
|
318 | impl FileExt for crate::File {
|
319 | fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
320 | self.file()
|
321 | .seek_read(buf, offset)
|
322 | .map_err(|err| self.error(err, ErrorKind::SeekRead))
|
323 | }
|
324 |
|
325 | fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
326 | self.file()
|
327 | .seek_write(buf, offset)
|
328 | .map_err(|err| self.error(err, ErrorKind::SeekWrite))
|
329 | }
|
330 | }
|
331 |
|
332 | impl AsRawHandle for crate::File {
|
333 | fn as_raw_handle(&self) -> RawHandle {
|
334 | self.file().as_raw_handle()
|
335 | }
|
336 | }
|
337 |
|
338 | // can't be implemented, because the trait doesn't give us a Path
|
339 | // impl std::os::windows::io::FromRawHandle for crate::File {
|
340 | // }
|
341 |
|
342 | impl IntoRawHandle for crate::File {
|
343 | fn into_raw_handle(self) -> RawHandle {
|
344 | self.file.into_raw_handle()
|
345 | }
|
346 | }
|
347 |
|
348 | #[cfg (feature = "io_safety" )]
|
349 | mod io_safety {
|
350 | use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};
|
351 |
|
352 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
353 | impl AsHandle for crate::File {
|
354 | fn as_handle(&self) -> BorrowedHandle<'_> {
|
355 | self.file().as_handle()
|
356 | }
|
357 | }
|
358 |
|
359 | #[cfg_attr (docsrs, doc(cfg(feature = "io_safety" )))]
|
360 | impl From<crate::File> for OwnedHandle {
|
361 | fn from(file: crate::File) -> Self {
|
362 | file.into_parts().0.into()
|
363 | }
|
364 | }
|
365 | }
|
366 | }
|
367 | |