fs_err/tokio/
file.rs

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