1use std::fs;
2use std::io::{self, Read, Seek, Write};
3use std::path::{Path, PathBuf};
4
5use 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)]
12pub 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.
19pub(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`
24pub(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
31impl 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
123impl 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
165impl 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
179impl<'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
193impl From<File> for fs::File {
194 fn from(file: File) -> Self {
195 file.into_parts().0
196 }
197}
198
199impl 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
207impl<'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
215impl 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
235impl<'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)]
256mod 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)]
309mod 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