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
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.
from_parts<P>(file: fs::File, path: P) -> Self where P: Into<PathBuf>,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)
into_parts(self) -> (fs::File, PathBuf)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
file(&self) -> &fs::File143 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
file_mut(&mut self) -> &mut fs::File150 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.
path(&self) -> &Path155 pub fn path(&self) -> &Path {
156 &self.path
157 }
158
159 /// Wrap the error in information specific to this `File` object.
error(&self, source: io::Error, kind: ErrorKind) -> io::Error160 fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
161 Error::new(source, kind, &self.path)
162 }
163 }
164
165 impl Read for File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>166 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
167 self.file
168 .read(buf)
169 .map_err(|source| self.error(source, ErrorKind::Read))
170 }
171
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>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| self.error(source, ErrorKind::Read))
176 }
177 }
178
179 impl<'a> Read for &'a File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>180 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
181 (&(**self).file)
182 .read(buf)
183 .map_err(|source| self.error(source, ErrorKind::Read))
184 }
185
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>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| self.error(source, ErrorKind::Read))
190 }
191 }
192
193 impl Seek for File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>194 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
195 self.file
196 .seek(pos)
197 .map_err(|source| self.error(source, ErrorKind::Seek))
198 }
199 }
200
201 impl<'a> Seek for &'a File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>202 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
203 (&(**self).file)
204 .seek(pos)
205 .map_err(|source| self.error(source, ErrorKind::Seek))
206 }
207 }
208
209 impl Write for File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>210 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
211 self.file
212 .write(buf)
213 .map_err(|source| self.error(source, ErrorKind::Write))
214 }
215
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>216 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
217 self.file
218 .write_vectored(bufs)
219 .map_err(|source| self.error(source, ErrorKind::Write))
220 }
221
flush(&mut self) -> std::io::Result<()>222 fn flush(&mut self) -> std::io::Result<()> {
223 self.file
224 .flush()
225 .map_err(|source| self.error(source, ErrorKind::Flush))
226 }
227 }
228
229 impl<'a> Write for &'a File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>230 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
231 (&(**self).file)
232 .write(buf)
233 .map_err(|source| self.error(source, ErrorKind::Write))
234 }
235
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>236 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
237 (&(**self).file)
238 .write_vectored(bufs)
239 .map_err(|source| self.error(source, ErrorKind::Write))
240 }
241
flush(&mut self) -> std::io::Result<()>242 fn flush(&mut self) -> std::io::Result<()> {
243 (&(**self).file)
244 .flush()
245 .map_err(|source| self.error(source, ErrorKind::Flush))
246 }
247 }
248
249 #[cfg(unix)]
250 mod unix {
251 use crate::os::unix::fs::FileExt;
252 use crate::ErrorKind;
253 use std::io;
254 use std::os::unix::fs::FileExt as _;
255 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
256
257 impl AsRawFd for crate::File {
as_raw_fd(&self) -> RawFd258 fn as_raw_fd(&self) -> RawFd {
259 self.file().as_raw_fd()
260 }
261 }
262
263 impl IntoRawFd for crate::File {
into_raw_fd(self) -> RawFd264 fn into_raw_fd(self) -> RawFd {
265 self.file.into_raw_fd()
266 }
267 }
268
269 impl FileExt for crate::File {
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>270 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
271 self.file()
272 .read_at(buf, offset)
273 .map_err(|err| self.error(err, ErrorKind::ReadAt))
274 }
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>275 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
276 self.file()
277 .write_at(buf, offset)
278 .map_err(|err| self.error(err, ErrorKind::WriteAt))
279 }
280 }
281 }
282
283 #[cfg(windows)]
284 mod windows {
285 use crate::os::windows::fs::FileExt;
286 use crate::ErrorKind;
287 use std::io;
288 use std::os::windows::{
289 fs::FileExt as _,
290 io::{AsRawHandle, IntoRawHandle, RawHandle},
291 };
292
293 impl FileExt for crate::File {
seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>294 fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
295 self.file()
296 .seek_read(buf, offset)
297 .map_err(|err| self.error(err, ErrorKind::SeekRead))
298 }
299
seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>300 fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
301 self.file()
302 .seek_write(buf, offset)
303 .map_err(|err| self.error(err, ErrorKind::SeekWrite))
304 }
305 }
306
307 impl AsRawHandle for crate::File {
as_raw_handle(&self) -> RawHandle308 fn as_raw_handle(&self) -> RawHandle {
309 self.file().as_raw_handle()
310 }
311 }
312
313 // can't be implemented, because the trait doesn't give us a Path
314 // impl std::os::windows::io::FromRawHandle for crate::File {
315 // }
316
317 impl IntoRawHandle for crate::File {
into_raw_handle(self) -> RawHandle318 fn into_raw_handle(self) -> RawHandle {
319 self.file.into_raw_handle()
320 }
321 }
322 }
323