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