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 /// 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>,119 pub fn from_parts<P>(file: fs::File, path: P) -> Self
120 where
121 P: Into<PathBuf>,
122 {
123 File {
124 file,
125 path: path.into(),
126 }
127 }
128 }
129
130 /// Methods added by fs-err that are not available on
131 /// [`std::fs::File`][std::fs::File].
132 ///
133 /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
134 impl File {
135 /// Returns a reference to the underlying [`std::fs::File`][std::fs::File].
136 ///
137 /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html
file(&self) -> &fs::File138 pub fn file(&self) -> &fs::File {
139 &self.file
140 }
141
142 /// Returns a reference to the path that this file was created with.
path(&self) -> &Path143 pub fn path(&self) -> &Path {
144 &self.path
145 }
146
147 /// Wrap the error in information specific to this `File` object.
error(&self, source: io::Error, kind: ErrorKind) -> io::Error148 fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
149 Error::new(source, kind, &self.path)
150 }
151 }
152
153 impl Read for File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>154 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
155 self.file
156 .read(buf)
157 .map_err(|source| self.error(source, ErrorKind::Read))
158 }
159
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>160 fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
161 self.file
162 .read_vectored(bufs)
163 .map_err(|source| self.error(source, ErrorKind::Read))
164 }
165 }
166
167 impl<'a> Read for &'a File {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>168 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
169 (&(**self).file)
170 .read(buf)
171 .map_err(|source| self.error(source, ErrorKind::Read))
172 }
173
read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize>174 fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
175 (&(**self).file)
176 .read_vectored(bufs)
177 .map_err(|source| self.error(source, ErrorKind::Read))
178 }
179 }
180
181 impl Seek for File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>182 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
183 self.file
184 .seek(pos)
185 .map_err(|source| self.error(source, ErrorKind::Seek))
186 }
187 }
188
189 impl<'a> Seek for &'a File {
seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64>190 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
191 (&(**self).file)
192 .seek(pos)
193 .map_err(|source| self.error(source, ErrorKind::Seek))
194 }
195 }
196
197 impl Write for File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>198 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
199 self.file
200 .write(buf)
201 .map_err(|source| self.error(source, ErrorKind::Write))
202 }
203
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>204 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
205 self.file
206 .write_vectored(bufs)
207 .map_err(|source| self.error(source, ErrorKind::Write))
208 }
209
flush(&mut self) -> std::io::Result<()>210 fn flush(&mut self) -> std::io::Result<()> {
211 self.file
212 .flush()
213 .map_err(|source| self.error(source, ErrorKind::Flush))
214 }
215 }
216
217 impl<'a> Write for &'a File {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>218 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
219 (&(**self).file)
220 .write(buf)
221 .map_err(|source| self.error(source, ErrorKind::Write))
222 }
223
write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize>224 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
225 (&(**self).file)
226 .write_vectored(bufs)
227 .map_err(|source| self.error(source, ErrorKind::Write))
228 }
229
flush(&mut self) -> std::io::Result<()>230 fn flush(&mut self) -> std::io::Result<()> {
231 (&(**self).file)
232 .flush()
233 .map_err(|source| self.error(source, ErrorKind::Flush))
234 }
235 }
236
237 #[cfg(unix)]
238 mod unix {
239 use crate::os::unix::fs::FileExt;
240 use crate::ErrorKind;
241 use std::io;
242 use std::os::unix::fs::FileExt as _;
243 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
244
245 impl AsRawFd for crate::File {
as_raw_fd(&self) -> RawFd246 fn as_raw_fd(&self) -> RawFd {
247 self.file().as_raw_fd()
248 }
249 }
250
251 impl IntoRawFd for crate::File {
into_raw_fd(self) -> RawFd252 fn into_raw_fd(self) -> RawFd {
253 self.file.into_raw_fd()
254 }
255 }
256
257 impl FileExt for crate::File {
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>258 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
259 self.file()
260 .read_at(buf, offset)
261 .map_err(|err| self.error(err, ErrorKind::ReadAt))
262 }
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>263 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
264 self.file()
265 .write_at(buf, offset)
266 .map_err(|err| self.error(err, ErrorKind::WriteAt))
267 }
268 }
269 }
270
271 #[cfg(windows)]
272 mod windows {
273 use crate::os::windows::fs::FileExt;
274 use crate::ErrorKind;
275 use std::io;
276 use std::os::windows::{
277 fs::FileExt as _,
278 io::{AsRawHandle, IntoRawHandle, RawHandle},
279 };
280
281 impl FileExt for crate::File {
seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>282 fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
283 self.file()
284 .seek_read(buf, offset)
285 .map_err(|err| self.error(err, ErrorKind::SeekRead))
286 }
287
seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>288 fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
289 self.file()
290 .seek_write(buf, offset)
291 .map_err(|err| self.error(err, ErrorKind::SeekWrite))
292 }
293 }
294
295 impl AsRawHandle for crate::File {
as_raw_handle(&self) -> RawHandle296 fn as_raw_handle(&self) -> RawHandle {
297 self.file().as_raw_handle()
298 }
299 }
300
301 // can't be implemented, because the trait doesn't give us a Path
302 // impl std::os::windows::io::FromRawHandle for crate::File {
303 // }
304
305 impl IntoRawHandle for crate::File {
into_raw_handle(self) -> RawHandle306 fn into_raw_handle(self) -> RawHandle {
307 self.file.into_raw_handle()
308 }
309 }
310 }
311