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