Skip to main content

path_extra/tokio/
path.rs

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}