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 | |