tokio_fs_ext/fs/native/
file.rs

1use std::{
2    fs::Metadata,
3    io,
4    path::Path,
5    pin::Pin,
6    task::{Context, Poll, ready},
7};
8
9use pin_project_lite::pin_project;
10use tokio::{fs::OpenOptions, io::AsyncSeek};
11
12pin_project! {
13    #[derive(Debug)]
14    pub struct File {
15        #[pin]
16        pub(crate) inner: tokio::fs::File,
17        pub(crate) seek_pos: Option<io::SeekFrom>,
18    }
19}
20
21impl File {
22    pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
23        Ok(File {
24            inner: tokio::fs::File::create(path).await?,
25            seek_pos: None,
26        })
27    }
28
29    pub async fn create_new<P: AsRef<Path>>(path: P) -> std::io::Result<File> {
30        Ok(File {
31            inner: tokio::fs::File::create_new(path).await?,
32            seek_pos: None,
33        })
34    }
35
36    pub async fn metadata(&self) -> io::Result<Metadata> {
37        self.inner.metadata().await
38    }
39
40    pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
41        Ok(File {
42            inner: tokio::fs::File::open(path).await?,
43            seek_pos: None,
44        })
45    }
46
47    #[must_use]
48    pub fn options() -> OpenOptions {
49        OpenOptions::new()
50    }
51
52    pub async fn sync_all(&self) -> io::Result<()> {
53        self.inner.sync_all().await
54    }
55
56    pub async fn sync_data(&self) -> io::Result<()> {
57        self.inner.sync_data().await
58    }
59
60    /// Truncates or extends the underlying file, updating the size of this file to become `size`.
61    ///
62    /// If `size` is less than the current file's size, then the file will be shrunk. If it is greater
63    /// than the currrent file's size, then the file will be extended to `size` and have all intermediate
64    /// data filled with 0s.
65    ///
66    /// The file's cursor is not changed. In particular, if the cursor was at the end of the file and
67    /// the file was shrunk using this operation, the cursor will now be past the end.
68    pub async fn set_len(&self, size: u64) -> io::Result<()> {
69        self.inner.set_len(size).await
70    }
71}
72
73impl futures::io::AsyncRead for File {
74    fn poll_read(
75        self: Pin<&mut Self>,
76        cx: &mut Context<'_>,
77        buf: &mut [u8],
78    ) -> Poll<io::Result<usize>> {
79        let mut buf = tokio::io::ReadBuf::new(buf);
80        ready!(tokio::io::AsyncRead::poll_read(
81            self.project().inner,
82            cx,
83            &mut buf
84        ))?;
85        Poll::Ready(Ok(buf.filled().len()))
86    }
87}
88
89impl futures::io::AsyncWrite for File {
90    fn poll_write(
91        self: Pin<&mut Self>,
92        cx: &mut Context<'_>,
93        buf: &[u8],
94    ) -> Poll<io::Result<usize>> {
95        tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf)
96    }
97
98    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
99        tokio::io::AsyncWrite::poll_flush(self.project().inner, cx)
100    }
101
102    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
103        tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
104    }
105}
106
107impl futures::io::AsyncSeek for File {
108    fn poll_seek(
109        mut self: Pin<&mut Self>,
110        cx: &mut Context<'_>,
111        pos: io::SeekFrom,
112    ) -> Poll<io::Result<u64>> {
113        if self.seek_pos != Some(pos) {
114            ready!(self.as_mut().project().inner.poll_complete(cx))?;
115            self.as_mut().project().inner.start_seek(pos)?;
116            *self.as_mut().project().seek_pos = Some(pos);
117        }
118        let res = ready!(self.as_mut().project().inner.poll_complete(cx));
119        *self.as_mut().project().seek_pos = None;
120        Poll::Ready(res)
121    }
122}