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#[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 #[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#[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 #[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}