1#![allow(clippy::unused_async)]
13
14use crate::fs::OpenOptions;
15use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
16use crate::runtime::spawn_blocking_io;
17use std::fs::{Metadata, Permissions};
18use std::io::{self, Read, Seek, SeekFrom, Write};
19use std::path::Path;
20use std::pin::Pin;
21use std::sync::Arc;
22use std::task::{Context, Poll};
23
24#[derive(Debug)]
29pub struct File {
30 pub(crate) inner: Arc<std::fs::File>,
31}
32
33impl File {
34 pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
38 let path = path.as_ref().to_owned();
39 let file = spawn_blocking_io(move || std::fs::File::open(&path)).await?;
40 Ok(Self {
41 inner: Arc::new(file),
42 })
43 }
44
45 pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
49 let path = path.as_ref().to_owned();
50 let file = spawn_blocking_io(move || std::fs::File::create(&path)).await?;
51 Ok(Self {
52 inner: Arc::new(file),
53 })
54 }
55
56 #[must_use]
58 pub fn options() -> OpenOptions {
59 OpenOptions::new()
60 }
61
62 pub(crate) fn from_std(file: std::fs::File) -> Self {
63 Self {
64 inner: Arc::new(file),
65 }
66 }
67
68 pub async fn sync_all(&self) -> io::Result<()> {
70 let inner = Arc::clone(&self.inner);
71 spawn_blocking_io(move || inner.sync_all()).await
72 }
73
74 pub async fn sync_data(&self) -> io::Result<()> {
76 let inner = Arc::clone(&self.inner);
77 spawn_blocking_io(move || inner.sync_data()).await
78 }
79
80 pub async fn set_len(&self, size: u64) -> io::Result<()> {
82 let inner = Arc::clone(&self.inner);
83 spawn_blocking_io(move || inner.set_len(size)).await
84 }
85
86 pub async fn metadata(&self) -> io::Result<Metadata> {
88 let inner = Arc::clone(&self.inner);
89 spawn_blocking_io(move || inner.metadata()).await
90 }
91
92 pub async fn try_clone(&self) -> io::Result<Self> {
94 let inner = Arc::clone(&self.inner);
95 let file = spawn_blocking_io(move || inner.try_clone()).await?;
96 Ok(Self {
97 inner: Arc::new(file),
98 })
99 }
100
101 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
103 let inner = Arc::clone(&self.inner);
104 spawn_blocking_io(move || inner.set_permissions(perm)).await
105 }
106
107 pub async fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
114 Arc::get_mut(&mut self.inner)
116 .ok_or_else(|| io::Error::other("file handle is shared"))?
117 .seek(pos)
118 }
119
120 pub async fn stream_position(&mut self) -> io::Result<u64> {
122 Arc::get_mut(&mut self.inner)
124 .ok_or_else(|| io::Error::other("file handle is shared"))?
125 .stream_position()
126 }
127
128 pub async fn rewind(&mut self) -> io::Result<()> {
130 Arc::get_mut(&mut self.inner)
132 .ok_or_else(|| io::Error::other("file handle is shared"))?
133 .rewind()
134 }
135}
136
137impl AsyncRead for File {
141 fn poll_read(
142 mut self: Pin<&mut Self>,
143 _cx: &mut Context<'_>,
144 buf: &mut ReadBuf<'_>,
145 ) -> Poll<io::Result<()>> {
146 let inner = Arc::get_mut(&mut self.inner)
147 .ok_or_else(|| io::Error::other("file handle is shared during poll_read"))?;
148 let n = inner.read(buf.unfilled())?;
149 buf.advance(n);
150 Poll::Ready(Ok(()))
151 }
152}
153
154impl AsyncWrite for File {
155 fn poll_write(
156 mut self: Pin<&mut Self>,
157 _cx: &mut Context<'_>,
158 buf: &[u8],
159 ) -> Poll<io::Result<usize>> {
160 let inner = Arc::get_mut(&mut self.inner)
161 .ok_or_else(|| io::Error::other("file handle is shared during poll_write"))?;
162 let n = inner.write(buf)?;
163 Poll::Ready(Ok(n))
164 }
165
166 fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
167 let inner = Arc::get_mut(&mut self.inner)
168 .ok_or_else(|| io::Error::other("file handle is shared during poll_flush"))?;
169 inner.flush()?;
170 Poll::Ready(Ok(()))
171 }
172
173 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
174 Poll::Ready(Ok(()))
175 }
176}
177
178impl AsyncSeek for File {
179 fn poll_seek(
180 mut self: Pin<&mut Self>,
181 _cx: &mut Context<'_>,
182 pos: SeekFrom,
183 ) -> Poll<io::Result<u64>> {
184 let inner = Arc::get_mut(&mut self.inner)
185 .ok_or_else(|| io::Error::other("file handle is shared during poll_seek"))?;
186 let n = inner.seek(pos)?;
187 Poll::Ready(Ok(n))
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::io::{AsyncReadExt, AsyncWriteExt}; use tempfile::tempdir;
196
197 fn init_test(name: &str) {
198 crate::test_utils::init_test_logging();
199 crate::test_phase!(name);
200 }
201
202 #[test]
203 fn test_file_create_write_read() {
204 init_test("test_file_create_write_read");
205 futures_lite::future::block_on(async {
208 let dir = tempdir().unwrap();
209 let path = dir.path().join("test.txt");
210
211 let mut file = File::create(&path).await.unwrap();
213 file.write_all(b"hello world").await.unwrap();
214 file.sync_all().await.unwrap();
215 drop(file);
216
217 let mut file = File::open(&path).await.unwrap();
219 let mut contents = String::new();
220 file.read_to_string(&mut contents).await.unwrap();
221 crate::assert_with_log!(
222 contents == "hello world",
223 "contents",
224 "hello world",
225 contents
226 );
227 });
228 crate::test_complete!("test_file_create_write_read");
229 }
230
231 #[test]
232 fn test_file_seek() {
233 init_test("test_file_seek");
234 futures_lite::future::block_on(async {
235 let dir = tempdir().unwrap();
236 let path = dir.path().join("test_seek.txt");
237
238 let mut file = OpenOptions::new()
239 .read(true)
240 .write(true)
241 .create(true)
242 .open(&path)
243 .await
244 .unwrap();
245
246 file.write_all(b"0123456789").await.unwrap();
247
248 file.seek(SeekFrom::Start(5)).await.unwrap();
249 let mut buf = [0u8; 5];
250 file.read_exact(&mut buf).await.unwrap();
251 crate::assert_with_log!(&buf == b"56789", "seek contents", b"56789", buf);
252 });
253 crate::test_complete!("test_file_seek");
254 }
255
256 #[test]
257 fn test_file_metadata() {
258 init_test("test_file_metadata");
259 futures_lite::future::block_on(async {
260 let dir = tempdir().unwrap();
261 let path = dir.path().join("test_metadata.txt");
262
263 let mut file = File::create(&path).await.unwrap();
265 file.write_all(b"test content").await.unwrap();
266 file.sync_all().await.unwrap();
267 drop(file);
268
269 let file = File::open(&path).await.unwrap();
271 let metadata = file.metadata().await.unwrap();
272
273 crate::assert_with_log!(metadata.is_file(), "is_file", true, metadata.is_file());
274 crate::assert_with_log!(metadata.len() == 12, "file length", 12u64, metadata.len());
275 });
276 crate::test_complete!("test_file_metadata");
277 }
278
279 #[test]
280 fn test_file_set_len() {
281 init_test("test_file_set_len");
282 futures_lite::future::block_on(async {
283 let dir = tempdir().unwrap();
284 let path = dir.path().join("test_truncate.txt");
285
286 let mut file = File::create(&path).await.unwrap();
288 file.write_all(b"hello world").await.unwrap();
289 file.sync_all().await.unwrap();
290
291 file.set_len(5).await.unwrap();
293 file.sync_all().await.unwrap();
294 drop(file);
295
296 let mut file = File::open(&path).await.unwrap();
298 let mut contents = String::new();
299 file.read_to_string(&mut contents).await.unwrap();
300 crate::assert_with_log!(contents == "hello", "truncated contents", "hello", contents);
301 });
302 crate::test_complete!("test_file_set_len");
303 }
304
305 #[test]
306 fn test_cancellation_safety_soft_cancel() {
307 init_test("test_cancellation_safety_soft_cancel");
310 futures_lite::future::block_on(async {
311 let dir = tempdir().unwrap();
312 let path = dir.path().join("test_cancel.txt");
313
314 let file = File::create(&path).await.unwrap();
316 drop(file);
317
318 let file = File::open(&path).await.unwrap();
320
321 let metadata = file.metadata().await.unwrap();
323 crate::assert_with_log!(metadata.is_file(), "file exists", true, metadata.is_file());
324 });
325 crate::test_complete!("test_cancellation_safety_soft_cancel");
326 }
327}