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}