Skip to main content

path_extra/std/
path.rs

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}