Skip to main content

path_extra/tokio/
path.rs

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