1use crate::default_uring;
39use crate::metadata::Metadata;
40use crate::uring::URING_LEN_MAX;
41use std::cmp::min;
42use std::io;
43use std::path::Path;
44use tokio::fs::File;
45
46#[derive(Clone, Debug, Default)]
78pub struct OpenOptions {
79 read: bool,
80 write: bool,
81 append: bool,
82 truncate: bool,
83 create: bool,
84 create_new: bool,
85 mode: Option<u32>,
86 custom_flags: i32,
87}
88
89impl OpenOptions {
90 pub fn new() -> Self {
94 Self::default()
95 }
96
97 pub fn read(&mut self, read: bool) -> &mut Self {
99 self.read = read;
100 self
101 }
102
103 pub fn write(&mut self, write: bool) -> &mut Self {
105 self.write = write;
106 self
107 }
108
109 pub fn append(&mut self, append: bool) -> &mut Self {
114 self.append = append;
115 self
116 }
117
118 pub fn truncate(&mut self, truncate: bool) -> &mut Self {
123 self.truncate = truncate;
124 self
125 }
126
127 pub fn create(&mut self, create: bool) -> &mut Self {
131 self.create = create;
132 self
133 }
134
135 pub fn create_new(&mut self, create_new: bool) -> &mut Self {
140 self.create_new = create_new;
141 self
142 }
143
144 pub fn mode(&mut self, mode: u32) -> &mut Self {
148 self.mode = Some(mode);
149 self
150 }
151
152 pub fn custom_flags(&mut self, flags: i32) -> &mut Self {
169 self.custom_flags = flags;
170 self
171 }
172
173 pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
177 let flags = self.to_flags()?;
178 let mode = self.mode.unwrap_or(0o644);
179 let fd = default_uring().open(path, flags, mode).await?;
180 Ok(File::from_std(std::fs::File::from(fd)))
181 }
182
183 fn to_flags(&self) -> io::Result<i32> {
184 if self.append && self.truncate {
186 return Err(io::Error::new(
187 io::ErrorKind::InvalidInput,
188 "cannot combine append and truncate",
189 ));
190 }
191
192 let mut flags = self.custom_flags;
194
195 let has_write = self.write || self.append;
197 if self.read && has_write {
198 flags |= libc::O_RDWR;
199 } else if self.read {
200 flags |= libc::O_RDONLY;
201 } else if has_write {
202 flags |= libc::O_WRONLY;
203 } else {
204 flags |= libc::O_RDONLY;
206 }
207
208 if self.append {
209 flags |= libc::O_APPEND;
210 }
211
212 if self.truncate {
213 flags |= libc::O_TRUNC;
214 }
215
216 if self.create_new {
217 flags |= libc::O_CREAT | libc::O_EXCL;
218 } else if self.create {
219 flags |= libc::O_CREAT;
220 }
221
222 Ok(flags)
223 }
224}
225
226pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
230 let fd = default_uring().open(path, libc::O_RDONLY, 0).await?;
231 Ok(File::from_std(std::fs::File::from(fd)))
232}
233
234pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
238 let fd = default_uring()
239 .open(path, libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC, 0o644)
240 .await?;
241 Ok(File::from_std(std::fs::File::from(fd)))
242}
243
244pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
248 let uring = default_uring();
249 let fd = uring.open(path.as_ref(), libc::O_RDONLY, 0).await?;
250 let meta = uring.statx(&fd).await?;
251 let size = meta.len();
252
253 if size == 0 {
254 return Ok(Vec::new());
255 }
256
257 if size <= URING_LEN_MAX {
259 return uring.read_exact_at(&fd, 0, size).await;
260 }
261
262 let mut buf = Vec::with_capacity(size as usize);
264 let mut offset = 0u64;
265 while offset < size {
266 let chunk_size = min(URING_LEN_MAX, size - offset);
267 let chunk = uring.read_exact_at(&fd, offset, chunk_size).await?;
268 buf.extend_from_slice(&chunk);
269 offset += chunk_size;
270 }
271 Ok(buf)
272}
273
274pub async fn read_to_string(path: impl AsRef<Path>) -> io::Result<String> {
280 let bytes = read(path).await?;
281 String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
282}
283
284pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
286 let uring = default_uring();
287 let fd = uring
288 .open(
289 path.as_ref(),
290 libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
291 0o644,
292 )
293 .await?;
294 uring.write_all_at(&fd, 0, contents.as_ref()).await
295}
296
297pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
299 default_uring().statx_path(path).await
300}
301
302pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
306 default_uring().mkdir(path, 0o755).await
307}
308
309pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
313 let path = path.as_ref();
314
315 if metadata(path).await.is_ok() {
317 return Ok(());
318 }
319
320 let mut to_create = Vec::new();
322 let mut current = Some(path);
323
324 while let Some(p) = current {
325 if metadata(p).await.is_ok() {
326 break;
327 }
328 to_create.push(p);
329 current = p.parent();
330 }
331
332 for p in to_create.into_iter().rev() {
334 match default_uring().mkdir(p, 0o755).await {
335 Ok(()) => {}
336 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
337 Err(e) => return Err(e),
338 }
339 }
340
341 Ok(())
342}
343
344pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
346 default_uring().unlink(path).await
347}
348
349pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
351 default_uring().rmdir(path).await
352}
353
354pub async fn remove_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
358 let path = path.as_ref();
359
360 let mut read_dir = tokio::fs::read_dir(path).await?;
362 while let Some(entry) = read_dir.next_entry().await? {
363 let entry_path = entry.path();
364 let file_type = entry.file_type().await?;
365 if file_type.is_dir() {
366 Box::pin(remove_dir_all(&entry_path)).await?;
367 } else {
368 remove_file(&entry_path).await?;
369 }
370 }
371
372 remove_dir(path).await
373}
374
375pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
377 default_uring().rename(from, to).await
378}
379
380pub async fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<u64> {
384 let contents = read(from).await?;
385 let len = contents.len() as u64;
386 write(to, contents).await?;
387 Ok(len)
388}
389
390pub async fn exists(path: impl AsRef<Path>) -> bool {
392 metadata(path).await.is_ok()
393}
394
395pub async fn symlink(target: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
399 default_uring().symlink(target, link).await
400}
401
402pub async fn hard_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
406 default_uring().hard_link(original, link).await
407}
408
409pub async fn truncate(path: impl AsRef<Path>, len: u64) -> io::Result<()> {
413 let uring = default_uring();
414 let fd = uring.open(path.as_ref(), libc::O_WRONLY, 0).await?;
415 uring.ftruncate(&fd, len).await
416}
417
418pub async fn append(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
420 let uring = default_uring();
421 let fd = uring
422 .open(
423 path.as_ref(),
424 libc::O_WRONLY | libc::O_CREAT | libc::O_APPEND,
425 0o644,
426 )
427 .await?;
428 uring.write_all_at(&fd, 0, contents.as_ref()).await
430}