1#[cfg(unix)]
2use std::os::unix::{self, fs::PermissionsExt as _};
3use std::{
4 borrow::Cow,
5 ffi::OsStr,
6 fs::{Metadata, Permissions},
7 io,
8 path::{self, Path, PathBuf},
9};
10
11#[cfg(unix)]
12use pathdiff::diff_paths;
13#[cfg(unix)]
14use tokio::task;
15use tokio::{
16 fs::{self, File, ReadDir},
17 io::{AsyncBufReadExt as _, AsyncWriteExt as _, BufReader, Lines},
18};
19
20use super::{fs::FileExt as _, temp_path::TempPath};
21
22#[trait_variant::make(Send)]
23pub trait PathExt {
24 fn base(&self) -> io::Result<&Path>;
25 fn with_base(&self, base: impl AsRef<Path>) -> Cow<'_, Path>;
26
27 fn file_suffix(&self) -> Option<&OsStr>;
28
29 fn absolute(&self) -> io::Result<PathBuf>;
30
31 async fn metadata_async(&self) -> io::Result<Metadata>;
32 async fn symlink_metadata_async(&self) -> io::Result<Metadata>;
33 async fn canonicalize_async(&self) -> io::Result<PathBuf>;
34 async fn read_link_async(&self) -> io::Result<PathBuf>;
35 async fn read_dir_async(&self) -> io::Result<ReadDir>;
36
37 async fn exists_async(&self) -> bool;
38 async fn is_file_async(&self) -> bool;
39 async fn is_dir_async(&self) -> bool;
40 async fn is_symlink_async(&self) -> bool;
41
42 async fn try_exists_async(&self) -> io::Result<bool>;
43
44 async fn try_is_file(&self) -> io::Result<bool>;
45 async fn try_is_dir(&self) -> io::Result<bool>;
46 async fn try_is_symlink(&self) -> io::Result<bool>;
47
48 async fn metadata_if_exists(&self) -> io::Result<Option<Metadata>>;
49 async fn symlink_metadata_if_exists(&self) -> io::Result<Option<Metadata>>;
50 async fn canonicalize_if_exists(&self) -> io::Result<Option<PathBuf>>;
51 async fn read_link_if_exists(&self) -> io::Result<Option<PathBuf>>;
52 async fn read_dir_if_exists(&self) -> io::Result<Option<ReadDir>>;
53
54 async fn exists_nofollow(&self) -> bool;
55 async fn is_file_nofollow(&self) -> bool;
56 async fn is_dir_nofollow(&self) -> bool;
57
58 async fn try_exists_nofollow(&self) -> io::Result<bool>;
59 async fn try_is_file_nofollow(&self) -> io::Result<bool>;
60 async fn try_is_dir_nofollow(&self) -> io::Result<bool>;
61
62 async fn try_is_read_dir_empty(&self) -> io::Result<bool>;
63 async fn try_is_read_dir_empty_if_exists(&self) -> io::Result<Option<bool>>;
64
65 async fn create_new(&self) -> io::Result<File>;
66 async fn create_new_if_not_exists(&self) -> io::Result<Option<File>>;
67
68 async fn create(&self) -> io::Result<File>;
69 async fn create_if_not_exists(&self) -> io::Result<Option<File>>;
70
71 async fn open(&self) -> io::Result<File>;
72 async fn open_if_exists(&self) -> io::Result<Option<File>>;
73
74 async fn read(&self) -> io::Result<Vec<u8>>;
75 async fn read_if_exists(&self) -> io::Result<Option<Vec<u8>>>;
76
77 async fn read_to_string(&self) -> io::Result<String>;
78 async fn read_to_string_if_exists(&self) -> io::Result<Option<String>>;
79
80 async fn read_lines(&self) -> io::Result<Lines<BufReader<File>>>;
81 async fn read_lines_if_exists(&self) -> io::Result<Option<Lines<BufReader<File>>>>;
82
83 async fn create_dir_all(self) -> io::Result<Self>
84 where
85 Self: Sized;
86
87 async fn create_dir(self) -> io::Result<Self>
88 where
89 Self: Sized;
90 async fn create_dir_if_not_exists(self) -> io::Result<Option<Self>>
91 where
92 Self: Sized;
93
94 async fn write_new(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Self>
95 where
96 Self: Sized;
97 async fn write_new_if_not_exists(
98 self,
99 contents: impl AsRef<[u8]> + Send,
100 ) -> io::Result<Option<Self>>
101 where
102 Self: Sized;
103
104 async fn write(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Self>
105 where
106 Self: Sized;
107 async fn write_if_exists(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<Self>>
108 where
109 Self: Sized;
110
111 async fn append(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Self>
112 where
113 Self: Sized;
114 async fn append_if_exists(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<Self>>
115 where
116 Self: Sized;
117
118 async fn copy(self, to: impl AsRef<Path> + Send) -> io::Result<Self>
119 where
120 Self: Sized;
121 async fn copy_if_exists(self, to: impl AsRef<Path> + Send) -> io::Result<Option<Self>>
122 where
123 Self: Sized;
124
125 async fn rename(self, to: impl AsRef<Path> + Send) -> io::Result<Self>
126 where
127 Self: Sized;
128 async fn rename_if_exists(self, to: impl AsRef<Path> + Send) -> io::Result<Option<Self>>
129 where
130 Self: Sized;
131
132 async fn remove_file(self) -> io::Result<Self>
133 where
134 Self: Sized;
135 async fn remove_file_if_exists(self) -> io::Result<Option<Self>>
136 where
137 Self: Sized;
138
139 async fn remove_dir(self) -> io::Result<Self>
140 where
141 Self: Sized;
142 async fn remove_dir_if_exists(self) -> io::Result<Option<Self>>
143 where
144 Self: Sized;
145
146 async fn remove_dir_all(self) -> io::Result<Self>
147 where
148 Self: Sized;
149 async fn remove_dir_all_if_exists(self) -> io::Result<Option<Self>>
150 where
151 Self: Sized;
152
153 async fn hard_link(self, link: impl AsRef<Path> + Send) -> io::Result<Self>
154 where
155 Self: Sized;
156 async fn hard_link_if_exists(self, link: impl AsRef<Path> + Send) -> io::Result<Option<Self>>
157 where
158 Self: Sized;
159 async fn hard_link_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Self>
160 where
161 Self: Sized;
162 async fn hard_link_atomic_if_exists(
163 self,
164 link: impl AsRef<Path> + Send,
165 ) -> io::Result<Option<Self>>
166 where
167 Self: Sized;
168
169 #[cfg(unix)]
170 async fn symlink(self, link: impl AsRef<Path> + Send) -> io::Result<Self>
171 where
172 Self: Sized;
173 #[cfg(unix)]
174 async fn symlink_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Self>
175 where
176 Self: Sized;
177
178 #[cfg(unix)]
179 async fn symlink_absolute(self, link: impl AsRef<Path> + Send) -> io::Result<Self>
180 where
181 Self: Sized;
182 #[cfg(unix)]
183 async fn symlink_absolute_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Self>
184 where
185 Self: Sized;
186
187 #[cfg(unix)]
188 async fn symlink_relative(self, link: impl AsRef<Path> + Send + Sync) -> io::Result<Self>
189 where
190 Self: Sized;
191 #[cfg(unix)]
192 async fn symlink_relative_atomic(
193 self,
194 link: impl AsRef<Path> + Send + Sync,
195 ) -> io::Result<Self>
196 where
197 Self: Sized;
198
199 async fn set_permissions(self, perm: Permissions) -> io::Result<Self>
200 where
201 Self: Sized;
202 async fn set_permissions_if_exists(self, perm: Permissions) -> io::Result<Option<Self>>
203 where
204 Self: Sized;
205
206 async fn set_permissions_readonly(self, readonly: bool) -> io::Result<Self>
207 where
208 Self: Sized;
209 async fn set_permissions_readonly_if_exists(self, readonly: bool) -> io::Result<Option<Self>>
210 where
211 Self: Sized;
212
213 #[cfg(unix)]
214 async fn set_permissions_mode(self, mode: u32) -> io::Result<Self>
215 where
216 Self: Sized;
217 #[cfg(unix)]
218 async fn set_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>>
219 where
220 Self: Sized;
221 #[cfg(unix)]
222 async fn add_permissions_mode(self, mode: u32) -> io::Result<Self>
223 where
224 Self: Sized;
225 #[cfg(unix)]
226 async fn add_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>>
227 where
228 Self: Sized;
229 #[cfg(unix)]
230 async fn remove_permissions_mode(self, mode: u32) -> io::Result<Self>
231 where
232 Self: Sized;
233 #[cfg(unix)]
234 async fn remove_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>>
235 where
236 Self: Sized;
237
238 #[cfg(unix)]
239 async fn chown(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Self>
240 where
241 Self: Sized;
242 #[cfg(unix)]
243 async fn chown_if_exists(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<Self>>
244 where
245 Self: Sized;
246
247 #[cfg(unix)]
248 async fn chown_nofollow(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Self>
249 where
250 Self: Sized;
251 #[cfg(unix)]
252 async fn chown_nofollow_if_exists(
253 self,
254 uid: Option<u32>,
255 gid: Option<u32>,
256 ) -> io::Result<Option<Self>>
257 where
258 Self: Sized;
259
260 #[cfg(all(unix, not(target_os = "fuchsia")))]
261 async fn chroot(self) -> io::Result<Self>
262 where
263 Self: Sized;
264 #[cfg(all(unix, not(target_os = "fuchsia")))]
265 async fn chroot_if_exists(self) -> io::Result<Option<Self>>
266 where
267 Self: Sized;
268}
269
270impl<T: AsRef<Path> + Send + Sync> PathExt for T {
271 #[inline]
272 fn base(&self) -> io::Result<&Path> {
273 self.as_ref()
274 .parent()
275 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Path has no parent"))
276 }
277
278 #[inline]
279 fn with_base(&self, base: impl AsRef<Path>) -> Cow<'_, Path> {
280 if self.as_ref().is_relative() {
281 let path = base.as_ref().join(self);
282
283 Cow::Owned(path)
284 } else {
285 Cow::Borrowed(self.as_ref())
286 }
287 }
288
289 #[inline]
290 fn file_suffix(&self) -> Option<&OsStr> {
291 let file_name = self.as_ref().file_name()?;
292 let file_prefix = self.as_ref().file_prefix()?;
293
294 let bytes = file_name.as_encoded_bytes();
295
296 let start = file_prefix.as_encoded_bytes().len() + 1;
297
298 if start <= bytes.len() {
299 let slice = &bytes[start..];
300
301 let os_str = unsafe { OsStr::from_encoded_bytes_unchecked(slice) };
302
303 Some(os_str)
304 } else {
305 None
306 }
307 }
308
309 #[inline]
310 fn absolute(&self) -> io::Result<PathBuf> {
311 path::absolute(self)
312 }
313
314 #[inline]
315 async fn metadata_async(&self) -> io::Result<Metadata> {
316 fs::metadata(self).await
317 }
318
319 #[inline]
320 async fn symlink_metadata_async(&self) -> io::Result<Metadata> {
321 fs::symlink_metadata(self).await
322 }
323
324 #[inline]
325 async fn canonicalize_async(&self) -> io::Result<PathBuf> {
326 fs::canonicalize(self).await
327 }
328
329 #[inline]
330 async fn read_link_async(&self) -> io::Result<PathBuf> {
331 fs::read_link(self).await
332 }
333
334 #[inline]
335 async fn read_dir_async(&self) -> io::Result<ReadDir> {
336 fs::read_dir(self).await
337 }
338
339 #[inline]
340 async fn exists_async(&self) -> bool {
341 self.metadata_async().await.is_ok()
342 }
343
344 #[inline]
345 async fn is_file_async(&self) -> bool {
346 self.metadata_async()
347 .await
348 .map(|meta| meta.is_file())
349 .unwrap_or(false)
350 }
351
352 #[inline]
353 async fn is_dir_async(&self) -> bool {
354 self.metadata_async()
355 .await
356 .map(|meta| meta.is_dir())
357 .unwrap_or(false)
358 }
359
360 #[inline]
361 async fn is_symlink_async(&self) -> bool {
362 self.symlink_metadata_async()
363 .await
364 .map(|meta| meta.is_symlink())
365 .unwrap_or(false)
366 }
367
368 #[inline]
369 async fn try_exists_async(&self) -> io::Result<bool> {
370 fs::try_exists(self).await
371 }
372
373 #[inline]
374 async fn try_is_file(&self) -> io::Result<bool> {
375 self.metadata_if_exists()
376 .await
377 .map(|meta_opt| meta_opt.map(|meta| meta.is_file()).unwrap_or(false))
378 }
379
380 #[inline]
381 async fn try_is_dir(&self) -> io::Result<bool> {
382 self.metadata_if_exists()
383 .await
384 .map(|meta_opt| meta_opt.map(|meta| meta.is_dir()).unwrap_or(false))
385 }
386
387 #[inline]
388 async fn try_is_symlink(&self) -> io::Result<bool> {
389 self.symlink_metadata_if_exists()
390 .await
391 .map(|meta_opt| meta_opt.map(|meta| meta.is_symlink()).unwrap_or(false))
392 }
393
394 #[inline]
395 async fn metadata_if_exists(&self) -> io::Result<Option<Metadata>> {
396 match self.metadata_async().await {
397 Ok(meta) => Ok(Some(meta)),
398 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
399 Err(err) => Err(err),
400 }
401 }
402
403 #[inline]
404 async fn symlink_metadata_if_exists(&self) -> io::Result<Option<Metadata>> {
405 match self.symlink_metadata_async().await {
406 Ok(meta) => Ok(Some(meta)),
407 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
408 Err(err) => Err(err),
409 }
410 }
411
412 #[inline]
413 async fn canonicalize_if_exists(&self) -> io::Result<Option<PathBuf>> {
414 match self.canonicalize_async().await {
415 Ok(path) => Ok(Some(path)),
416 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
417 Err(err) => Err(err),
418 }
419 }
420
421 #[inline]
422 async fn read_link_if_exists(&self) -> io::Result<Option<PathBuf>> {
423 match self.read_link_async().await {
424 Ok(path) => Ok(Some(path)),
425 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
426 Err(err) => Err(err),
427 }
428 }
429
430 #[inline]
431 async fn read_dir_if_exists(&self) -> io::Result<Option<ReadDir>> {
432 match self.read_dir_async().await {
433 Ok(entries) => Ok(Some(entries)),
434 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
435 Err(err) => Err(err),
436 }
437 }
438
439 #[inline]
440 async fn exists_nofollow(&self) -> bool {
441 self.symlink_metadata_async().await.is_ok()
442 }
443
444 #[inline]
445 async fn is_file_nofollow(&self) -> bool {
446 self.symlink_metadata_async()
447 .await
448 .map(|meta| meta.is_file())
449 .unwrap_or(false)
450 }
451
452 #[inline]
453 async fn is_dir_nofollow(&self) -> bool {
454 self.symlink_metadata_async()
455 .await
456 .map(|meta| meta.is_dir())
457 .unwrap_or(false)
458 }
459
460 #[inline]
461 async fn try_exists_nofollow(&self) -> io::Result<bool> {
462 self.symlink_metadata_if_exists()
463 .await
464 .map(|meta_opt| meta_opt.is_some())
465 }
466
467 #[inline]
468 async fn try_is_file_nofollow(&self) -> io::Result<bool> {
469 self.symlink_metadata_if_exists()
470 .await
471 .map(|meta_opt| meta_opt.map(|meta| meta.is_file()).unwrap_or(false))
472 }
473
474 #[inline]
475 async fn try_is_dir_nofollow(&self) -> io::Result<bool> {
476 self.symlink_metadata_if_exists()
477 .await
478 .map(|meta_opt| meta_opt.map(|meta| meta.is_dir()).unwrap_or(false))
479 }
480
481 #[inline]
482 async fn try_is_read_dir_empty(&self) -> io::Result<bool> {
483 let mut entries = self.read_dir_async().await?;
484
485 entries
486 .next_entry()
487 .await
488 .map(|entry_opt| entry_opt.is_none())
489 }
490
491 #[inline]
492 async fn try_is_read_dir_empty_if_exists(&self) -> io::Result<Option<bool>> {
493 match self.try_is_read_dir_empty().await {
494 Ok(is_empty) => Ok(Some(is_empty)),
495 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
496 Err(err) => Err(err),
497 }
498 }
499
500 #[inline]
501 async fn create_new(&self) -> io::Result<File> {
502 File::create_new(self).await
503 }
504
505 #[inline]
506 async fn create_new_if_not_exists(&self) -> io::Result<Option<File>> {
507 File::create_new_if_not_exists(self).await
508 }
509
510 #[inline]
511 async fn create(&self) -> io::Result<File> {
512 File::create(self).await
513 }
514
515 #[inline]
516 async fn create_if_not_exists(&self) -> io::Result<Option<File>> {
517 File::create_if_not_exists(self).await
518 }
519
520 #[inline]
521 async fn open(&self) -> io::Result<File> {
522 File::open(self).await
523 }
524
525 #[inline]
526 async fn open_if_exists(&self) -> io::Result<Option<File>> {
527 File::open_if_exists(self).await
528 }
529
530 #[inline]
531 async fn read(&self) -> io::Result<Vec<u8>> {
532 fs::read(self).await
533 }
534
535 #[inline]
536 async fn read_if_exists(&self) -> io::Result<Option<Vec<u8>>> {
537 match self.read().await {
538 Ok(bytes) => Ok(Some(bytes)),
539 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
540 Err(err) => Err(err),
541 }
542 }
543
544 #[inline]
545 async fn read_to_string(&self) -> io::Result<String> {
546 fs::read_to_string(self).await
547 }
548
549 #[inline]
550 async fn read_to_string_if_exists(&self) -> io::Result<Option<String>> {
551 match self.read_to_string().await {
552 Ok(string) => Ok(Some(string)),
553 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
554 Err(err) => Err(err),
555 }
556 }
557
558 #[inline]
559 async fn read_lines(&self) -> io::Result<Lines<BufReader<File>>> {
560 let file = self.open().await?;
561
562 let buf_reader = BufReader::new(file);
563
564 let lines = buf_reader.lines();
565
566 Ok(lines)
567 }
568
569 #[inline]
570 async fn read_lines_if_exists(&self) -> io::Result<Option<Lines<BufReader<File>>>> {
571 match self.read_lines().await {
572 Ok(lines) => Ok(Some(lines)),
573 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
574 Err(err) => Err(err),
575 }
576 }
577
578 #[inline]
579 async fn create_dir_all(self) -> io::Result<Self> {
580 fs::create_dir_all(self.as_ref()).await?;
581
582 Ok(self)
583 }
584
585 #[inline]
586 async fn create_dir(self) -> io::Result<Self> {
587 fs::create_dir(self.as_ref()).await?;
588
589 Ok(self)
590 }
591
592 #[inline]
593 async fn create_dir_if_not_exists(self) -> io::Result<Option<Self>> {
594 match self.as_ref().create_dir().await {
595 Ok(_) => Ok(Some(self)),
596 Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
597 Err(err) => Err(err),
598 }
599 }
600
601 #[inline]
602 async fn write_new(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Self> {
603 let mut options = File::options();
604
605 options.write(true).create_new(true);
606
607 let mut file = options.open(self.as_ref()).await?;
608
609 file.write_all(contents.as_ref()).await?;
610
611 Ok(self)
612 }
613
614 #[inline]
615 async fn write_new_if_not_exists(
616 self,
617 contents: impl AsRef<[u8]> + Send,
618 ) -> io::Result<Option<Self>> {
619 match self.as_ref().write_new(contents).await {
620 Ok(_) => Ok(Some(self)),
621 Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
622 Err(err) => Err(err),
623 }
624 }
625
626 #[inline]
627 async fn write(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Self> {
628 fs::write(self.as_ref(), contents).await?;
629
630 Ok(self)
631 }
632
633 #[inline]
634 async fn write_if_exists(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<Self>> {
635 let mut options = File::options();
636
637 options.write(true).truncate(true);
638
639 match options.open(self.as_ref()).await {
640 Ok(mut file) => {
641 file.write_all(contents.as_ref()).await?;
642
643 Ok(Some(self))
644 },
645 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
646 Err(err) => Err(err),
647 }
648 }
649
650 #[inline]
651 async fn append(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Self> {
652 let mut options = File::options();
653
654 options.append(true).create(true);
655
656 let mut file = options.open(self.as_ref()).await?;
657
658 file.write_all(contents.as_ref()).await?;
659
660 Ok(self)
661 }
662
663 #[inline]
664 async fn append_if_exists(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<Self>> {
665 let mut options = File::options();
666
667 options.append(true);
668
669 match options.open(self.as_ref()).await {
670 Ok(mut file) => {
671 file.write_all(contents.as_ref()).await?;
672
673 Ok(Some(self))
674 },
675 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
676 Err(err) => Err(err),
677 }
678 }
679
680 #[inline]
681 async fn copy(self, to: impl AsRef<Path> + Send) -> io::Result<Self> {
682 fs::copy(self.as_ref(), to).await?;
683
684 Ok(self)
685 }
686
687 #[inline]
688 async fn copy_if_exists(self, to: impl AsRef<Path> + Send) -> io::Result<Option<Self>> {
689 match self.as_ref().copy(to).await {
690 Ok(_) => Ok(Some(self)),
691 Err(err) if err.kind() == io::ErrorKind::NotFound => {
692 if !self.try_exists_async().await? {
693 Ok(None)
694 } else {
695 Err(err)
696 }
697 },
698 Err(err) => Err(err),
699 }
700 }
701
702 #[inline]
703 async fn rename(self, to: impl AsRef<Path> + Send) -> io::Result<Self> {
704 fs::rename(self.as_ref(), to).await?;
705
706 Ok(self)
707 }
708
709 #[inline]
710 async fn rename_if_exists(self, to: impl AsRef<Path> + Send) -> io::Result<Option<Self>> {
711 match self.as_ref().rename(to).await {
712 Ok(_) => Ok(Some(self)),
713 Err(err) if err.kind() == io::ErrorKind::NotFound => {
714 if !self.try_exists_nofollow().await? {
715 Ok(None)
716 } else {
717 Err(err)
718 }
719 },
720 Err(err) => Err(err),
721 }
722 }
723
724 #[inline]
725 async fn remove_file(self) -> io::Result<Self> {
726 fs::remove_file(self.as_ref()).await?;
727
728 Ok(self)
729 }
730
731 #[inline]
732 async fn remove_file_if_exists(self) -> io::Result<Option<Self>> {
733 match self.as_ref().remove_file().await {
734 Ok(_) => Ok(Some(self)),
735 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
736 Err(err) => Err(err),
737 }
738 }
739
740 #[inline]
741 async fn remove_dir(self) -> io::Result<Self> {
742 fs::remove_dir(self.as_ref()).await?;
743
744 Ok(self)
745 }
746
747 #[inline]
748 async fn remove_dir_if_exists(self) -> io::Result<Option<Self>> {
749 match self.as_ref().remove_dir().await {
750 Ok(_) => Ok(Some(self)),
751 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
752 Err(err) => Err(err),
753 }
754 }
755
756 #[inline]
757 async fn remove_dir_all(self) -> io::Result<Self> {
758 fs::remove_dir_all(self.as_ref()).await?;
759
760 Ok(self)
761 }
762
763 #[inline]
764 async fn remove_dir_all_if_exists(self) -> io::Result<Option<Self>> {
765 match self.as_ref().remove_dir_all().await {
766 Ok(_) => Ok(Some(self)),
767 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
768 Err(err) => Err(err),
769 }
770 }
771
772 #[inline]
773 async fn hard_link(self, link: impl AsRef<Path> + Send) -> io::Result<Self> {
774 fs::hard_link(self.as_ref(), link).await?;
775
776 Ok(self)
777 }
778
779 #[inline]
780 async fn hard_link_if_exists(self, link: impl AsRef<Path> + Send) -> io::Result<Option<Self>> {
781 match self.as_ref().hard_link(link).await {
782 Ok(_) => Ok(Some(self)),
783 Err(err) if err.kind() == io::ErrorKind::NotFound => {
784 if !self.try_exists_nofollow().await? {
785 Ok(None)
786 } else {
787 Err(err)
788 }
789 },
790 Err(err) => Err(err),
791 }
792 }
793
794 #[inline]
795 async fn hard_link_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Self> {
796 let temp = TempPath::try_from_path(link.as_ref())?;
797
798 self.as_ref().hard_link(temp.path()).await?;
799
800 temp.persist(link).await?;
801
802 Ok(self)
803 }
804
805 #[inline]
806 async fn hard_link_atomic_if_exists(
807 self,
808 link: impl AsRef<Path> + Send,
809 ) -> io::Result<Option<Self>> {
810 match self.as_ref().hard_link_atomic(link).await {
811 Ok(_) => Ok(Some(self)),
812 Err(err) if err.kind() == io::ErrorKind::NotFound => {
813 if !self.try_exists_nofollow().await? {
814 Ok(None)
815 } else {
816 Err(err)
817 }
818 },
819 Err(err) => Err(err),
820 }
821 }
822
823 #[cfg(unix)]
824 #[inline]
825 async fn symlink(self, link: impl AsRef<Path> + Send) -> io::Result<Self> {
826 fs::symlink(self.as_ref(), link).await?;
827
828 Ok(self)
829 }
830
831 #[cfg(unix)]
832 #[inline]
833 async fn symlink_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Self> {
834 let temp = TempPath::try_from_path(link.as_ref())?;
835
836 self.as_ref().symlink(temp.path()).await?;
837
838 temp.persist(link).await?;
839
840 Ok(self)
841 }
842
843 #[cfg(unix)]
844 #[inline]
845 async fn symlink_absolute(self, link: impl AsRef<Path> + Send) -> io::Result<Self> {
846 #[expect(unstable_name_collisions)]
847 let absolute = self.as_ref().absolute()?;
848
849 absolute.symlink(link).await?;
850
851 Ok(self)
852 }
853
854 #[cfg(unix)]
855 #[inline]
856 async fn symlink_absolute_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Self> {
857 #[expect(unstable_name_collisions)]
858 let absolute = self.as_ref().absolute()?;
859
860 absolute.symlink_atomic(link).await?;
861
862 Ok(self)
863 }
864
865 #[cfg(unix)]
866 #[inline]
867 async fn symlink_relative(self, link: impl AsRef<Path> + Send + Sync) -> io::Result<Self> {
868 let base = link.base()?;
869
870 let relative = diff_paths(self.as_ref(), base)
871 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Failed to diff paths"))?;
872
873 relative.symlink(link).await?;
874
875 Ok(self)
876 }
877
878 #[cfg(unix)]
879 #[inline]
880 async fn symlink_relative_atomic(
881 self,
882 link: impl AsRef<Path> + Send + Sync,
883 ) -> io::Result<Self> {
884 let base = link.base()?;
885
886 let relative = diff_paths(self.as_ref(), base)
887 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Failed to diff paths"))?;
888
889 relative.symlink_atomic(link).await?;
890
891 Ok(self)
892 }
893
894 #[inline]
895 async fn set_permissions(self, perm: Permissions) -> io::Result<Self> {
896 fs::set_permissions(self.as_ref(), perm).await?;
897
898 Ok(self)
899 }
900
901 #[inline]
902 async fn set_permissions_if_exists(self, perm: Permissions) -> io::Result<Option<Self>> {
903 match self.as_ref().set_permissions(perm).await {
904 Ok(_) => Ok(Some(self)),
905 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
906 Err(err) => Err(err),
907 }
908 }
909
910 #[inline]
911 async fn set_permissions_readonly(self, readonly: bool) -> io::Result<Self> {
912 let meta = self.metadata_async().await?;
913
914 let mut perm = meta.permissions();
915
916 perm.set_readonly(readonly);
917
918 self.set_permissions(perm).await
919 }
920
921 #[inline]
922 async fn set_permissions_readonly_if_exists(self, readonly: bool) -> io::Result<Option<Self>> {
923 match self.as_ref().set_permissions_readonly(readonly).await {
924 Ok(_) => Ok(Some(self)),
925 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
926 Err(err) => Err(err),
927 }
928 }
929
930 #[cfg(unix)]
931 #[inline]
932 async fn set_permissions_mode(self, mode: u32) -> io::Result<Self> {
933 let perm = Permissions::from_mode(mode);
934
935 self.set_permissions(perm).await
936 }
937
938 #[cfg(unix)]
939 #[inline]
940 async fn set_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>> {
941 match self.as_ref().set_permissions_mode(mode).await {
942 Ok(_) => Ok(Some(self)),
943 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
944 Err(err) => Err(err),
945 }
946 }
947
948 #[cfg(unix)]
949 #[inline]
950 async fn add_permissions_mode(self, mode: u32) -> io::Result<Self> {
951 let meta = self.metadata_async().await?;
952
953 let perm = meta.permissions();
954
955 self.set_permissions_mode(perm.mode() | mode).await
956 }
957
958 #[cfg(unix)]
959 #[inline]
960 async fn add_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>> {
961 match self.as_ref().add_permissions_mode(mode).await {
962 Ok(_) => Ok(Some(self)),
963 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
964 Err(err) => Err(err),
965 }
966 }
967
968 #[cfg(unix)]
969 #[inline]
970 async fn remove_permissions_mode(self, mode: u32) -> io::Result<Self> {
971 let meta = self.metadata_async().await?;
972
973 let perm = meta.permissions();
974
975 self.set_permissions_mode(perm.mode() & !mode).await
976 }
977
978 #[cfg(unix)]
979 #[inline]
980 async fn remove_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>> {
981 match self.as_ref().remove_permissions_mode(mode).await {
982 Ok(_) => Ok(Some(self)),
983 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
984 Err(err) => Err(err),
985 }
986 }
987
988 #[cfg(unix)]
989 #[inline]
990 async fn chown(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Self> {
991 let this = self.as_ref().to_owned();
992
993 task::spawn_blocking(move || unix::fs::chown(this, uid, gid)).await??;
994
995 Ok(self)
996 }
997
998 #[cfg(unix)]
999 #[inline]
1000 async fn chown_if_exists(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<Self>> {
1001 match self.as_ref().chown(uid, gid).await {
1002 Ok(_) => Ok(Some(self)),
1003 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
1004 Err(err) => Err(err),
1005 }
1006 }
1007
1008 #[cfg(unix)]
1009 #[inline]
1010 async fn chown_nofollow(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Self> {
1011 let this = self.as_ref().to_owned();
1012
1013 task::spawn_blocking(move || unix::fs::lchown(this, uid, gid)).await??;
1014
1015 Ok(self)
1016 }
1017
1018 #[cfg(unix)]
1019 #[inline]
1020 async fn chown_nofollow_if_exists(
1021 self,
1022 uid: Option<u32>,
1023 gid: Option<u32>,
1024 ) -> io::Result<Option<Self>> {
1025 match self.as_ref().chown_nofollow(uid, gid).await {
1026 Ok(_) => Ok(Some(self)),
1027 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
1028 Err(err) => Err(err),
1029 }
1030 }
1031
1032 #[cfg(all(unix, not(target_os = "fuchsia")))]
1033 #[inline]
1034 async fn chroot(self) -> io::Result<Self> {
1035 let this = self.as_ref().to_owned();
1036
1037 task::spawn_blocking(|| unix::fs::chroot(this)).await??;
1038
1039 Ok(self)
1040 }
1041
1042 #[cfg(all(unix, not(target_os = "fuchsia")))]
1043 #[inline]
1044 async fn chroot_if_exists(self) -> io::Result<Option<Self>> {
1045 match self.as_ref().chroot().await {
1046 Ok(_) => Ok(Some(self)),
1047 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
1048 Err(err) => Err(err),
1049 }
1050 }
1051}
1052
1053macro_rules! define_option_path_ext {
1054 (
1055 $(
1056 $(#[$meta:meta])*
1057 async fn $method:ident(self $(, $arg:ident: $ty:ty)* $(,)*) -> $ret:ty;
1058 )*
1059 ) => {
1060 #[trait_variant::make(Send)]
1061 pub trait OptionPathExt<T> {
1062 $(
1063 $(#[$meta])*
1064 async fn $method(self $(, $arg: $ty)*) -> $ret;
1065 )*
1066 }
1067
1068 impl<T: PathExt> OptionPathExt<T> for Option<T> {
1069 $(
1070 #[inline]
1071 $(#[$meta])*
1072 async fn $method(self $(, $arg: $ty)*) -> $ret {
1073 match self {
1074 Some(path) => path.$method($($arg),*).await.map(Some),
1075 None => Ok(None),
1076 }
1077 }
1078 )*
1079 }
1080 };
1081}
1082
1083define_option_path_ext! {
1084 async fn metadata_async(self) -> io::Result<Option<Metadata>>;
1085 async fn symlink_metadata_async(self) -> io::Result<Option<Metadata>>;
1086 async fn canonicalize_async(self) -> io::Result<Option<PathBuf>>;
1087 async fn read_link_async(self) -> io::Result<Option<PathBuf>>;
1088 async fn read_dir_async(self) -> io::Result<Option<ReadDir>>;
1089
1090 async fn try_exists_async(self) -> io::Result<Option<bool>>;
1091
1092 async fn try_is_file(self) -> io::Result<Option<bool>>;
1093 async fn try_is_dir(self) -> io::Result<Option<bool>>;
1094 async fn try_is_symlink(self) -> io::Result<Option<bool>>;
1095
1096 async fn try_exists_nofollow(self) -> io::Result<Option<bool>>;
1097 async fn try_is_file_nofollow(self) -> io::Result<Option<bool>>;
1098 async fn try_is_dir_nofollow(self) -> io::Result<Option<bool>>;
1099
1100 async fn try_is_read_dir_empty(self) -> io::Result<Option<bool>>;
1101
1102 async fn create_new(self) -> io::Result<Option<File>>;
1103
1104 async fn create(self) -> io::Result<Option<File>>;
1105
1106 async fn open(self) -> io::Result<Option<File>>;
1107
1108 async fn read(self) -> io::Result<Option<Vec<u8>>>;
1109
1110 async fn read_to_string(self) -> io::Result<Option<String>>;
1111
1112 async fn read_lines(self) -> io::Result<Option<Lines<BufReader<File>>>>;
1113
1114 async fn create_dir_all(self) -> io::Result<Option<T>>;
1115
1116 async fn create_dir(self) -> io::Result<Option<T>>;
1117
1118 async fn write_new(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1119
1120 async fn write(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1121
1122 async fn append(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1123
1124 async fn copy(self, to: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1125
1126 async fn rename(self, to: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1127
1128 async fn remove_file(self) -> io::Result<Option<T>>;
1129
1130 async fn remove_dir(self) -> io::Result<Option<T>>;
1131
1132 async fn remove_dir_all(self) -> io::Result<Option<T>>;
1133
1134 async fn hard_link(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1135 async fn hard_link_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1136
1137 #[cfg(unix)]
1138 async fn symlink(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1139 #[cfg(unix)]
1140 async fn symlink_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1141
1142 #[cfg(unix)]
1143 async fn symlink_absolute(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1144 #[cfg(unix)]
1145 async fn symlink_absolute_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1146
1147 #[cfg(unix)]
1148 async fn symlink_relative(self, link: impl AsRef<Path> + Send + Sync) -> io::Result<Option<T>>;
1149 #[cfg(unix)]
1150 async fn symlink_relative_atomic(
1151 self,
1152 link: impl AsRef<Path> + Send + Sync,
1153 ) -> io::Result<Option<T>>;
1154
1155 async fn set_permissions(self, perm: Permissions) -> io::Result<Option<T>>;
1156
1157 async fn set_permissions_readonly(self, readonly: bool) -> io::Result<Option<T>>;
1158
1159 #[cfg(unix)]
1160 async fn set_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
1161 #[cfg(unix)]
1162 async fn add_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
1163 #[cfg(unix)]
1164 async fn remove_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
1165
1166 #[cfg(unix)]
1167 async fn chown(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<T>>;
1168
1169 #[cfg(unix)]
1170 async fn chown_nofollow(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<T>>;
1171
1172 #[cfg(all(unix, not(target_os = "fuchsia")))]
1173 async fn chroot(self) -> io::Result<Option<T>>;
1174}
1175
1176macro_rules! define_async_path_ext {
1177 (
1178 $(
1179 $(#[$meta:meta])*
1180 async fn $method:ident(self $(, $arg:ident: $ty:ty)* $(,)*) -> $ret:ty;
1181 )*
1182 ) => {
1183 #[trait_variant::make(Send)]
1184 pub trait AsyncPathExt<T: PathExt>: Future<Output = io::Result<T>> {
1185 $(
1186 $(#[$meta])*
1187 async fn $method(self $(, $arg: $ty)*) -> $ret;
1188 )*
1189 }
1190
1191 impl<T: PathExt, F: Future<Output = io::Result<T>> + Send> AsyncPathExt<T> for F {
1192 $(
1193 #[inline]
1194 $(#[$meta])*
1195 async fn $method(self $(, $arg: $ty)*) -> $ret {
1196 let path = self.await?;
1197
1198 path.$method($($arg),*).await
1199 }
1200 )*
1201 }
1202 };
1203}
1204
1205define_async_path_ext! {
1206 async fn metadata_async(self) -> io::Result<Metadata>;
1207 async fn symlink_metadata_async(self) -> io::Result<Metadata>;
1208 async fn canonicalize_async(self) -> io::Result<PathBuf>;
1209 async fn read_link_async(self) -> io::Result<PathBuf>;
1210 async fn read_dir_async(self) -> io::Result<ReadDir>;
1211
1212 async fn try_exists_async(self) -> io::Result<bool>;
1213
1214 async fn try_is_file(self) -> io::Result<bool>;
1215 async fn try_is_dir(self) -> io::Result<bool>;
1216 async fn try_is_symlink(self) -> io::Result<bool>;
1217
1218 async fn metadata_if_exists(self) -> io::Result<Option<Metadata>>;
1219 async fn symlink_metadata_if_exists(self) -> io::Result<Option<Metadata>>;
1220 async fn canonicalize_if_exists(self) -> io::Result<Option<PathBuf>>;
1221 async fn read_link_if_exists(self) -> io::Result<Option<PathBuf>>;
1222 async fn read_dir_if_exists(self) -> io::Result<Option<ReadDir>>;
1223
1224 async fn try_exists_nofollow(self) -> io::Result<bool>;
1225 async fn try_is_file_nofollow(self) -> io::Result<bool>;
1226 async fn try_is_dir_nofollow(self) -> io::Result<bool>;
1227
1228 async fn try_is_read_dir_empty(self) -> io::Result<bool>;
1229 async fn try_is_read_dir_empty_if_exists(self) -> io::Result<Option<bool>>;
1230
1231 async fn create_new(self) -> io::Result<File>;
1232 async fn create_new_if_not_exists(self) -> io::Result<Option<File>>;
1233
1234 async fn create(self) -> io::Result<File>;
1235 async fn create_if_not_exists(self) -> io::Result<Option<File>>;
1236
1237 async fn open(self) -> io::Result<File>;
1238 async fn open_if_exists(self) -> io::Result<Option<File>>;
1239
1240 async fn read(self) -> io::Result<Vec<u8>>;
1241 async fn read_if_exists(self) -> io::Result<Option<Vec<u8>>>;
1242
1243 async fn read_to_string(self) -> io::Result<String>;
1244 async fn read_to_string_if_exists(self) -> io::Result<Option<String>>;
1245
1246 async fn read_lines(self) -> io::Result<Lines<BufReader<File>>>;
1247 async fn read_lines_if_exists(self) -> io::Result<Option<Lines<BufReader<File>>>>;
1248
1249 async fn create_dir_all(self) -> io::Result<T>;
1250
1251 async fn create_dir(self) -> io::Result<T>;
1252 async fn create_dir_if_not_exists(self) -> io::Result<Option<T>>;
1253
1254 async fn write_new(self, contents: impl AsRef<[u8]> + Send) -> io::Result<T>;
1255 async fn write_new_if_not_exists(
1256 self,
1257 contents: impl AsRef<[u8]> + Send,
1258 ) -> io::Result<Option<T>>;
1259
1260 async fn write(self, contents: impl AsRef<[u8]> + Send) -> io::Result<T>;
1261 async fn write_if_exists(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1262
1263 async fn append(self, contents: impl AsRef<[u8]> + Send) -> io::Result<T>;
1264 async fn append_if_exists(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1265
1266 async fn copy(self, to: impl AsRef<Path> + Send) -> io::Result<T>;
1267 async fn copy_if_exists(self, to: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1268
1269 async fn rename(self, to: impl AsRef<Path> + Send) -> io::Result<T>;
1270 async fn rename_if_exists(self, to: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1271
1272 async fn remove_file(self) -> io::Result<T>;
1273 async fn remove_file_if_exists(self) -> io::Result<Option<T>>;
1274
1275 async fn remove_dir(self) -> io::Result<T>;
1276 async fn remove_dir_if_exists(self) -> io::Result<Option<T>>;
1277
1278 async fn remove_dir_all(self) -> io::Result<T>;
1279 async fn remove_dir_all_if_exists(self) -> io::Result<Option<T>>;
1280
1281 async fn hard_link(self, link: impl AsRef<Path> + Send) -> io::Result<T>;
1282 async fn hard_link_if_exists(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1283 async fn hard_link_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<T>;
1284 async fn hard_link_atomic_if_exists(
1285 self,
1286 link: impl AsRef<Path> + Send,
1287 ) -> io::Result<Option<T>>;
1288
1289 #[cfg(unix)]
1290 async fn symlink(self, link: impl AsRef<Path> + Send) -> io::Result<T>;
1291 #[cfg(unix)]
1292 async fn symlink_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<T>;
1293
1294 #[cfg(unix)]
1295 async fn symlink_absolute(self, link: impl AsRef<Path> + Send) -> io::Result<T>;
1296 #[cfg(unix)]
1297 async fn symlink_absolute_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<T>;
1298
1299 #[cfg(unix)]
1300 async fn symlink_relative(self, link: impl AsRef<Path> + Send + Sync) -> io::Result<T>;
1301 #[cfg(unix)]
1302 async fn symlink_relative_atomic(self, link: impl AsRef<Path> + Send + Sync) -> io::Result<T>;
1303
1304 async fn set_permissions(self, perm: Permissions) -> io::Result<T>;
1305 async fn set_permissions_if_exists(self, perm: Permissions) -> io::Result<Option<T>>;
1306
1307 async fn set_permissions_readonly(self, readonly: bool) -> io::Result<T>;
1308 async fn set_permissions_readonly_if_exists(self, readonly: bool) -> io::Result<Option<T>>;
1309
1310 #[cfg(unix)]
1311 async fn set_permissions_mode(self, mode: u32) -> io::Result<T>;
1312 #[cfg(unix)]
1313 async fn set_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<T>>;
1314 #[cfg(unix)]
1315 async fn add_permissions_mode(self, mode: u32) -> io::Result<T>;
1316 #[cfg(unix)]
1317 async fn add_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<T>>;
1318 #[cfg(unix)]
1319 async fn remove_permissions_mode(self, mode: u32) -> io::Result<T>;
1320 #[cfg(unix)]
1321 async fn remove_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<T>>;
1322
1323 #[cfg(unix)]
1324 async fn chown(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<T>;
1325 #[cfg(unix)]
1326 async fn chown_if_exists(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<T>>;
1327
1328 #[cfg(unix)]
1329 async fn chown_nofollow(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<T>;
1330 #[cfg(unix)]
1331 async fn chown_nofollow_if_exists(
1332 self,
1333 uid: Option<u32>,
1334 gid: Option<u32>,
1335 ) -> io::Result<Option<T>>;
1336
1337 #[cfg(all(unix, not(target_os = "fuchsia")))]
1338 async fn chroot(self) -> io::Result<T>;
1339 #[cfg(all(unix, not(target_os = "fuchsia")))]
1340 async fn chroot_if_exists(self) -> io::Result<Option<T>>;
1341}
1342
1343macro_rules! define_async_option_path_ext {
1344 (
1345 $(
1346 $(#[$meta:meta])*
1347 async fn $method:ident(self $(, $arg:ident: $ty:ty)* $(,)*) -> $ret:ty;
1348 )*
1349 ) => {
1350 #[trait_variant::make(Send)]
1351 pub trait AsyncOptionPathExt<T: PathExt>: Future<Output = io::Result<Option<T>>>
1352 where
1353 Option<T>: OptionPathExt<T>,
1354 {
1355 $(
1356 $(#[$meta])*
1357 async fn $method(self $(, $arg: $ty)*) -> $ret;
1358 )*
1359 }
1360
1361 impl<T: PathExt, F: Future<Output = io::Result<Option<T>>> + Send> AsyncOptionPathExt<T>
1362 for F
1363 where
1364 Option<T>: OptionPathExt<T>,
1365 {
1366 $(
1367 #[inline]
1368 $(#[$meta])*
1369 async fn $method(self $(, $arg: $ty)*) -> $ret {
1370 let path = self.await?;
1371
1372 path.$method($($arg),*).await
1373 }
1374 )*
1375 }
1376 };
1377}
1378
1379define_async_option_path_ext! {
1380 async fn metadata_async(self) -> io::Result<Option<Metadata>>;
1381 async fn symlink_metadata_async(self) -> io::Result<Option<Metadata>>;
1382 async fn canonicalize_async(self) -> io::Result<Option<PathBuf>>;
1383 async fn read_link_async(self) -> io::Result<Option<PathBuf>>;
1384 async fn read_dir_async(self) -> io::Result<Option<ReadDir>>;
1385
1386 async fn try_exists_async(self) -> io::Result<Option<bool>>;
1387
1388 async fn try_is_file(self) -> io::Result<Option<bool>>;
1389 async fn try_is_dir(self) -> io::Result<Option<bool>>;
1390 async fn try_is_symlink(self) -> io::Result<Option<bool>>;
1391
1392 async fn try_exists_nofollow(self) -> io::Result<Option<bool>>;
1393 async fn try_is_file_nofollow(self) -> io::Result<Option<bool>>;
1394 async fn try_is_dir_nofollow(self) -> io::Result<Option<bool>>;
1395
1396 async fn try_is_read_dir_empty(self) -> io::Result<Option<bool>>;
1397
1398 async fn create_new(self) -> io::Result<Option<File>>;
1399
1400 async fn create(self) -> io::Result<Option<File>>;
1401
1402 async fn open(self) -> io::Result<Option<File>>;
1403
1404 async fn read(self) -> io::Result<Option<Vec<u8>>>;
1405
1406 async fn read_to_string(self) -> io::Result<Option<String>>;
1407
1408 async fn read_lines(self) -> io::Result<Option<Lines<BufReader<File>>>>;
1409
1410 async fn create_dir_all(self) -> io::Result<Option<T>>;
1411
1412 async fn create_dir(self) -> io::Result<Option<T>>;
1413
1414 async fn write_new(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1415
1416 async fn write(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1417
1418 async fn append(self, contents: impl AsRef<[u8]> + Send) -> io::Result<Option<T>>;
1419
1420 async fn copy(self, to: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1421
1422 async fn rename(self, to: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1423
1424 async fn remove_file(self) -> io::Result<Option<T>>;
1425
1426 async fn remove_dir(self) -> io::Result<Option<T>>;
1427
1428 async fn remove_dir_all(self) -> io::Result<Option<T>>;
1429
1430 async fn hard_link(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1431 async fn hard_link_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1432
1433 #[cfg(unix)]
1434 async fn symlink(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1435 #[cfg(unix)]
1436 async fn symlink_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1437
1438 #[cfg(unix)]
1439 async fn symlink_absolute(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1440 #[cfg(unix)]
1441 async fn symlink_absolute_atomic(self, link: impl AsRef<Path> + Send) -> io::Result<Option<T>>;
1442
1443 #[cfg(unix)]
1444 async fn symlink_relative(self, link: impl AsRef<Path> + Send + Sync) -> io::Result<Option<T>>;
1445 #[cfg(unix)]
1446 async fn symlink_relative_atomic(
1447 self,
1448 link: impl AsRef<Path> + Send + Sync,
1449 ) -> io::Result<Option<T>>;
1450
1451 async fn set_permissions(self, perm: Permissions) -> io::Result<Option<T>>;
1452
1453 async fn set_permissions_readonly(self, readonly: bool) -> io::Result<Option<T>>;
1454
1455 #[cfg(unix)]
1456 async fn set_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
1457 #[cfg(unix)]
1458 async fn add_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
1459 #[cfg(unix)]
1460 async fn remove_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
1461
1462 #[cfg(unix)]
1463 async fn chown(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<T>>;
1464
1465 #[cfg(unix)]
1466 async fn chown_nofollow(self, uid: Option<u32>, gid: Option<u32>) -> io::Result<Option<T>>;
1467
1468 #[cfg(all(unix, not(target_os = "fuchsia")))]
1469 async fn chroot(self) -> io::Result<Option<T>>;
1470}