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