1#[cfg(unix)]
2use std::os::unix::{self, fs::PermissionsExt as _};
3use std::{
4 borrow::Cow,
5 ffi::OsStr,
6 fs::{self, File, Metadata, Permissions, ReadDir},
7 io::{self, Write},
8 path::{self, Path, PathBuf},
9};
10
11#[cfg(unix)]
12use pathdiff::diff_paths;
13
14use super::{fs::FileExt, temp_path::TempPath};
15
16pub trait PathExt {
17 fn base(&self) -> io::Result<&Path>;
18 fn with_base(&self, base: impl AsRef<Path>) -> Cow<'_, Path>;
19
20 fn file_suffix(&self) -> Option<&OsStr>;
21
22 fn absolute(&self) -> io::Result<PathBuf>;
23
24 fn metadata(&self) -> io::Result<Metadata>;
25 fn symlink_metadata(&self) -> io::Result<Metadata>;
26 fn canonicalize(&self) -> io::Result<PathBuf>;
27 fn read_link(&self) -> io::Result<PathBuf>;
28 fn read_dir(&self) -> io::Result<ReadDir>;
29
30 fn try_exists(&self) -> io::Result<bool>;
31
32 fn try_is_file(&self) -> io::Result<bool>;
33 fn try_is_dir(&self) -> io::Result<bool>;
34 fn try_is_symlink(&self) -> io::Result<bool>;
35
36 fn metadata_if_exists(&self) -> io::Result<Option<Metadata>>;
37 fn symlink_metadata_if_exists(&self) -> io::Result<Option<Metadata>>;
38 fn canonicalize_if_exists(&self) -> io::Result<Option<PathBuf>>;
39 fn read_link_if_exists(&self) -> io::Result<Option<PathBuf>>;
40 fn read_dir_if_exists(&self) -> io::Result<Option<ReadDir>>;
41
42 fn exists_nofollow(&self) -> bool;
43 fn is_file_nofollow(&self) -> bool;
44 fn is_dir_nofollow(&self) -> bool;
45
46 fn try_exists_nofollow(&self) -> io::Result<bool>;
47 fn try_is_file_nofollow(&self) -> io::Result<bool>;
48 fn try_is_dir_nofollow(&self) -> io::Result<bool>;
49
50 fn try_is_read_dir_empty(&self) -> io::Result<bool>;
51 fn try_is_read_dir_empty_if_exists(&self) -> io::Result<Option<bool>>;
52
53 fn create_new(&self) -> io::Result<File>;
54 fn create_new_if_not_exists(&self) -> io::Result<Option<File>>;
55
56 fn create(&self) -> io::Result<File>;
57 fn create_if_not_exists(&self) -> io::Result<Option<File>>;
58
59 fn open(&self) -> io::Result<File>;
60 fn open_if_exists(&self) -> io::Result<Option<File>>;
61
62 fn read(&self) -> io::Result<Vec<u8>>;
63 fn read_if_exists(&self) -> io::Result<Option<Vec<u8>>>;
64
65 fn read_to_string(&self) -> io::Result<String>;
66 fn read_to_string_if_exists(&self) -> io::Result<Option<String>>;
67
68 fn create_dir_all(self) -> io::Result<Self>
69 where
70 Self: Sized;
71
72 fn create_dir(self) -> io::Result<Self>
73 where
74 Self: Sized;
75 fn create_dir_if_not_exists(self) -> io::Result<Option<Self>>
76 where
77 Self: Sized;
78
79 fn write_new(self, contents: impl AsRef<[u8]>) -> io::Result<Self>
80 where
81 Self: Sized;
82 fn write_new_if_not_exists(self, contents: impl AsRef<[u8]>) -> io::Result<Option<Self>>
83 where
84 Self: Sized;
85
86 fn write(self, contents: impl AsRef<[u8]>) -> io::Result<Self>
87 where
88 Self: Sized;
89 fn write_if_exists(self, contents: impl AsRef<[u8]>) -> io::Result<Option<Self>>
90 where
91 Self: Sized;
92
93 fn append(self, contents: impl AsRef<[u8]>) -> io::Result<Self>
94 where
95 Self: Sized;
96 fn append_if_exists(self, contents: impl AsRef<[u8]>) -> io::Result<Option<Self>>
97 where
98 Self: Sized;
99
100 fn copy(self, to: impl AsRef<Path>) -> io::Result<Self>
101 where
102 Self: Sized;
103 fn copy_if_exists(self, to: impl AsRef<Path>) -> io::Result<Option<Self>>
104 where
105 Self: Sized;
106
107 fn rename(self, to: impl AsRef<Path>) -> io::Result<Self>
108 where
109 Self: Sized;
110 fn rename_if_exists(self, to: impl AsRef<Path>) -> io::Result<Option<Self>>
111 where
112 Self: Sized;
113
114 fn remove_file(self) -> io::Result<Self>
115 where
116 Self: Sized;
117 fn remove_file_if_exists(self) -> io::Result<Option<Self>>
118 where
119 Self: Sized;
120
121 fn remove_dir(self) -> io::Result<Self>
122 where
123 Self: Sized;
124 fn remove_dir_if_exists(self) -> io::Result<Option<Self>>
125 where
126 Self: Sized;
127
128 fn remove_dir_all(self) -> io::Result<Self>
129 where
130 Self: Sized;
131 fn remove_dir_all_if_exists(self) -> io::Result<Option<Self>>
132 where
133 Self: Sized;
134
135 fn hard_link(self, link: impl AsRef<Path>) -> io::Result<Self>
136 where
137 Self: Sized;
138 fn hard_link_if_exists(self, link: impl AsRef<Path>) -> io::Result<Option<Self>>
139 where
140 Self: Sized;
141 fn hard_link_atomic(self, link: impl AsRef<Path>) -> io::Result<Self>
142 where
143 Self: Sized;
144 fn hard_link_atomic_if_exists(self, link: impl AsRef<Path>) -> io::Result<Option<Self>>
145 where
146 Self: Sized;
147
148 #[cfg(unix)]
149 fn symlink(self, link: impl AsRef<Path>) -> io::Result<Self>
150 where
151 Self: Sized;
152 #[cfg(unix)]
153 fn symlink_atomic(self, link: impl AsRef<Path>) -> io::Result<Self>
154 where
155 Self: Sized;
156
157 #[cfg(unix)]
158 fn symlink_absolute(self, link: impl AsRef<Path>) -> io::Result<Self>
159 where
160 Self: Sized;
161 #[cfg(unix)]
162 fn symlink_absolute_atomic(self, link: impl AsRef<Path>) -> io::Result<Self>
163 where
164 Self: Sized;
165
166 #[cfg(unix)]
167 fn symlink_relative(self, link: impl AsRef<Path>) -> io::Result<Self>
168 where
169 Self: Sized;
170 #[cfg(unix)]
171 fn symlink_relative_atomic(self, link: impl AsRef<Path>) -> io::Result<Self>
172 where
173 Self: Sized;
174
175 fn set_permissions(self, perm: Permissions) -> io::Result<Self>
176 where
177 Self: Sized;
178 fn set_permissions_if_exists(self, perm: Permissions) -> io::Result<Option<Self>>
179 where
180 Self: Sized;
181
182 fn set_permissions_readonly(self, readonly: bool) -> io::Result<Self>
183 where
184 Self: Sized;
185 fn set_permissions_readonly_if_exists(self, readonly: bool) -> io::Result<Option<Self>>
186 where
187 Self: Sized;
188
189 #[cfg(unix)]
190 fn set_permissions_mode(self, mode: u32) -> io::Result<Self>
191 where
192 Self: Sized;
193 #[cfg(unix)]
194 fn set_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>>
195 where
196 Self: Sized;
197 #[cfg(unix)]
198 fn add_permissions_mode(self, mode: u32) -> io::Result<Self>
199 where
200 Self: Sized;
201 #[cfg(unix)]
202 fn add_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>>
203 where
204 Self: Sized;
205 #[cfg(unix)]
206 fn remove_permissions_mode(self, mode: u32) -> io::Result<Self>
207 where
208 Self: Sized;
209 #[cfg(unix)]
210 fn remove_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>>
211 where
212 Self: Sized;
213}
214
215impl<T: AsRef<Path>> PathExt for T {
216 #[inline]
217 fn base(&self) -> io::Result<&Path> {
218 self.as_ref()
219 .parent()
220 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Path has no parent"))
221 }
222
223 #[inline]
224 fn with_base(&self, base: impl AsRef<Path>) -> Cow<'_, Path> {
225 if self.as_ref().is_relative() {
226 let path = base.as_ref().join(self);
227
228 Cow::Owned(path)
229 } else {
230 Cow::Borrowed(self.as_ref())
231 }
232 }
233
234 #[inline]
235 fn file_suffix(&self) -> Option<&OsStr> {
236 let file_name = self.as_ref().file_name()?;
237 let file_prefix = self.as_ref().file_prefix()?;
238
239 let bytes = file_name.as_encoded_bytes();
240
241 let start = file_prefix.as_encoded_bytes().len() + 1;
242
243 if start <= bytes.len() {
244 let slice = &bytes[start..];
245
246 let os_str = unsafe { OsStr::from_encoded_bytes_unchecked(slice) };
247
248 Some(os_str)
249 } else {
250 None
251 }
252 }
253
254 #[inline]
255 fn absolute(&self) -> io::Result<PathBuf> {
256 path::absolute(self)
257 }
258
259 #[inline]
260 fn metadata(&self) -> io::Result<Metadata> {
261 self.as_ref().metadata()
262 }
263
264 #[inline]
265 fn symlink_metadata(&self) -> io::Result<Metadata> {
266 self.as_ref().symlink_metadata()
267 }
268
269 #[inline]
270 fn canonicalize(&self) -> io::Result<PathBuf> {
271 self.as_ref().canonicalize()
272 }
273
274 #[inline]
275 fn read_link(&self) -> io::Result<PathBuf> {
276 self.as_ref().read_link()
277 }
278
279 #[inline]
280 fn read_dir(&self) -> io::Result<ReadDir> {
281 self.as_ref().read_dir()
282 }
283
284 #[inline]
285 fn try_exists(&self) -> io::Result<bool> {
286 self.as_ref().try_exists()
287 }
288
289 #[inline]
290 fn try_is_file(&self) -> io::Result<bool> {
291 self.metadata_if_exists()
292 .map(|meta_opt| meta_opt.map(|meta| meta.is_file()).unwrap_or(false))
293 }
294
295 #[inline]
296 fn try_is_dir(&self) -> io::Result<bool> {
297 self.metadata_if_exists()
298 .map(|meta_opt| meta_opt.map(|meta| meta.is_dir()).unwrap_or(false))
299 }
300
301 #[inline]
302 fn try_is_symlink(&self) -> io::Result<bool> {
303 self.symlink_metadata_if_exists()
304 .map(|meta_opt| meta_opt.map(|meta| meta.is_symlink()).unwrap_or(false))
305 }
306
307 #[inline]
308 fn metadata_if_exists(&self) -> io::Result<Option<Metadata>> {
309 match self.metadata() {
310 Ok(meta) => Ok(Some(meta)),
311 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
312 Err(err) => Err(err),
313 }
314 }
315
316 #[inline]
317 fn symlink_metadata_if_exists(&self) -> io::Result<Option<Metadata>> {
318 match self.symlink_metadata() {
319 Ok(meta) => Ok(Some(meta)),
320 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
321 Err(err) => Err(err),
322 }
323 }
324
325 #[inline]
326 fn canonicalize_if_exists(&self) -> io::Result<Option<PathBuf>> {
327 match self.canonicalize() {
328 Ok(path) => Ok(Some(path)),
329 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
330 Err(err) => Err(err),
331 }
332 }
333
334 #[inline]
335 fn read_link_if_exists(&self) -> io::Result<Option<PathBuf>> {
336 match self.read_link() {
337 Ok(path) => Ok(Some(path)),
338 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
339 Err(err) => Err(err),
340 }
341 }
342
343 #[inline]
344 fn read_dir_if_exists(&self) -> io::Result<Option<ReadDir>> {
345 match self.read_dir() {
346 Ok(entries) => Ok(Some(entries)),
347 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
348 Err(err) => Err(err),
349 }
350 }
351
352 #[inline]
353 fn exists_nofollow(&self) -> bool {
354 self.symlink_metadata().is_ok()
355 }
356
357 #[inline]
358 fn is_file_nofollow(&self) -> bool {
359 self.symlink_metadata()
360 .map(|meta| meta.is_file())
361 .unwrap_or(false)
362 }
363
364 #[inline]
365 fn is_dir_nofollow(&self) -> bool {
366 self.symlink_metadata()
367 .map(|meta| meta.is_dir())
368 .unwrap_or(false)
369 }
370
371 #[inline]
372 fn try_exists_nofollow(&self) -> io::Result<bool> {
373 self.symlink_metadata_if_exists()
374 .map(|meta_opt| meta_opt.is_some())
375 }
376
377 #[inline]
378 fn try_is_file_nofollow(&self) -> io::Result<bool> {
379 self.symlink_metadata_if_exists()
380 .map(|meta_opt| meta_opt.map(|meta| meta.is_file()).unwrap_or(false))
381 }
382
383 #[inline]
384 fn try_is_dir_nofollow(&self) -> io::Result<bool> {
385 self.symlink_metadata_if_exists()
386 .map(|meta_opt| meta_opt.map(|meta| meta.is_dir()).unwrap_or(false))
387 }
388
389 #[inline]
390 fn try_is_read_dir_empty(&self) -> io::Result<bool> {
391 let mut entries = self.read_dir()?;
392
393 entries
394 .next()
395 .transpose()
396 .map(|entry_opt| entry_opt.is_none())
397 }
398
399 #[inline]
400 fn try_is_read_dir_empty_if_exists(&self) -> io::Result<Option<bool>> {
401 match self.try_is_read_dir_empty() {
402 Ok(is_empty) => Ok(Some(is_empty)),
403 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
404 Err(err) => Err(err),
405 }
406 }
407
408 #[inline]
409 fn create_new(&self) -> io::Result<File> {
410 File::create_new(self)
411 }
412
413 #[inline]
414 fn create_new_if_not_exists(&self) -> io::Result<Option<File>> {
415 File::create_new_if_not_exists(self)
416 }
417
418 #[inline]
419 fn create(&self) -> io::Result<File> {
420 File::create(self)
421 }
422
423 #[inline]
424 fn create_if_not_exists(&self) -> io::Result<Option<File>> {
425 File::create_if_not_exists(self)
426 }
427
428 #[inline]
429 fn open(&self) -> io::Result<File> {
430 File::open(self)
431 }
432
433 #[inline]
434 fn open_if_exists(&self) -> io::Result<Option<File>> {
435 File::open_if_exists(self)
436 }
437
438 #[inline]
439 fn read(&self) -> io::Result<Vec<u8>> {
440 fs::read(self)
441 }
442
443 #[inline]
444 fn read_if_exists(&self) -> io::Result<Option<Vec<u8>>> {
445 match self.read() {
446 Ok(bytes) => Ok(Some(bytes)),
447 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
448 Err(err) => Err(err),
449 }
450 }
451
452 #[inline]
453 fn read_to_string(&self) -> io::Result<String> {
454 fs::read_to_string(self)
455 }
456
457 #[inline]
458 fn read_to_string_if_exists(&self) -> io::Result<Option<String>> {
459 match self.read_to_string() {
460 Ok(string) => Ok(Some(string)),
461 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
462 Err(err) => Err(err),
463 }
464 }
465
466 #[inline]
467 fn create_dir_all(self) -> io::Result<Self> {
468 fs::create_dir_all(self.as_ref())?;
469
470 Ok(self)
471 }
472
473 #[inline]
474 fn create_dir(self) -> io::Result<Self> {
475 fs::create_dir(self.as_ref())?;
476
477 Ok(self)
478 }
479
480 #[inline]
481 fn create_dir_if_not_exists(self) -> io::Result<Option<Self>> {
482 match self.as_ref().create_dir() {
483 Ok(_) => Ok(Some(self)),
484 Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
485 Err(err) => Err(err),
486 }
487 }
488
489 #[inline]
490 fn write_new(self, contents: impl AsRef<[u8]>) -> io::Result<Self> {
491 let mut options = File::options();
492
493 options.write(true).create_new(true);
494
495 let mut file = options.open(self.as_ref())?;
496
497 file.write_all(contents.as_ref())?;
498
499 Ok(self)
500 }
501
502 #[inline]
503 fn write_new_if_not_exists(self, contents: impl AsRef<[u8]>) -> io::Result<Option<Self>> {
504 match self.as_ref().write_new(contents) {
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 fn write(self, contents: impl AsRef<[u8]>) -> io::Result<Self> {
513 fs::write(self.as_ref(), contents)?;
514
515 Ok(self)
516 }
517
518 #[inline]
519 fn write_if_exists(self, contents: impl AsRef<[u8]>) -> 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()) {
525 Ok(mut file) => {
526 file.write_all(contents.as_ref())?;
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 fn append(self, contents: impl AsRef<[u8]>) -> 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())?;
542
543 file.write_all(contents.as_ref())?;
544
545 Ok(self)
546 }
547
548 #[inline]
549 fn append_if_exists(self, contents: impl AsRef<[u8]>) -> io::Result<Option<Self>> {
550 let mut options = File::options();
551
552 options.append(true);
553
554 match options.open(self.as_ref()) {
555 Ok(mut file) => {
556 file.write_all(contents.as_ref())?;
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 fn copy(self, to: impl AsRef<Path>) -> io::Result<Self> {
567 fs::copy(self.as_ref(), to)?;
568
569 Ok(self)
570 }
571
572 #[inline]
573 fn copy_if_exists(self, to: impl AsRef<Path>) -> io::Result<Option<Self>> {
574 match self.as_ref().copy(to) {
575 Ok(_) => Ok(Some(self)),
576 Err(err) if err.kind() == io::ErrorKind::NotFound => {
577 if !self.try_exists()? {
578 Ok(None)
579 } else {
580 Err(err)
581 }
582 },
583 Err(err) => Err(err),
584 }
585 }
586
587 #[inline]
588 fn rename(self, to: impl AsRef<Path>) -> io::Result<Self> {
589 fs::rename(self.as_ref(), to)?;
590
591 Ok(self)
592 }
593
594 #[inline]
595 fn rename_if_exists(self, to: impl AsRef<Path>) -> io::Result<Option<Self>> {
596 match self.as_ref().rename(to) {
597 Ok(_) => Ok(Some(self)),
598 Err(err) if err.kind() == io::ErrorKind::NotFound => {
599 if !self.try_exists_nofollow()? {
600 Ok(None)
601 } else {
602 Err(err)
603 }
604 },
605 Err(err) => Err(err),
606 }
607 }
608
609 #[inline]
610 fn remove_file(self) -> io::Result<Self> {
611 fs::remove_file(self.as_ref())?;
612
613 Ok(self)
614 }
615
616 #[inline]
617 fn remove_file_if_exists(self) -> io::Result<Option<Self>> {
618 match self.as_ref().remove_file() {
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 fn remove_dir(self) -> io::Result<Self> {
627 fs::remove_dir(self.as_ref())?;
628
629 Ok(self)
630 }
631
632 #[inline]
633 fn remove_dir_if_exists(self) -> io::Result<Option<Self>> {
634 match self.as_ref().remove_dir() {
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 fn remove_dir_all(self) -> io::Result<Self> {
643 fs::remove_dir_all(self.as_ref())?;
644
645 Ok(self)
646 }
647
648 #[inline]
649 fn remove_dir_all_if_exists(self) -> io::Result<Option<Self>> {
650 match self.as_ref().remove_dir_all() {
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 fn hard_link(self, link: impl AsRef<Path>) -> io::Result<Self> {
659 fs::hard_link(self.as_ref(), link)?;
660
661 Ok(self)
662 }
663
664 #[inline]
665 fn hard_link_if_exists(self, link: impl AsRef<Path>) -> io::Result<Option<Self>> {
666 match self.as_ref().hard_link(link) {
667 Ok(_) => Ok(Some(self)),
668 Err(err) if err.kind() == io::ErrorKind::NotFound => {
669 if !self.try_exists_nofollow()? {
670 Ok(None)
671 } else {
672 Err(err)
673 }
674 },
675 Err(err) => Err(err),
676 }
677 }
678
679 #[inline]
680 fn hard_link_atomic(self, link: impl AsRef<Path>) -> io::Result<Self> {
681 let temp = TempPath::try_from_path(link.as_ref())?;
682
683 self.as_ref().hard_link(temp.path())?;
684
685 temp.persist(link)?;
686
687 Ok(self)
688 }
689
690 #[inline]
691 fn hard_link_atomic_if_exists(self, link: impl AsRef<Path>) -> io::Result<Option<Self>> {
692 match self.as_ref().hard_link_atomic(link) {
693 Ok(_) => Ok(Some(self)),
694 Err(err) if err.kind() == io::ErrorKind::NotFound => {
695 if !self.try_exists_nofollow()? {
696 Ok(None)
697 } else {
698 Err(err)
699 }
700 },
701 Err(err) => Err(err),
702 }
703 }
704
705 #[cfg(unix)]
706 #[inline]
707 fn symlink(self, link: impl AsRef<Path>) -> io::Result<Self> {
708 unix::fs::symlink(self.as_ref(), link)?;
709
710 Ok(self)
711 }
712
713 #[cfg(unix)]
714 #[inline]
715 fn symlink_atomic(self, link: impl AsRef<Path>) -> io::Result<Self> {
716 let temp = TempPath::try_from_path(link.as_ref())?;
717
718 self.as_ref().symlink(temp.path())?;
719
720 temp.persist(link)?;
721
722 Ok(self)
723 }
724
725 #[cfg(unix)]
726 #[inline]
727 fn symlink_absolute(self, link: impl AsRef<Path>) -> io::Result<Self> {
728 #[expect(unstable_name_collisions)]
729 let absolute = self.as_ref().absolute()?;
730
731 absolute.symlink(link)?;
732
733 Ok(self)
734 }
735
736 #[cfg(unix)]
737 #[inline]
738 fn symlink_absolute_atomic(self, link: impl AsRef<Path>) -> io::Result<Self> {
739 #[expect(unstable_name_collisions)]
740 let absolute = self.as_ref().absolute()?;
741
742 absolute.symlink_atomic(link)?;
743
744 Ok(self)
745 }
746
747 #[cfg(unix)]
748 #[inline]
749 fn symlink_relative(self, link: impl AsRef<Path>) -> io::Result<Self> {
750 let base = link.base()?;
751
752 let relative = diff_paths(self.as_ref(), base)
753 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Failed to diff paths"))?;
754
755 relative.symlink(link)?;
756
757 Ok(self)
758 }
759
760 #[cfg(unix)]
761 #[inline]
762 fn symlink_relative_atomic(self, link: impl AsRef<Path>) -> io::Result<Self> {
763 let base = link.base()?;
764
765 let relative = diff_paths(self.as_ref(), base)
766 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Failed to diff paths"))?;
767
768 relative.symlink_atomic(link)?;
769
770 Ok(self)
771 }
772
773 #[inline]
774 fn set_permissions(self, perm: Permissions) -> io::Result<Self> {
775 fs::set_permissions(self.as_ref(), perm)?;
776
777 Ok(self)
778 }
779
780 #[inline]
781 fn set_permissions_if_exists(self, perm: Permissions) -> io::Result<Option<Self>> {
782 match self.as_ref().set_permissions(perm) {
783 Ok(_) => Ok(Some(self)),
784 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
785 Err(err) => Err(err),
786 }
787 }
788
789 #[inline]
790 fn set_permissions_readonly(self, readonly: bool) -> io::Result<Self> {
791 let meta = self.metadata()?;
792
793 let mut perm = meta.permissions();
794
795 perm.set_readonly(readonly);
796
797 self.set_permissions(perm)
798 }
799
800 #[inline]
801 fn set_permissions_readonly_if_exists(self, readonly: bool) -> io::Result<Option<Self>> {
802 match self.as_ref().set_permissions_readonly(readonly) {
803 Ok(_) => Ok(Some(self)),
804 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
805 Err(err) => Err(err),
806 }
807 }
808
809 #[cfg(unix)]
810 #[inline]
811 fn set_permissions_mode(self, mode: u32) -> io::Result<Self> {
812 let perm = Permissions::from_mode(mode);
813
814 self.set_permissions(perm)
815 }
816
817 #[cfg(unix)]
818 #[inline]
819 fn set_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>> {
820 match self.as_ref().set_permissions_mode(mode) {
821 Ok(_) => Ok(Some(self)),
822 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
823 Err(err) => Err(err),
824 }
825 }
826
827 #[cfg(unix)]
828 #[inline]
829 fn add_permissions_mode(self, mode: u32) -> io::Result<Self> {
830 let meta = self.metadata()?;
831
832 let perm = meta.permissions();
833
834 self.set_permissions_mode(perm.mode() | mode)
835 }
836
837 #[cfg(unix)]
838 #[inline]
839 fn add_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>> {
840 match self.as_ref().add_permissions_mode(mode) {
841 Ok(_) => Ok(Some(self)),
842 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
843 Err(err) => Err(err),
844 }
845 }
846
847 #[cfg(unix)]
848 #[inline]
849 fn remove_permissions_mode(self, mode: u32) -> io::Result<Self> {
850 let meta = self.metadata()?;
851
852 let perm = meta.permissions();
853
854 self.set_permissions_mode(perm.mode() & !mode)
855 }
856
857 #[cfg(unix)]
858 #[inline]
859 fn remove_permissions_mode_if_exists(self, mode: u32) -> io::Result<Option<Self>> {
860 match self.as_ref().remove_permissions_mode(mode) {
861 Ok(_) => Ok(Some(self)),
862 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
863 Err(err) => Err(err),
864 }
865 }
866}
867
868macro_rules! define_option_path_ext {
869 (
870 $(
871 $(#[$meta:meta])*
872 fn $method:ident(self $(, $arg:ident: $ty:ty)* $(,)*) -> $ret:ty;
873 )*
874 ) => {
875 pub trait OptionPathExt<T> {
876 $(
877 $(#[$meta])*
878 fn $method(self $(, $arg: $ty)*) -> $ret;
879 )*
880 }
881
882 impl<T: PathExt> OptionPathExt<T> for Option<T> {
883 $(
884 #[inline]
885 $(#[$meta])*
886 fn $method(self $(, $arg: $ty)*) -> $ret {
887 match self {
888 Some(path) => path.$method($($arg),*).map(Some),
889 None => Ok(None),
890 }
891 }
892 )*
893 }
894 };
895}
896
897define_option_path_ext! {
898 fn metadata(self) -> io::Result<Option<Metadata>>;
899 fn symlink_metadata(self) -> io::Result<Option<Metadata>>;
900 fn canonicalize(self) -> io::Result<Option<PathBuf>>;
901 fn read_link(self) -> io::Result<Option<PathBuf>>;
902 fn read_dir(self) -> io::Result<Option<ReadDir>>;
903
904 fn try_exists(self) -> io::Result<Option<bool>>;
905
906 fn try_is_file(self) -> io::Result<Option<bool>>;
907 fn try_is_dir(self) -> io::Result<Option<bool>>;
908 fn try_is_symlink(self) -> io::Result<Option<bool>>;
909
910 fn try_exists_nofollow(self) -> io::Result<Option<bool>>;
911 fn try_is_file_nofollow(self) -> io::Result<Option<bool>>;
912 fn try_is_dir_nofollow(self) -> io::Result<Option<bool>>;
913
914 fn try_is_read_dir_empty(self) -> io::Result<Option<bool>>;
915
916 fn create_new(self) -> io::Result<Option<File>>;
917
918 fn create(self) -> io::Result<Option<File>>;
919
920 fn open(self) -> io::Result<Option<File>>;
921
922 fn read(self) -> io::Result<Option<Vec<u8>>>;
923
924 fn read_to_string(self) -> io::Result<Option<String>>;
925
926 fn create_dir_all(self) -> io::Result<Option<T>>;
927
928 fn create_dir(self) -> io::Result<Option<T>>;
929
930 fn write_new(self, contents: impl AsRef<[u8]>) -> io::Result<Option<T>>;
931
932 fn write(self, contents: impl AsRef<[u8]>) -> io::Result<Option<T>>;
933
934 fn append(self, contents: impl AsRef<[u8]>) -> io::Result<Option<T>>;
935
936 fn copy(self, to: impl AsRef<Path>) -> io::Result<Option<T>>;
937
938 fn rename(self, to: impl AsRef<Path>) -> io::Result<Option<T>>;
939
940 fn remove_file(self) -> io::Result<Option<T>>;
941
942 fn remove_dir(self) -> io::Result<Option<T>>;
943
944 fn remove_dir_all(self) -> io::Result<Option<T>>;
945
946 fn hard_link(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
947 fn hard_link_atomic(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
948
949 #[cfg(unix)]
950 fn symlink(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
951 #[cfg(unix)]
952 fn symlink_atomic(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
953
954 #[cfg(unix)]
955 fn symlink_absolute(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
956 #[cfg(unix)]
957 fn symlink_absolute_atomic(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
958
959 #[cfg(unix)]
960 fn symlink_relative(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
961 #[cfg(unix)]
962 fn symlink_relative_atomic(self, link: impl AsRef<Path>) -> io::Result<Option<T>>;
963
964 fn set_permissions(self, perm: Permissions) -> io::Result<Option<T>>;
965
966 fn set_permissions_readonly(self, readonly: bool) -> io::Result<Option<T>>;
967
968 #[cfg(unix)]
969 fn set_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
970 #[cfg(unix)]
971 fn add_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
972 #[cfg(unix)]
973 fn remove_permissions_mode(self, mode: u32) -> io::Result<Option<T>>;
974}