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