fs_err/
errors.rs

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