1use std::{future::Future, io, mem::ManuallyDrop, path::Path};
2
3use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut};
4#[cfg(unix)]
5use compio_driver::op::FileStat;
6use compio_driver::{
7 ToSharedFd, impl_raw_fd,
8 op::{
9 AsyncifyFd, BufResultExt, CloseFile, ReadAt, ReadManagedAt, ResultTakeBuffer, Sync, WriteAt,
10 },
11};
12use compio_io::{AsyncReadAt, AsyncReadManagedAt, AsyncWriteAt, util::Splittable};
13use compio_runtime::{Attacher, BorrowedBuffer, BufferPool};
14#[cfg(all(unix, not(solarish)))]
15use {
16 compio_buf::{IoVectoredBuf, IoVectoredBufMut},
17 compio_driver::op::{ReadVectoredAt, WriteVectoredAt},
18};
19
20use crate::{Metadata, OpenOptions, Permissions};
21
22#[derive(Debug, Clone)]
53pub struct File {
54 inner: Attacher<std::fs::File>,
55}
56
57impl File {
58 pub(crate) fn from_std(file: std::fs::File) -> io::Result<Self> {
59 Ok(Self {
60 inner: Attacher::new(file)?,
61 })
62 }
63
64 pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
68 OpenOptions::new().read(true).open(path).await
69 }
70
71 pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
78 OpenOptions::new()
79 .create(true)
80 .write(true)
81 .truncate(true)
82 .open(path)
83 .await
84 }
85
86 pub fn close(self) -> impl Future<Output = io::Result<()>> {
89 let this = ManuallyDrop::new(self);
93 async move {
94 let fd = ManuallyDrop::into_inner(this)
95 .inner
96 .into_inner()
97 .take()
98 .await;
99 if let Some(fd) = fd {
100 let op = CloseFile::new(fd.into());
101 compio_runtime::submit(op).await.0?;
102 }
103 Ok(())
104 }
105 }
106
107 #[cfg(windows)]
109 pub async fn metadata(&self) -> io::Result<Metadata> {
110 let op = AsyncifyFd::new(self.to_shared_fd(), |file: &std::fs::File| {
111 match file.metadata().map(Metadata::from_std) {
112 Ok(meta) => BufResult(Ok(0), Some(meta)),
113 Err(e) => BufResult(Err(e), None),
114 }
115 });
116 let BufResult(res, meta) = compio_runtime::submit(op).await;
117 res.map(|_| meta.into_inner().expect("metadata should be present"))
118 }
119
120 #[cfg(windows)]
121 pub async fn set_len(&self, size: u64) -> io::Result<()> {
124 let op = AsyncifyFd::new(self.to_shared_fd(), move |file: &std::fs::File| {
125 BufResult(file.set_len(size).map(|_| 0), ())
126 });
127
128 compio_runtime::submit(op).await.0.map(|_| ())
129 }
130
131 #[cfg(unix)]
132 pub async fn set_len(&self, size: u64) -> io::Result<()> {
138 use compio_driver::op::TruncateFile;
139
140 let op = TruncateFile::new(self.to_shared_fd(), size);
141 compio_runtime::submit(op).await.0.map(|_| ())
142 }
143
144 #[cfg(unix)]
146 pub async fn metadata(&self) -> io::Result<Metadata> {
147 let op = FileStat::new(self.to_shared_fd());
148 let BufResult(res, op) = compio_runtime::submit(op).await;
149 res.map(|_| Metadata::from_stat(op.into_inner()))
150 }
151
152 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
154 let op = AsyncifyFd::new(self.to_shared_fd(), move |file: &std::fs::File| {
155 BufResult(file.set_permissions(perm.0).map(|_| 0), ())
156 });
157 compio_runtime::submit(op).await.0.map(|_| ())
158 }
159
160 async fn sync_impl(&self, datasync: bool) -> io::Result<()> {
161 let op = Sync::new(self.to_shared_fd(), datasync);
162 compio_runtime::submit(op).await.0?;
163 Ok(())
164 }
165
166 pub async fn sync_all(&self) -> io::Result<()> {
171 self.sync_impl(false).await
172 }
173
174 pub async fn sync_data(&self) -> io::Result<()> {
186 self.sync_impl(true).await
187 }
188}
189
190impl AsyncReadAt for File {
191 async fn read_at<T: IoBufMut>(&self, buffer: T, pos: u64) -> BufResult<usize, T> {
192 let fd = self.inner.to_shared_fd();
193 let op = ReadAt::new(fd, pos, buffer);
194 let res = compio_runtime::submit(op).await.into_inner();
195 unsafe { res.map_advanced() }
196 }
197
198 #[cfg(all(unix, not(solarish)))]
199 async fn read_vectored_at<T: IoVectoredBufMut>(
200 &self,
201 buffer: T,
202 pos: u64,
203 ) -> BufResult<usize, T> {
204 use compio_driver::op::VecBufResultExt;
205
206 let fd = self.inner.to_shared_fd();
207 let op = ReadVectoredAt::new(fd, pos, buffer);
208 let res = compio_runtime::submit(op).await.into_inner();
209 unsafe { res.map_vec_advanced() }
210 }
211}
212
213impl AsyncReadManagedAt for File {
214 type Buffer<'a> = BorrowedBuffer<'a>;
215 type BufferPool = BufferPool;
216
217 async fn read_managed_at<'a>(
218 &self,
219 buffer_pool: &'a Self::BufferPool,
220 len: usize,
221 pos: u64,
222 ) -> io::Result<Self::Buffer<'a>> {
223 let fd = self.inner.to_shared_fd();
224 let buffer_pool = buffer_pool.try_inner()?;
225 let op = ReadManagedAt::new(fd, pos, buffer_pool, len)?;
226 compio_runtime::submit(op)
227 .with_extra()
228 .await
229 .take_buffer(buffer_pool)
230 }
231}
232
233impl AsyncWriteAt for File {
234 #[inline]
235 async fn write_at<T: IoBuf>(&mut self, buf: T, pos: u64) -> BufResult<usize, T> {
236 (&*self).write_at(buf, pos).await
237 }
238
239 #[cfg(all(unix, not(solarish)))]
240 #[inline]
241 async fn write_vectored_at<T: IoVectoredBuf>(
242 &mut self,
243 buf: T,
244 pos: u64,
245 ) -> BufResult<usize, T> {
246 (&*self).write_vectored_at(buf, pos).await
247 }
248}
249
250impl AsyncWriteAt for &File {
251 async fn write_at<T: IoBuf>(&mut self, buffer: T, pos: u64) -> BufResult<usize, T> {
252 let fd = self.inner.to_shared_fd();
253 let op = WriteAt::new(fd, pos, buffer);
254 compio_runtime::submit(op).await.into_inner()
255 }
256
257 #[cfg(all(unix, not(solarish)))]
258 async fn write_vectored_at<T: IoVectoredBuf>(
259 &mut self,
260 buffer: T,
261 pos: u64,
262 ) -> BufResult<usize, T> {
263 let fd = self.inner.to_shared_fd();
264 let op = WriteVectoredAt::new(fd, pos, buffer);
265 compio_runtime::submit(op).await.into_inner()
266 }
267}
268
269impl Splittable for File {
270 type ReadHalf = File;
271 type WriteHalf = File;
272
273 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
274 (self.clone(), self)
275 }
276}
277
278impl Splittable for &File {
279 type ReadHalf = File;
280 type WriteHalf = File;
281
282 fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
283 (self.clone(), self.clone())
284 }
285}
286
287impl_raw_fd!(File, std::fs::File, inner, file);