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