Skip to main content

async_fs_io/
async_file.rs

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