Skip to main content

path_extra/tokio/
path.rs

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