1 use crate::errors::{Error, ErrorKind};
2 use std::fs::{Metadata, Permissions};
3 use std::io;
4 use std::io::{IoSlice, SeekFrom};
5 use std::path::{Path, PathBuf};
6 use std::pin::Pin;
7 use std::task::{ready, Context, Poll};
8 use tokio::fs;
9 use tokio::fs::File as TokioFile;
10 use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
11 
12 /// Wrapper around [`tokio::fs::File`] which adds more helpful
13 /// information to all errors.
14 #[derive(Debug)]
15 #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
16 pub struct File {
17     tokio: fs::File,
18     path: PathBuf,
19 }
20 
21 impl File {
22     /// Attempts to open a file in read-only mode.
23     ///
24     /// Wrapper for [`tokio::fs::File::open`].
open(path: impl Into<PathBuf>) -> io::Result<File>25     pub async fn open(path: impl Into<PathBuf>) -> io::Result<File> {
26         let path = path.into();
27         let f = TokioFile::open(&path)
28             .await
29             .map_err(|err| Error::build(err, ErrorKind::OpenFile, &path))?;
30         Ok(File::from_parts(f, path))
31     }
32 
33     /// Opens a file in write-only mode.
34     ///
35     /// Wrapper for [`tokio::fs::File::create`].
create(path: impl Into<PathBuf>) -> io::Result<File>36     pub async fn create(path: impl Into<PathBuf>) -> io::Result<File> {
37         let path = path.into();
38         match TokioFile::create(&path).await {
39             Ok(f) => Ok(File::from_parts(f, path)),
40             Err(err) => Err(Error::build(err, ErrorKind::CreateFile, &path)),
41         }
42     }
43 
44     /// Converts a [`crate::File`] to a [`tokio::fs::File`].
45     ///
46     /// Wrapper for [`tokio::fs::File::from_std`].
from_std(std: crate::File) -> File47     pub fn from_std(std: crate::File) -> File {
48         let (std, path) = std.into_parts();
49         File::from_parts(TokioFile::from_std(std), path)
50     }
51 
52     /// Attempts to sync all OS-internal metadata to disk.
53     ///
54     /// Wrapper for [`tokio::fs::File::sync_all`].
sync_all(&self) -> io::Result<()>55     pub async fn sync_all(&self) -> io::Result<()> {
56         self.tokio
57             .sync_all()
58             .await
59             .map_err(|err| self.error(err, ErrorKind::SyncFile))
60     }
61 
62     /// This function is similar to `sync_all`, except that it may not
63     /// synchronize file metadata to the filesystem.
64     ///
65     /// Wrapper for [`tokio::fs::File::sync_data`].
sync_data(&self) -> io::Result<()>66     pub async fn sync_data(&self) -> io::Result<()> {
67         self.tokio
68             .sync_data()
69             .await
70             .map_err(|err| self.error(err, ErrorKind::SyncFile))
71     }
72 
73     /// Truncates or extends the underlying file, updating the size of this file to become size.
74     ///
75     /// Wrapper for [`tokio::fs::File::set_len`].
set_len(&self, size: u64) -> io::Result<()>76     pub async fn set_len(&self, size: u64) -> io::Result<()> {
77         self.tokio
78             .set_len(size)
79             .await
80             .map_err(|err| self.error(err, ErrorKind::SetLen))
81     }
82 
83     /// Queries metadata about the underlying file.
84     ///
85     /// Wrapper for [`tokio::fs::File::metadata`].
metadata(&self) -> io::Result<Metadata>86     pub async fn metadata(&self) -> io::Result<Metadata> {
87         self.tokio
88             .metadata()
89             .await
90             .map_err(|err| self.error(err, ErrorKind::Metadata))
91     }
92 
93     /// Creates a new `File` instance that shares the same underlying file handle
94     /// as the existing `File` instance. Reads, writes, and seeks will affect both
95     /// `File` instances simultaneously.
96     ///
97     /// Wrapper for [`tokio::fs::File::try_clone`].
try_clone(&self) -> io::Result<File>98     pub async fn try_clone(&self) -> io::Result<File> {
99         match self.tokio.try_clone().await {
100             Ok(file) => Ok(File::from_parts(file, self.path.clone())),
101             Err(err) => Err(self.error(err, ErrorKind::Clone)),
102         }
103     }
104 
105     /// Destructures `File` into a [`crate::File`]. This function is async to allow any
106     /// in-flight operations to complete.
107     ///
108     /// Wrapper for [`tokio::fs::File::into_std`].
into_std(self) -> crate::File109     pub async fn into_std(self) -> crate::File {
110         crate::File::from_parts(self.tokio.into_std().await, self.path)
111     }
112 
113     /// Tries to immediately destructure `File` into a [`crate::File`].
114     ///
115     /// Wrapper for [`tokio::fs::File::try_into_std`].
try_into_std(self) -> Result<crate::File, File>116     pub fn try_into_std(self) -> Result<crate::File, File> {
117         match self.tokio.try_into_std() {
118             Ok(f) => Ok(crate::File::from_parts(f, self.path)),
119             Err(f) => Err(File::from_parts(f, self.path)),
120         }
121     }
122 
123     /// Changes the permissions on the underlying file.
124     ///
125     /// Wrapper for [`tokio::fs::File::set_permissions`].
set_permissions(&self, perm: Permissions) -> io::Result<()>126     pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
127         self.tokio
128             .set_permissions(perm)
129             .await
130             .map_err(|err| self.error(err, ErrorKind::SetPermissions))
131     }
132 }
133 
134 /// Methods added by fs-err that are not available on
135 /// [`tokio::fs::File`].
136 impl File {
137     /// Creates a [`File`](struct.File.html) from a raw file and its path.
from_parts<P>(file: TokioFile, path: P) -> Self where P: Into<PathBuf>,138     pub fn from_parts<P>(file: TokioFile, path: P) -> Self
139     where
140         P: Into<PathBuf>,
141     {
142         File {
143             tokio: file,
144             path: path.into(),
145         }
146     }
147 
148     /// Extract the raw file and its path from this [`File`](struct.File.html).
into_parts(self) -> (TokioFile, PathBuf)149     pub fn into_parts(self) -> (TokioFile, PathBuf) {
150         (self.tokio, self.path)
151     }
152 
153     /// Returns a reference to the underlying [`tokio::fs::File`].
file(&self) -> &TokioFile154     pub fn file(&self) -> &TokioFile {
155         &self.tokio
156     }
157 
158     /// Returns a mutable reference to the underlying [`tokio::fs::File`].
file_mut(&mut self) -> &mut TokioFile159     pub fn file_mut(&mut self) -> &mut TokioFile {
160         &mut self.tokio
161     }
162 
163     /// Returns a reference to the path that this file was created with.
path(&self) -> &Path164     pub fn path(&self) -> &Path {
165         &self.path
166     }
167 
168     /// Wrap the error in information specific to this `File` object.
error(&self, source: io::Error, kind: ErrorKind) -> io::Error169     fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
170         Error::build(source, kind, &self.path)
171     }
172 }
173 
174 impl From<crate::File> for File {
from(f: crate::File) -> Self175     fn from(f: crate::File) -> Self {
176         let (f, path) = f.into_parts();
177         File::from_parts(f.into(), path)
178     }
179 }
180 
181 impl From<File> for TokioFile {
from(f: File) -> Self182     fn from(f: File) -> Self {
183         f.into_parts().0
184     }
185 }
186 
187 #[cfg(unix)]
188 impl std::os::unix::io::AsRawFd for File {
as_raw_fd(&self) -> std::os::unix::io::RawFd189     fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
190         self.tokio.as_raw_fd()
191     }
192 }
193 
194 #[cfg(windows)]
195 impl std::os::windows::io::AsRawHandle for File {
as_raw_handle(&self) -> std::os::windows::io::RawHandle196     fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
197         self.tokio.as_raw_handle()
198     }
199 }
200 
201 impl AsyncRead for File {
poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>202     fn poll_read(
203         mut self: Pin<&mut Self>,
204         cx: &mut Context<'_>,
205         buf: &mut ReadBuf<'_>,
206     ) -> Poll<io::Result<()>> {
207         Poll::Ready(
208             ready!(Pin::new(&mut self.tokio).poll_read(cx, buf))
209                 .map_err(|err| self.error(err, ErrorKind::Read)),
210         )
211     }
212 }
213 
214 impl AsyncSeek for File {
start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()>215     fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> {
216         Pin::new(&mut self.tokio)
217             .start_seek(position)
218             .map_err(|err| self.error(err, ErrorKind::Seek))
219     }
220 
poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>>221     fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
222         Poll::Ready(
223             ready!(Pin::new(&mut self.tokio).poll_complete(cx))
224                 .map_err(|err| self.error(err, ErrorKind::Seek)),
225         )
226     }
227 }
228 
229 impl AsyncWrite for File {
poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<io::Result<usize>>230     fn poll_write(
231         mut self: Pin<&mut Self>,
232         cx: &mut Context<'_>,
233         buf: &[u8],
234     ) -> Poll<io::Result<usize>> {
235         Poll::Ready(
236             ready!(Pin::new(&mut self.tokio).poll_write(cx, buf))
237                 .map_err(|err| self.error(err, ErrorKind::Write)),
238         )
239     }
240 
poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>241     fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
242         Poll::Ready(
243             ready!(Pin::new(&mut self.tokio).poll_flush(cx))
244                 .map_err(|err| self.error(err, ErrorKind::Flush)),
245         )
246     }
247 
poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>248     fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
249         Poll::Ready(
250             ready!(Pin::new(&mut self.tokio).poll_shutdown(cx))
251                 .map_err(|err| self.error(err, ErrorKind::Flush)),
252         )
253     }
254 
poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll<io::Result<usize>>255     fn poll_write_vectored(
256         mut self: Pin<&mut Self>,
257         cx: &mut Context<'_>,
258         bufs: &[IoSlice<'_>],
259     ) -> Poll<io::Result<usize>> {
260         Poll::Ready(
261             ready!(Pin::new(&mut self.tokio).poll_write_vectored(cx, bufs))
262                 .map_err(|err| self.error(err, ErrorKind::Write)),
263         )
264     }
265 
is_write_vectored(&self) -> bool266     fn is_write_vectored(&self) -> bool {
267         self.tokio.is_write_vectored()
268     }
269 }
270