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