1#![deny(unsafe_code)]
50#![warn(
52 future_incompatible,
53 missing_debug_implementations,
54 missing_docs,
55 rust_2018_idioms,
56 rustdoc,
57 trivial_casts
58)]
59#![allow(clippy::new_without_default, clippy::len_without_is_empty)]
61
62mod error;
63
64use std::{
65 ffi, fmt, fs, io,
66 path::{Path, PathBuf},
67 process, time,
68};
69use tracing::{debug, instrument};
70
71pub struct DirBuilder {
73 inner: fs::DirBuilder,
74}
75
76impl fmt::Debug for DirBuilder {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 self.inner.fmt(f)
79 }
80}
81
82impl DirBuilder {
85 pub fn new() -> Self {
87 Self {
88 inner: fs::DirBuilder::new(),
89 }
90 }
91
92 pub fn recursive(&mut self, recursive: bool) -> &mut Self {
94 self.inner.recursive(recursive);
95 self
96 }
97
98 pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
100 #[instrument(skip(this), fields(self = ?this, path = ?path))]
102 fn create(this: &DirBuilder, path: &Path) -> io::Result<()> {
103 this.inner.create(path).map_err(error::Error::wrap_std)
104 }
105
106 create(self, path.as_ref())
107 }
108}
109
110pub struct DirEntry {
112 inner: fs::DirEntry,
113}
114
115impl fmt::Debug for DirEntry {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 self.inner.fmt(f)
118 }
119}
120
121impl DirEntry {
124 pub fn path(&self) -> PathBuf {
126 self.inner.path()
127 }
128
129 #[instrument]
131 pub fn metadata(&self) -> io::Result<Metadata> {
132 self.inner
133 .metadata()
134 .map(|inner| Metadata { inner })
135 .map_err(error::Error::wrap_std)
136 }
137
138 #[instrument]
140 pub fn file_type(&self) -> io::Result<FileType> {
141 self.inner
142 .file_type()
143 .map(|inner| FileType { inner })
144 .map_err(error::Error::wrap_std)
145 }
146
147 pub fn file_name(&self) -> ffi::OsString {
149 self.inner.file_name()
150 }
151}
152
153pub struct File {
155 inner: fs::File,
156}
157
158impl fmt::Debug for File {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 self.inner.fmt(f)
163 }
164}
165
166impl From<File> for process::Stdio {
167 fn from(file: File) -> Self {
168 Self::from(file.inner)
169 }
170}
171
172impl io::Read for File {
173 #[instrument]
174 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
175 self.inner.read(buf).map_err(error::Error::wrap_std)
176 }
177
178 #[instrument]
179 fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
180 self.inner
181 .read_vectored(bufs)
182 .map_err(error::Error::wrap_std)
183 }
184
185 #[instrument]
186 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
187 self.inner.read_to_end(buf).map_err(error::Error::wrap_std)
188 }
189
190 #[instrument]
191 fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
192 self.inner
193 .read_to_string(buf)
194 .map_err(error::Error::wrap_std)
195 }
196
197 #[instrument]
198 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
199 self.inner.read_exact(buf).map_err(error::Error::wrap_std)
200 }
201}
202
203impl io::Read for &File {
204 #[instrument]
205 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
206 (&self.inner).read(buf).map_err(error::Error::wrap_std)
207 }
208
209 #[instrument]
210 fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
211 (&self.inner)
212 .read_vectored(bufs)
213 .map_err(error::Error::wrap_std)
214 }
215
216 #[instrument]
217 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
218 (&self.inner)
219 .read_to_end(buf)
220 .map_err(error::Error::wrap_std)
221 }
222
223 #[instrument]
224 fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
225 (&self.inner)
226 .read_to_string(buf)
227 .map_err(error::Error::wrap_std)
228 }
229
230 #[instrument]
231 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
232 (&self.inner)
233 .read_exact(buf)
234 .map_err(error::Error::wrap_std)
235 }
236}
237
238impl io::Seek for File {
239 #[instrument]
240 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
241 self.inner.seek(pos).map_err(error::Error::wrap_std)
242 }
243}
244
245impl io::Seek for &File {
246 #[instrument]
247 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
248 (&self.inner).seek(pos).map_err(error::Error::wrap_std)
249 }
250}
251
252impl io::Write for File {
253 #[instrument]
254 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
255 self.inner.write(buf).map_err(error::Error::wrap_std)
256 }
257
258 #[instrument]
259 fn flush(&mut self) -> io::Result<()> {
260 self.inner.flush().map_err(error::Error::wrap_std)
261 }
262
263 #[instrument]
264 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
265 self.inner
266 .write_vectored(bufs)
267 .map_err(error::Error::wrap_std)
268 }
269
270 #[instrument]
271 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
272 self.inner.write_all(buf).map_err(error::Error::wrap_std)
273 }
274
275 #[instrument]
276 fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
277 self.inner.write_fmt(fmt).map_err(error::Error::wrap_std)
278 }
279}
280
281impl io::Write for &File {
282 #[instrument]
283 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
284 (&self.inner).write(buf).map_err(error::Error::wrap_std)
285 }
286
287 #[instrument]
288 fn flush(&mut self) -> io::Result<()> {
289 (&self.inner).flush().map_err(error::Error::wrap_std)
290 }
291
292 #[instrument]
293 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
294 (&self.inner)
295 .write_vectored(bufs)
296 .map_err(error::Error::wrap_std)
297 }
298
299 #[instrument]
300 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
301 (&self.inner).write_all(buf).map_err(error::Error::wrap_std)
302 }
303
304 #[instrument]
305 fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
306 (&self.inner).write_fmt(fmt).map_err(error::Error::wrap_std)
307 }
308}
309
310impl File {
311 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
313 #[instrument]
314 fn open(path: &Path) -> io::Result<File> {
315 fs::File::open(path)
316 .map(|inner| File { inner })
317 .map_err(error::Error::wrap_std)
318 }
319
320 open(path.as_ref())
321 }
322
323 pub fn create<P: AsRef<Path>>(path: P) -> io::Result<Self> {
325 #[instrument]
326 fn create(path: &Path) -> io::Result<File> {
327 fs::File::create(path)
328 .map(|inner| File { inner })
329 .map_err(error::Error::wrap_std)
330 }
331
332 create(path.as_ref())
333 }
334
335 #[instrument]
337 pub fn sync_all(&self) -> io::Result<()> {
338 self.inner.sync_all().map_err(error::Error::wrap_std)
339 }
340
341 #[instrument]
343 pub fn sync_data(&self) -> io::Result<()> {
344 self.inner.sync_data().map_err(error::Error::wrap_std)
345 }
346
347 #[instrument]
349 pub fn set_len(&self, size: u64) -> io::Result<()> {
350 self.inner.set_len(size).map_err(error::Error::wrap_std)
351 }
352
353 #[instrument]
355 pub fn metadata(&self) -> io::Result<Metadata> {
356 self.inner
357 .metadata()
358 .map(|inner| Metadata { inner })
359 .map_err(error::Error::wrap_std)
360 }
361
362 #[instrument]
364 pub fn try_clone(&self) -> io::Result<File> {
365 self.inner
366 .try_clone()
367 .map(|inner| File { inner })
368 .map_err(error::Error::wrap_std)
369 }
370
371 #[instrument]
373 pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
374 self.inner
375 .set_permissions(perm.inner)
376 .map_err(error::Error::wrap_std)
377 }
378}
379
380#[derive(Clone, Copy, PartialEq, Eq)]
382pub struct FileType {
383 inner: fs::FileType,
384}
385
386impl fmt::Debug for FileType {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 self.inner.fmt(f)
389 }
390}
391
392impl FileType {
395 pub fn is_dir(&self) -> bool {
397 self.inner.is_dir()
398 }
399
400 pub fn is_file(&self) -> bool {
402 self.inner.is_file()
403 }
404
405 pub fn is_symlink(&self) -> bool {
407 self.inner.is_symlink()
408 }
409}
410
411#[derive(Clone)]
413pub struct Metadata {
414 inner: fs::Metadata,
415}
416
417impl fmt::Debug for Metadata {
418 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419 self.inner.fmt(f)
420 }
421}
422
423impl Metadata {
426 pub fn file_type(&self) -> FileType {
428 FileType {
429 inner: self.inner.file_type(),
430 }
431 }
432
433 pub fn is_dir(&self) -> bool {
435 self.inner.is_dir()
436 }
437
438 pub fn is_file(&self) -> bool {
440 self.inner.is_file()
441 }
442
443 pub fn len(&self) -> u64 {
445 self.inner.len()
446 }
447
448 pub fn permissions(&self) -> Permissions {
450 Permissions {
451 inner: self.inner.permissions(),
452 }
453 }
454
455 #[instrument]
457 pub fn modified(&self) -> io::Result<time::SystemTime> {
458 self.inner.modified().map_err(error::Error::wrap_std)
459 }
460
461 #[instrument]
463 pub fn accessed(&self) -> io::Result<time::SystemTime> {
464 self.inner.accessed().map_err(error::Error::wrap_std)
465 }
466
467 #[instrument]
469 pub fn created(&self) -> io::Result<time::SystemTime> {
470 self.inner.created().map_err(error::Error::wrap_std)
471 }
472}
473
474#[derive(Clone)]
476pub struct OpenOptions {
477 inner: fs::OpenOptions,
478}
479
480impl fmt::Debug for OpenOptions {
481 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 self.inner.fmt(f)
483 }
484}
485
486impl OpenOptions {
489 pub fn new() -> Self {
491 Self {
492 inner: fs::OpenOptions::new(),
493 }
494 }
495
496 pub fn read(&mut self, read: bool) -> &mut Self {
498 self.inner.read(read);
499 self
500 }
501
502 pub fn write(&mut self, write: bool) -> &mut Self {
504 self.inner.write(write);
505 self
506 }
507
508 pub fn append(&mut self, append: bool) -> &mut Self {
510 self.inner.append(append);
511 self
512 }
513
514 pub fn truncate(&mut self, truncate: bool) -> &mut Self {
516 self.inner.truncate(truncate);
517 self
518 }
519
520 pub fn create(&mut self, create: bool) -> &mut Self {
522 self.inner.create(create);
523 self
524 }
525
526 pub fn create_new(&mut self, create_new: bool) -> &mut Self {
528 self.inner.create_new(create_new);
529 self
530 }
531
532 pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
534 #[instrument(skip(this), fields(self = ?this, path = ?path))]
535 fn open(this: &OpenOptions, path: &Path) -> io::Result<File> {
536 this.inner
537 .open(path)
538 .map(|inner| File { inner })
539 .map_err(error::Error::wrap_std)
540 }
541
542 open(self, path.as_ref())
543 }
544}
545
546#[derive(Clone, PartialEq, Eq)]
548pub struct Permissions {
549 inner: fs::Permissions,
550}
551
552impl fmt::Debug for Permissions {
553 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 self.inner.fmt(f)
555 }
556}
557
558impl Permissions {
561 pub fn readonly(&self) -> bool {
563 self.inner.readonly()
564 }
565
566 pub fn set_readonly(&mut self, readonly: bool) {
568 self.inner.set_readonly(readonly)
569 }
570}
571
572pub struct ReadDir {
574 inner: fs::ReadDir,
575 }
577
578impl fmt::Debug for ReadDir {
579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580 self.inner.fmt(f)
581 }
582}
583
584impl Iterator for ReadDir {
585 type Item = io::Result<DirEntry>;
586
587 #[instrument]
588 fn next(&mut self) -> Option<Self::Item> {
589 self.inner.next().map(|result| {
590 result
591 .map(|inner| DirEntry { inner })
592 .map_err(error::Error::wrap_std)
593 })
594 }
595}
596
597pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
599 #[instrument]
600 fn canonicalize(path: &Path) -> io::Result<PathBuf> {
601 fs::canonicalize(path).map_err(error::Error::wrap_std)
602 }
603
604 canonicalize(path.as_ref())
605}
606
607pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
609 #[instrument]
610 fn copy(from: &Path, to: &Path) -> io::Result<u64> {
611 if from == to {
613 debug!("`from' and `to' point to the same file");
615 }
616
617 fs::copy(from, to).map_err(error::Error::wrap_std)
618 }
619
620 copy(from.as_ref(), to.as_ref())
621}
622
623pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
625 #[instrument]
626 fn create_dir(path: &Path) -> io::Result<()> {
627 fs::create_dir(path).map_err(error::Error::wrap_std)
628 }
629
630 create_dir(path.as_ref())
631}
632
633pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
635 #[instrument]
636 fn create_dir_all(path: &Path) -> io::Result<()> {
637 fs::create_dir_all(path).map_err(error::Error::wrap_std)
638 }
639
640 create_dir_all(path.as_ref())
641}
642
643pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
645 #[instrument]
646 fn hard_link(original: &Path, link: &Path) -> io::Result<()> {
647 fs::hard_link(original, link).map_err(error::Error::wrap_std)
648 }
649
650 hard_link(original.as_ref(), link.as_ref())
651}
652
653pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
655 #[instrument]
656 fn metadata(path: &Path) -> io::Result<Metadata> {
657 fs::metadata(path)
658 .map(|inner| Metadata { inner })
659 .map_err(error::Error::wrap_std)
660 }
661
662 metadata(path.as_ref())
663}
664
665pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
667 #[instrument]
668 fn read(path: &Path) -> io::Result<Vec<u8>> {
669 fs::read(path).map_err(error::Error::wrap_std)
670 }
671
672 read(path.as_ref())
673}
674
675pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
677 #[instrument]
678 fn read_dir(path: &Path) -> io::Result<ReadDir> {
679 fs::read_dir(path)
680 .map(|inner| ReadDir { inner })
681 .map_err(error::Error::wrap_std)
682 }
683
684 read_dir(path.as_ref())
685}
686
687pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
689 #[instrument]
690 fn read_link(path: &Path) -> io::Result<PathBuf> {
691 fs::read_link(path).map_err(error::Error::wrap_std)
692 }
693
694 read_link(path.as_ref())
695}
696
697pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
699 #[instrument]
700 fn read_to_string(path: &Path) -> io::Result<String> {
701 fs::read_to_string(path).map_err(error::Error::wrap_std)
702 }
703
704 read_to_string(path.as_ref())
705}
706
707pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
709 #[instrument]
710 fn remove_dir(path: &Path) -> io::Result<()> {
711 fs::remove_dir(path).map_err(error::Error::wrap_std)
712 }
713
714 remove_dir(path.as_ref())
715}
716
717pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
719 #[instrument]
720 fn remove_dir_all(path: &Path) -> io::Result<()> {
721 fs::remove_dir_all(path).map_err(error::Error::wrap_std)
722 }
723
724 remove_dir_all(path.as_ref())
725}
726
727pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
729 #[instrument]
730 fn remove_file(path: &Path) -> io::Result<()> {
731 fs::remove_file(path).map_err(error::Error::wrap_std)
732 }
733
734 remove_file(path.as_ref())
735}
736
737pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
739 #[instrument]
740 fn rename(from: &Path, to: &Path) -> io::Result<()> {
741 fs::rename(from, to).map_err(error::Error::wrap_std)
742 }
743
744 rename(from.as_ref(), to.as_ref())
745}
746
747pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
749 #[instrument]
750 fn set_permissions(path: &Path, perm: Permissions) -> io::Result<()> {
751 fs::set_permissions(path, perm.inner).map_err(error::Error::wrap_std)
752 }
753
754 set_permissions(path.as_ref(), perm)
755}
756
757pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
759 #[instrument]
760 fn symlink_metadata(path: &Path) -> io::Result<Metadata> {
761 fs::symlink_metadata(path)
762 .map(|inner| Metadata { inner })
763 .map_err(error::Error::wrap_std)
764 }
765
766 symlink_metadata(path.as_ref())
767}
768
769pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
771 #[instrument]
772 fn write(path: &Path, contents: &[u8]) -> io::Result<()> {
773 fs::write(path, contents).map_err(error::Error::wrap_std)
774 }
775
776 write(path.as_ref(), contents.as_ref())
777}