Skip to main content

async_fs_io/
async_file.rs

1//! Async low-level file handle.
2
3use std::io::SeekFrom;
4use std::path::{
5    Path,
6    PathBuf,
7};
8use std::pin::Pin;
9use std::task::{
10    Context,
11    Poll,
12};
13
14use tokio::io::{
15    AsyncRead,
16    AsyncReadExt,
17    AsyncSeekExt,
18    AsyncWrite,
19    AsyncWriteExt,
20    ReadBuf,
21};
22
23use crate::{
24    FsError,
25    Operation,
26};
27
28/// A file handle whose open, read, write, seek, flush, and metadata operations
29/// are all asynchronous.
30pub struct AsyncFile {
31    inner: tokio::fs::File,
32    path: PathBuf,
33}
34
35impl AsyncFile {
36    /// Open an existing file for reading.
37    pub async fn open(path: impl AsRef<Path>) -> Result<Self, FsError> {
38        let path = path.as_ref().to_owned();
39        let inner = tokio::fs::File::open(&path)
40            .await
41            .map_err(|error| FsError::io(Operation::Read, &path, error))?;
42        Ok(Self { inner, path })
43    }
44
45    /// Open an existing file, returning `None` when it does not exist.
46    pub async fn open_if_exists(path: impl AsRef<Path>) -> Result<Option<Self>, FsError> {
47        let path = path.as_ref().to_owned();
48        match tokio::fs::File::open(&path).await {
49            Ok(inner) => Ok(Some(Self { inner, path })),
50            Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
51            Err(error) => Err(FsError::io(Operation::Read, &path, error)),
52        }
53    }
54
55    /// Create or truncate a file for writing.
56    pub async fn create(path: impl AsRef<Path>) -> Result<Self, FsError> {
57        let path = path.as_ref().to_owned();
58        let inner = tokio::fs::File::create(&path)
59            .await
60            .map_err(|error| FsError::io(Operation::Write, &path, error))?;
61        Ok(Self { inner, path })
62    }
63
64    /// Create a new file, failing if it already exists.
65    pub async fn create_new(path: impl AsRef<Path>) -> Result<Self, FsError> {
66        let path = path.as_ref().to_owned();
67        let inner = tokio::fs::OpenOptions::new()
68            .write(true)
69            .create_new(true)
70            .open(&path)
71            .await
72            .map_err(|error| FsError::io(Operation::Write, &path, error))?;
73        Ok(Self { inner, path })
74    }
75
76    /// Open a file for appending, creating it when needed.
77    pub async fn open_append(path: impl AsRef<Path>) -> Result<Self, FsError> {
78        let path = path.as_ref().to_owned();
79        let inner = tokio::fs::OpenOptions::new()
80            .create(true)
81            .append(true)
82            .read(true)
83            .open(&path)
84            .await
85            .map_err(|error| FsError::io(Operation::Write, &path, error))?;
86        Ok(Self { inner, path })
87    }
88
89    /// Open a file for writing and position it at its current end.
90    pub async fn open_write_at_end(path: impl AsRef<Path>) -> Result<Self, FsError> {
91        let path = path.as_ref().to_owned();
92        let mut inner = tokio::fs::OpenOptions::new()
93            .create(true)
94            .read(true)
95            .write(true)
96            .truncate(false)
97            .open(&path)
98            .await
99            .map_err(|error| FsError::io(Operation::Write, &path, error))?;
100        inner
101            .seek(SeekFrom::End(0))
102            .await
103            .map_err(|error| FsError::io(Operation::Write, &path, error))?;
104        Ok(Self { inner, path })
105    }
106
107    /// Read exactly the requested number of bytes.
108    pub async fn read_exact(&mut self, buffer: &mut [u8]) -> Result<(), FsError> {
109        self.inner
110            .read_exact(buffer)
111            .await
112            .map(|_| ())
113            .map_err(|error| FsError::io(Operation::Read, &self.path, error))
114    }
115
116    /// Write all bytes in `buffer`.
117    pub async fn write_all(&mut self, buffer: &[u8]) -> Result<(), FsError> {
118        self.inner
119            .write_all(buffer)
120            .await
121            .map_err(|error| FsError::io(Operation::Write, &self.path, error))
122    }
123
124    /// Flush buffered bytes to the operating system.
125    pub async fn flush(&mut self) -> Result<(), FsError> {
126        self.inner
127            .flush()
128            .await
129            .map_err(|error| FsError::io(Operation::Write, &self.path, error))
130    }
131
132    /// Flush file contents and metadata to stable storage.
133    pub async fn sync_all(&self) -> Result<(), FsError> {
134        self.inner
135            .sync_all()
136            .await
137            .map_err(|error| FsError::io(Operation::Write, &self.path, error))
138    }
139
140    /// Flush file contents to stable storage using the platform's data-sync
141    /// operation.
142    pub async fn sync_data(&self) -> Result<(), FsError> {
143        self.inner
144            .sync_data()
145            .await
146            .map_err(|error| FsError::io(Operation::Write, &self.path, error))
147    }
148
149    /// Seek to a position and return the new offset.
150    pub async fn seek(&mut self, position: SeekFrom) -> Result<u64, FsError> {
151        self.inner
152            .seek(position)
153            .await
154            .map_err(|error| FsError::io(Operation::Read, &self.path, error))
155    }
156
157    /// Return the current stream position.
158    pub async fn stream_position(&mut self) -> Result<u64, FsError> {
159        self.seek(SeekFrom::Current(0)).await
160    }
161
162    /// Return metadata for the open file.
163    pub async fn metadata(&self) -> Result<std::fs::Metadata, FsError> {
164        self.inner
165            .metadata()
166            .await
167            .map_err(|error| FsError::io(Operation::Read, &self.path, error))
168    }
169
170    /// Change the file length.
171    pub async fn set_len(&self, length: u64) -> Result<(), FsError> {
172        self.inner
173            .set_len(length)
174            .await
175            .map_err(|error| FsError::io(Operation::Write, &self.path, error))
176    }
177
178    /// Return the path used to open this file.
179    #[must_use]
180    pub fn path(&self) -> &Path {
181        &self.path
182    }
183}
184
185impl AsyncRead for AsyncFile {
186    fn poll_read(
187        mut self: Pin<&mut Self>,
188        context: &mut Context<'_>,
189        buffer: &mut ReadBuf<'_>,
190    ) -> Poll<std::io::Result<()>> {
191        Pin::new(&mut self.inner).poll_read(context, buffer)
192    }
193}
194
195impl AsyncWrite for AsyncFile {
196    fn poll_write(
197        mut self: Pin<&mut Self>,
198        context: &mut Context<'_>,
199        buffer: &[u8],
200    ) -> Poll<std::io::Result<usize>> {
201        Pin::new(&mut self.inner).poll_write(context, buffer)
202    }
203
204    fn poll_flush(
205        mut self: Pin<&mut Self>,
206        context: &mut Context<'_>,
207    ) -> Poll<std::io::Result<()>> {
208        Pin::new(&mut self.inner).poll_flush(context)
209    }
210
211    fn poll_shutdown(
212        mut self: Pin<&mut Self>,
213        context: &mut Context<'_>,
214    ) -> Poll<std::io::Result<()>> {
215        Pin::new(&mut self.inner).poll_shutdown(context)
216    }
217}