fs_tracing/
lib.rs

1//! fs-tracing is a drop-in replacement for [`std::fs`](std::fs) that provides an auxiliary
2//! information (such as paths) on error via [`tracing`](https://github.com/tokio-rs/tracing).
3//!
4//! # Usage
5//! You need to install [`tracing_error::ErrorLayer`](https://docs.rs/tracing-error/0.1.2/tracing_error/struct.ErrorLayer.html)
6//! for capturing the error context. For example, the following function installs `ErrorLayer`.
7//!
8//! ```
9//! // https://docs.rs/tracing-error/0.1.2/tracing_error/index.html
10//! pub fn install() {
11//!     use tracing_error::ErrorLayer;
12//!     use tracing_subscriber::prelude::*;
13//!
14//!     let subscriber = tracing_subscriber::Registry::default().with(ErrorLayer::default());
15//!
16//!     tracing::subscriber::set_global_default(subscriber).unwrap();
17//! }
18//! ```
19//!
20//! For more information, please visit [https://docs.rs/tracing-subscriber/0.2.16/tracing_subscriber/registry/index.html](https://docs.rs/tracing-subscriber/0.2.16/tracing_subscriber/registry/index.html).
21//!
22//! Then, you can replace `std::fs` with `fs_tracing` in your code and you get nice error messages.
23//!
24//! # Errors
25//! fs-tracing returns [`std::io::Error`](std::io::Error) on errors for compatibility, although
26//! the returned error contains the context information such as the kind of the operation and the
27//! values passed as arguments.
28//!
29//! For example, when you open a file which does not exist, the error message returned by fs-tracing
30//! prints the operation name (`fs_tracing::read`) and the offending path (`/not_exist`):
31//! ```text
32//! No such file or directory (os error 2)
33//! Trace:
34//!    0: fs_tracing::read
35//!            with path="/not_exist"
36//!              at src/lib.rs:652
37//! ```
38
39// CR pandaman: implement error wrapper
40// CR pandaman: consider whether to #[instrument] non-fallible functions such as builders.
41// CR pandaman: implement nightly only functions?
42// CR pandaman: propose that #[instrument] can take parent parameter
43// https://github.com/tokio-rs/tracing/issues/879
44// or, add them as a non-parent span?
45// CR pandaman: report to the rust-analyzer team the following:
46// 1. autocompleting a trait method signature removes attributes (such as #[instrument])
47// 2. autocompletion should show methods from the implementing trait
48
49#![deny(unsafe_code)]
50// CR pandaman: promote to deny
51#![warn(
52    future_incompatible,
53    missing_debug_implementations,
54    missing_docs,
55    rust_2018_idioms,
56    rustdoc,
57    trivial_casts
58)]
59// std does not have ones.
60#![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
71/// Wrapper for [`fs::DirBuilder`](std::fs::DirBuilder).
72pub 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
82// CR pandaman: implement DirBuilderExt for DirBuilder (unix only)
83
84impl DirBuilder {
85    /// Wrapper for [`DirBuilder::new`](std::fs::DirBuilder::new).
86    pub fn new() -> Self {
87        Self {
88            inner: fs::DirBuilder::new(),
89        }
90    }
91
92    /// Wrapper for [`DirBuilder::recursive`](std::fs::DirBuilder::recursive).
93    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
94        self.inner.recursive(recursive);
95        self
96    }
97
98    /// Wrapper for [`DirBuilder::create`](std::fs::DirBuilder::create).
99    pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
100        // CR pandaman: consult doc for tracing::instrument to mimic the ordinary ordering
101        #[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
110/// Wrapper for [`fs::DirEntry`](std::fs::DirEntry).
111pub 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
121// CR pandaman: implement DirEntryExt for DirEntry (unix only)
122
123impl DirEntry {
124    /// Wrapper for [`DirEntry::path`](std::fs::DirEntry::path).
125    pub fn path(&self) -> PathBuf {
126        self.inner.path()
127    }
128
129    /// Wrapper for [`DirEntry::metadata`](std::fs::DirEntry::metadata).
130    #[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    /// Wrapper for [`DirEntry::file_type`](std::fs::DirEntry::file_type).
139    #[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    /// Wrapper for [`DirEntry::file_name`](std::fs::DirEntry::file_name).
148    pub fn file_name(&self) -> ffi::OsString {
149        self.inner.file_name()
150    }
151}
152
153/// Wrapper for [`fs::File`](std::fs::File).
154pub struct File {
155    inner: fs::File,
156}
157
158// CR pandaman: implement extension traits
159
160impl 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    /// Wrapper for [`File::open`](std::fs::File::open).
312    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    /// Wrapper for [`File::create`](std::fs::File::create).
324    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    /// Wrapper for [`File::sync_all`](std::fs::File::sync_all).
336    #[instrument]
337    pub fn sync_all(&self) -> io::Result<()> {
338        self.inner.sync_all().map_err(error::Error::wrap_std)
339    }
340
341    /// Wrapper for [`File::sync_data`](std::fs::File::sync_data).
342    #[instrument]
343    pub fn sync_data(&self) -> io::Result<()> {
344        self.inner.sync_data().map_err(error::Error::wrap_std)
345    }
346
347    /// Wrapper for [`File::set_len`](std::fs::File::set_len),
348    #[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    /// Wrapper for [`File::metadata`](std::fs::File::metadata).
354    #[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    /// Wrapper for [`File::try_clone`](std::fs::File::try_clone).
363    #[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    /// Wrapper for [`File::set_permissions`](std::fs::File::set_permissions).
372    #[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/// Wrapper for [`fs::FileType`](std::fs::FileType).
381#[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
392// CR pandaman: implement FileTypeExt for FileType
393
394impl FileType {
395    /// Wrapper for [`FileType::is_dir`](std::fs::FileType::is_dir).
396    pub fn is_dir(&self) -> bool {
397        self.inner.is_dir()
398    }
399
400    /// Wrapper for [`FileType::is_file`](std::fs::FileType::is_file).
401    pub fn is_file(&self) -> bool {
402        self.inner.is_file()
403    }
404
405    /// Wrapper for [`FileType::is_symlink`](std::fs::FileType::is_symlink).
406    pub fn is_symlink(&self) -> bool {
407        self.inner.is_symlink()
408    }
409}
410
411/// Wrapper for [`fs::Metadata`](std::fs::Metadata).
412#[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
423// CR pandaman: implement MetadataExt for Metadata
424
425impl Metadata {
426    /// Wrapper for [`Metadata::file_type`](std::fs::Metadata::file_type).
427    pub fn file_type(&self) -> FileType {
428        FileType {
429            inner: self.inner.file_type(),
430        }
431    }
432
433    /// Wrapper for [`Metadata::is_dir`](std::fs::Metadata::is_dir).
434    pub fn is_dir(&self) -> bool {
435        self.inner.is_dir()
436    }
437
438    /// Wrapper for [`Metadata::is_file`](std::fs::Metadata::is_file).
439    pub fn is_file(&self) -> bool {
440        self.inner.is_file()
441    }
442
443    /// Wrapper for [`Metadata::len`](std::fs::Metadata::len).
444    pub fn len(&self) -> u64 {
445        self.inner.len()
446    }
447
448    /// Wrapper for [`Metadata::permissions`](std::fs::Metadata::permissions).
449    pub fn permissions(&self) -> Permissions {
450        Permissions {
451            inner: self.inner.permissions(),
452        }
453    }
454
455    /// Wrapper for [`Metadata::modified`](std::fs::Metadata::modified).
456    #[instrument]
457    pub fn modified(&self) -> io::Result<time::SystemTime> {
458        self.inner.modified().map_err(error::Error::wrap_std)
459    }
460
461    /// Wrapper for [`Metadata::accessed`](std::fs::Metadata::accessed).
462    #[instrument]
463    pub fn accessed(&self) -> io::Result<time::SystemTime> {
464        self.inner.accessed().map_err(error::Error::wrap_std)
465    }
466
467    /// Wrapper for [`Metadata::created`](std::fs::Metadata::created).
468    #[instrument]
469    pub fn created(&self) -> io::Result<time::SystemTime> {
470        self.inner.created().map_err(error::Error::wrap_std)
471    }
472}
473
474/// Wrapper for [`fs::OpenOptions`](std::fs::OpenOptions).
475#[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
486// CR pandaman: implement OpenOptionsExt for OpenOptions
487
488impl OpenOptions {
489    /// Wrapper for [`OpenOptions::new`](std::fs::OpenOptions::new).
490    pub fn new() -> Self {
491        Self {
492            inner: fs::OpenOptions::new(),
493        }
494    }
495
496    /// Wrapper for [`OpenOptions::read`](std::fs::OpenOptions::read).
497    pub fn read(&mut self, read: bool) -> &mut Self {
498        self.inner.read(read);
499        self
500    }
501
502    /// Wrapper for [`OpenOptions::write`](std::fs::OpenOptions::write).
503    pub fn write(&mut self, write: bool) -> &mut Self {
504        self.inner.write(write);
505        self
506    }
507
508    /// Wrapper for [`OpenOptions::append`](std::fs::OpenOptions::append).
509    pub fn append(&mut self, append: bool) -> &mut Self {
510        self.inner.append(append);
511        self
512    }
513
514    /// Wrapper for [`OpenOptions::truncate`](std::fs::OpenOptions::truncate).
515    pub fn truncate(&mut self, truncate: bool) -> &mut Self {
516        self.inner.truncate(truncate);
517        self
518    }
519
520    /// Wrapper for [`OpenOptions::create`](std::fs::OpenOptions::create).
521    pub fn create(&mut self, create: bool) -> &mut Self {
522        self.inner.create(create);
523        self
524    }
525
526    /// Wrapper for [`OpenOptions::create_new`](std::fs::OpenOptions::create_new).
527    pub fn create_new(&mut self, create_new: bool) -> &mut Self {
528        self.inner.create_new(create_new);
529        self
530    }
531
532    /// Wrapper for [`OpenOptions::open`](std::fs::OpenOptions::open).
533    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/// Wrapper for [`fs::Permissions`](std::fs::Permissions).
547#[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
558// CR pandaman: implement PermissionExt for Permissions
559
560impl Permissions {
561    /// Wrapper for [`Permissions::readonly`](std::fs::Permissions::readonly).
562    pub fn readonly(&self) -> bool {
563        self.inner.readonly()
564    }
565
566    /// Wrapper for [`Permissions::set_readonly`](std::fs::Permissions::set_readonly).
567    pub fn set_readonly(&mut self, readonly: bool) {
568        self.inner.set_readonly(readonly)
569    }
570}
571
572/// Wrapper for [`fs::ReadDir`](std::fs::ReadDir).
573pub struct ReadDir {
574    inner: fs::ReadDir,
575    // CR pandaman: consider adding a Span context here
576}
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
597/// Wrapper for [`fs::canonicalize`](std::fs::canonicalize).
598pub 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
607/// Wrapper for [`fs::copy`](std::fs::copy).
608pub 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        // CR pandaman: I don't know why copying between the same file can result in a truncated file
612        if from == to {
613            // CR pandaman: consider the appropriate log level
614            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
623/// Wrapper for [`fs::create_dir`](std::fs::create_dir).
624pub 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
633/// Wrapper for [`fs::create_dir_all`](std::fs::create_dir_all).
634pub 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
643/// Wrapper for [`fs::hard_link`](std::fs::hard_link).
644pub 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
653/// Wrapper for [`fs::metadata`](std::fs::metadata).
654pub 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
665/// Wrapper for [`fs::read`](std::fs::read).
666pub 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
675/// Wrapper for [`fs::read_dir`](std::fs::read_dir).
676pub 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
687/// Wrapper for [`fs::read_link`](std::fs::read_link).
688pub 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
697/// Wrapper for [`fs::read_to_string`](std::fs::read_to_string).
698pub 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
707/// Wrapper for [`fs::remove_dir`](std::fs::remove_dir).
708pub 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
717/// Wrapper for [`fs::remove_dir_all`](std::fs::remove_dir_all).
718pub 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
727/// Wrapper for [`fs::remove_file`](std::fs::remove_file).
728pub 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
737/// Wrapper for [`fs::rename`](std::fs::rename).
738pub 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
747/// Wrapper for [`fs::set_permissions`](std::fs::set_permissions).
748pub 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
757/// Wrapper for [`fs::symlink_metadata`](std::fs::symlink_metadata).
758pub 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
769/// Wrapper for [`fs::write`](std::fs::write).
770pub 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}