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
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")))]
16pub struct File {
17    tokio: fs::File,
18    path: PathBuf,
19}
20
21impl File {
22    /// Attempts to open a file in read-only mode.
23    ///
24    /// Wrapper for [`tokio::fs::File::open`].
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`].
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`].
47    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`].
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`].
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`].
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`].
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`].
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`].
109    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`].
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`].
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`].
136impl File {
137    /// Creates a [`File`](struct.File.html) from a raw file and its path.
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).
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`].
154    pub fn file(&self) -> &TokioFile {
155        &self.tokio
156    }
157
158    /// Returns a mutable reference to the underlying [`tokio::fs::File`].
159    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.
164    pub fn path(&self) -> &Path {
165        &self.path
166    }
167
168    /// Wrap the error in information specific to this `File` object.
169    fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error {
170        Error::build(source, kind, &self.path)
171    }
172}
173
174impl From<crate::File> for File {
175    fn from(f: crate::File) -> Self {
176        let (f, path) = f.into_parts();
177        File::from_parts(f.into(), path)
178    }
179}
180
181impl From<File> for TokioFile {
182    fn from(f: File) -> Self {
183        f.into_parts().0
184    }
185}
186
187#[cfg(unix)]
188impl std::os::unix::io::AsRawFd for File {
189    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
190        self.tokio.as_raw_fd()
191    }
192}
193
194#[cfg(windows)]
195impl std::os::windows::io::AsRawHandle for File {
196    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
197        self.tokio.as_raw_handle()
198    }
199}
200
201impl AsyncRead for File {
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
214impl AsyncSeek for File {
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
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
229impl AsyncWrite for File {
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
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
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
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
266    fn is_write_vectored(&self) -> bool {
267        self.tokio.is_write_vectored()
268    }
269}