fs_err/
errors.rs

1#[cfg(feature = "debug")]
2use path_facts::PathFacts;
3use std::error::Error as StdError;
4use std::fmt;
5use std::io;
6use std::path::PathBuf;
7
8#[derive(Debug, Clone, Copy)]
9pub(crate) enum ErrorKind {
10    OpenFile,
11    CreateFile,
12    CreateDir,
13    SyncFile,
14    SetLen,
15    Metadata,
16    Clone,
17    SetPermissions,
18    Read,
19    Seek,
20    Write,
21    Flush,
22    ReadDir,
23    RemoveFile,
24    RemoveDir,
25    Canonicalize,
26    ReadLink,
27    SymlinkMetadata,
28    #[allow(dead_code)]
29    FileExists,
30    Lock,
31    Unlock,
32
33    #[cfg(windows)]
34    SeekRead,
35    #[cfg(windows)]
36    SeekWrite,
37
38    #[cfg(unix)]
39    ReadAt,
40    #[cfg(unix)]
41    WriteAt,
42}
43
44/// Contains an IO error that has a file path attached.
45///
46/// This type is never returned directly, but is instead wrapped inside yet
47/// another IO error.
48#[derive(Debug)]
49pub(crate) struct Error {
50    kind: ErrorKind,
51    source: io::Error,
52    path: PathBuf,
53}
54
55impl Error {
56    pub fn build(source: io::Error, kind: ErrorKind, path: impl Into<PathBuf>) -> io::Error {
57        io::Error::new(
58            source.kind(),
59            Self {
60                kind,
61                source,
62                path: path.into(),
63            },
64        )
65    }
66}
67
68impl fmt::Display for Error {
69    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
70        use ErrorKind as E;
71
72        let path = self.path.display();
73
74        match self.kind {
75            E::OpenFile => write!(formatter, "failed to open file `{}`", path),
76            E::CreateFile => write!(formatter, "failed to create file `{}`", path),
77            E::CreateDir => write!(formatter, "failed to create directory `{}`", path),
78            E::SyncFile => write!(formatter, "failed to sync file `{}`", path),
79            E::SetLen => write!(formatter, "failed to set length of file `{}`", path),
80            E::Metadata => write!(formatter, "failed to query metadata of file `{}`", path),
81            E::Clone => write!(formatter, "failed to clone handle for file `{}`", path),
82            E::SetPermissions => write!(formatter, "failed to set permissions for file `{}`", path),
83            E::Read => write!(formatter, "failed to read from file `{}`", path),
84            E::Seek => write!(formatter, "failed to seek in file `{}`", path),
85            E::Write => write!(formatter, "failed to write to file `{}`", path),
86            E::Flush => write!(formatter, "failed to flush file `{}`", path),
87            E::ReadDir => write!(formatter, "failed to read directory `{}`", path),
88            E::RemoveFile => write!(formatter, "failed to remove file `{}`", path),
89            E::RemoveDir => write!(formatter, "failed to remove directory `{}`", path),
90            E::Canonicalize => write!(formatter, "failed to canonicalize path `{}`", path),
91            E::ReadLink => write!(formatter, "failed to read symbolic link `{}`", path),
92            E::SymlinkMetadata => {
93                write!(formatter, "failed to query metadata of symlink `{}`", path)
94            }
95            E::FileExists => write!(formatter, "failed to check file existence `{}`", path),
96            E::Lock => write!(formatter, "failed to lock `{}`", path),
97            E::Unlock => write!(formatter, "failed to unlock `{}`", path),
98
99            #[cfg(windows)]
100            E::SeekRead => write!(formatter, "failed to seek and read from `{}`", path),
101            #[cfg(windows)]
102            E::SeekWrite => write!(formatter, "failed to seek and write to `{}`", path),
103
104            #[cfg(unix)]
105            E::ReadAt => write!(formatter, "failed to read with offset from `{}`", path),
106            #[cfg(unix)]
107            E::WriteAt => write!(formatter, "failed to write with offset to `{}`", path),
108        }?;
109
110        // The `expose_original_error` feature indicates the caller should display the original error
111        #[cfg(not(feature = "expose_original_error"))]
112        write!(formatter, ": {}", self.source)?;
113
114        #[cfg(all(feature = "debug", not(feature = "tokio")))]
115        writeln!(formatter, "{}", path_facts(&self.path))?;
116
117        #[cfg(all(feature = "debug", feature = "tokio"))]
118        writeln!(formatter, "{}", async_path_facts(&self.path))?;
119        Ok(())
120    }
121}
122
123impl StdError for Error {
124    fn cause(&self) -> Option<&dyn StdError> {
125        self.source()
126    }
127
128    #[cfg(not(feature = "expose_original_error"))]
129    fn source(&self) -> Option<&(dyn StdError + 'static)> {
130        None
131    }
132
133    #[cfg(feature = "expose_original_error")]
134    fn source(&self) -> Option<&(dyn StdError + 'static)> {
135        Some(&self.source)
136    }
137}
138
139#[derive(Debug, Clone, Copy)]
140pub(crate) enum SourceDestErrorKind {
141    Copy,
142    HardLink,
143    Rename,
144    SoftLink,
145
146    #[cfg(unix)]
147    Symlink,
148
149    #[cfg(windows)]
150    SymlinkDir,
151    #[cfg(windows)]
152    SymlinkFile,
153}
154
155/// Error type used by functions like `fs::copy` that holds two paths.
156#[derive(Debug)]
157pub(crate) struct SourceDestError {
158    kind: SourceDestErrorKind,
159    source: io::Error,
160    from_path: PathBuf,
161    to_path: PathBuf,
162}
163
164impl SourceDestError {
165    pub fn build(
166        source: io::Error,
167        kind: SourceDestErrorKind,
168        from_path: impl Into<PathBuf>,
169        to_path: impl Into<PathBuf>,
170    ) -> io::Error {
171        io::Error::new(
172            source.kind(),
173            Self {
174                kind,
175                source,
176                from_path: from_path.into(),
177                to_path: to_path.into(),
178            },
179        )
180    }
181}
182
183impl fmt::Display for SourceDestError {
184    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
185        let from = self.from_path.display();
186        let to = self.to_path.display();
187        match self.kind {
188            SourceDestErrorKind::Copy => {
189                write!(formatter, "failed to copy file from {} to {}", from, to)
190            }
191            SourceDestErrorKind::HardLink => {
192                write!(formatter, "failed to hardlink file from {} to {}", from, to)
193            }
194            SourceDestErrorKind::Rename => {
195                write!(formatter, "failed to rename file from {} to {}", from, to)
196            }
197            SourceDestErrorKind::SoftLink => {
198                write!(formatter, "failed to softlink file from {} to {}", from, to)
199            }
200
201            #[cfg(unix)]
202            SourceDestErrorKind::Symlink => {
203                write!(formatter, "failed to symlink file from {} to {}", from, to)
204            }
205
206            #[cfg(windows)]
207            SourceDestErrorKind::SymlinkFile => {
208                write!(formatter, "failed to symlink file from {} to {}", from, to)
209            }
210            #[cfg(windows)]
211            SourceDestErrorKind::SymlinkDir => {
212                write!(formatter, "failed to symlink dir from {} to {}", from, to)
213            }
214        }?;
215
216        // The `expose_original_error` feature indicates the caller should display the original error
217        #[cfg(not(feature = "expose_original_error"))]
218        write!(formatter, ": {}", self.source)?;
219
220        #[cfg(all(feature = "debug", not(feature = "tokio")))]
221        writeln!(
222            formatter,
223            "{}",
224            path_facts_source_dest(&self.from_path, &self.to_path)
225        )?;
226
227        #[cfg(all(feature = "debug", feature = "tokio"))]
228        writeln!(
229            formatter,
230            "{}",
231            async_path_facts_source_dest(&self.from_path, &self.to_path)
232        )?;
233
234        Ok(())
235    }
236}
237
238#[cfg(feature = "debug")]
239pub(crate) fn path_facts_source_dest(from: &std::path::Path, to: &std::path::Path) -> String {
240    format!(
241        "
242
243From path {}
244To path {}",
245        PathFacts::new(from),
246        PathFacts::new(to)
247    )
248}
249
250#[cfg(all(feature = "debug", feature = "tokio"))]
251pub(crate) fn async_path_facts(path: &std::path::Path) -> String {
252    tokio::task::block_in_place(|| path_facts(path))
253}
254
255#[cfg(all(feature = "debug", feature = "tokio"))]
256pub(crate) fn async_path_facts_source_dest(from: &std::path::Path, to: &std::path::Path) -> String {
257    tokio::task::block_in_place(|| path_facts_source_dest(from, to))
258}
259
260#[cfg(feature = "debug")]
261pub(crate) fn path_facts(path: &std::path::Path) -> String {
262    format!(
263        "
264
265Path {}",
266        PathFacts::new(path)
267    )
268}
269
270impl StdError for SourceDestError {
271    fn cause(&self) -> Option<&dyn StdError> {
272        self.source()
273    }
274
275    #[cfg(not(feature = "expose_original_error"))]
276    fn source(&self) -> Option<&(dyn StdError + 'static)> {
277        None
278    }
279
280    #[cfg(feature = "expose_original_error")]
281    fn source(&self) -> Option<&(dyn StdError + 'static)> {
282        Some(&self.source)
283    }
284}