1#![allow(unknown_lints)]
2
3use crate::core::{TargetKind, Workspace};
4use crate::ops::CompileOptions;
5use anyhow::Error;
6use std::fmt;
7use std::path::PathBuf;
8use std::process::{ExitStatus, Output};
9use std::str;
10
11pub type CargoResult<T> = anyhow::Result<T>;
12
13pub trait CargoResultExt<T, E> {
15 fn chain_err<F, D>(self, f: F) -> CargoResult<T>
16 where
17 F: FnOnce() -> D,
18 D: fmt::Display + Send + Sync + 'static;
19}
20
21impl<T, E> CargoResultExt<T, E> for Result<T, E>
22where
23 E: Into<Error>,
24{
25 fn chain_err<F, D>(self, f: F) -> CargoResult<T>
26 where
27 F: FnOnce() -> D,
28 D: fmt::Display + Send + Sync + 'static,
29 {
30 self.map_err(|e| e.into().context(f()))
31 }
32}
33
34#[derive(Debug)]
35pub struct HttpNot200 {
36 pub code: u32,
37 pub url: String,
38}
39
40impl fmt::Display for HttpNot200 {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(
43 f,
44 "failed to get 200 response from `{}`, got {}",
45 self.url, self.code
46 )
47 }
48}
49
50impl std::error::Error for HttpNot200 {}
51
52pub struct VerboseError {
62 inner: Error,
63}
64
65impl VerboseError {
66 pub fn new(inner: Error) -> VerboseError {
67 VerboseError { inner }
68 }
69}
70
71impl std::error::Error for VerboseError {
72 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
73 self.inner.source()
74 }
75}
76
77impl fmt::Debug for VerboseError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 self.inner.fmt(f)
80 }
81}
82
83impl fmt::Display for VerboseError {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 self.inner.fmt(f)
86 }
87}
88
89pub struct InternalError {
97 inner: Error,
98}
99
100impl InternalError {
101 pub fn new(inner: Error) -> InternalError {
102 InternalError { inner }
103 }
104}
105
106impl std::error::Error for InternalError {
107 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
108 self.inner.source()
109 }
110}
111
112impl fmt::Debug for InternalError {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 self.inner.fmt(f)
115 }
116}
117
118impl fmt::Display for InternalError {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 self.inner.fmt(f)
121 }
122}
123
124pub struct ManifestError {
131 cause: Error,
132 manifest: PathBuf,
133}
134
135impl ManifestError {
136 pub fn new<E: Into<Error>>(cause: E, manifest: PathBuf) -> Self {
137 Self {
138 cause: cause.into(),
139 manifest,
140 }
141 }
142
143 pub fn manifest_path(&self) -> &PathBuf {
144 &self.manifest
145 }
146
147 pub fn manifest_causes(&self) -> ManifestCauses<'_> {
151 ManifestCauses { current: self }
152 }
153}
154
155impl std::error::Error for ManifestError {
156 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
157 self.cause.source()
158 }
159}
160
161impl fmt::Debug for ManifestError {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 self.cause.fmt(f)
164 }
165}
166
167impl fmt::Display for ManifestError {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 self.cause.fmt(f)
170 }
171}
172
173pub struct ManifestCauses<'a> {
175 current: &'a ManifestError,
176}
177
178impl<'a> Iterator for ManifestCauses<'a> {
179 type Item = &'a ManifestError;
180
181 fn next(&mut self) -> Option<Self::Item> {
182 self.current = self.current.cause.downcast_ref()?;
183 Some(self.current)
184 }
185}
186
187impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
188
189#[derive(Debug)]
192pub struct ProcessError {
193 pub desc: String,
195 pub exit: Option<ExitStatus>,
199 pub output: Option<Output>,
203}
204
205impl fmt::Display for ProcessError {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 self.desc.fmt(f)
208 }
209}
210
211impl std::error::Error for ProcessError {}
212
213#[derive(Debug)]
218pub struct CargoTestError {
219 pub test: Test,
220 pub desc: String,
221 pub exit: Option<ExitStatus>,
222 pub causes: Vec<ProcessError>,
223}
224
225impl fmt::Display for CargoTestError {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 self.desc.fmt(f)
228 }
229}
230
231impl std::error::Error for CargoTestError {}
232
233#[derive(Debug)]
234pub enum Test {
235 Multiple,
236 Doc,
237 UnitTest {
238 kind: TargetKind,
239 name: String,
240 pkg_name: String,
241 },
242}
243
244impl CargoTestError {
245 pub fn new(test: Test, errors: Vec<ProcessError>) -> Self {
246 if errors.is_empty() {
247 panic!("Cannot create CargoTestError from empty Vec")
248 }
249 let desc = errors
250 .iter()
251 .map(|error| error.desc.clone())
252 .collect::<Vec<String>>()
253 .join("\n");
254 CargoTestError {
255 test,
256 desc,
257 exit: errors[0].exit,
258 causes: errors,
259 }
260 }
261
262 pub fn hint(&self, ws: &Workspace<'_>, opts: &CompileOptions) -> String {
263 match self.test {
264 Test::UnitTest {
265 ref kind,
266 ref name,
267 ref pkg_name,
268 } => {
269 let pkg_info = if opts.spec.needs_spec_flag(ws) {
270 format!("-p {} ", pkg_name)
271 } else {
272 String::new()
273 };
274
275 match *kind {
276 TargetKind::Bench => {
277 format!("test failed, to rerun pass '{}--bench {}'", pkg_info, name)
278 }
279 TargetKind::Bin => {
280 format!("test failed, to rerun pass '{}--bin {}'", pkg_info, name)
281 }
282 TargetKind::Lib(_) => format!("test failed, to rerun pass '{}--lib'", pkg_info),
283 TargetKind::Test => {
284 format!("test failed, to rerun pass '{}--test {}'", pkg_info, name)
285 }
286 TargetKind::ExampleBin | TargetKind::ExampleLib(_) => {
287 format!("test failed, to rerun pass '{}--example {}", pkg_info, name)
288 }
289 _ => "test failed.".into(),
290 }
291 }
292 Test::Doc => "test failed, to rerun pass '--doc'".into(),
293 _ => "test failed.".into(),
294 }
295 }
296}
297
298pub type CliResult = Result<(), CliError>;
302
303#[derive(Debug)]
304pub struct CliError {
310 pub error: Option<anyhow::Error>,
316 pub exit_code: i32,
318}
319
320impl CliError {
321 pub fn new(error: anyhow::Error, code: i32) -> CliError {
322 CliError {
323 error: Some(error),
324 exit_code: code,
325 }
326 }
327
328 pub fn code(code: i32) -> CliError {
329 CliError {
330 error: None,
331 exit_code: code,
332 }
333 }
334}
335
336impl From<anyhow::Error> for CliError {
337 fn from(err: anyhow::Error) -> CliError {
338 CliError::new(err, 101)
339 }
340}
341
342impl From<clap::Error> for CliError {
343 fn from(err: clap::Error) -> CliError {
344 let code = if err.use_stderr() { 1 } else { 0 };
345 CliError::new(err.into(), code)
346 }
347}
348
349pub fn process_error(
357 msg: &str,
358 status: Option<ExitStatus>,
359 output: Option<&Output>,
360) -> ProcessError {
361 let exit = match status {
362 Some(s) => status_to_string(s),
363 None => "never executed".to_string(),
364 };
365 let mut desc = format!("{} ({})", &msg, exit);
366
367 if let Some(out) = output {
368 match str::from_utf8(&out.stdout) {
369 Ok(s) if !s.trim().is_empty() => {
370 desc.push_str("\n--- stdout\n");
371 desc.push_str(s);
372 }
373 Ok(..) | Err(..) => {}
374 }
375 match str::from_utf8(&out.stderr) {
376 Ok(s) if !s.trim().is_empty() => {
377 desc.push_str("\n--- stderr\n");
378 desc.push_str(s);
379 }
380 Ok(..) | Err(..) => {}
381 }
382 }
383
384 return ProcessError {
385 desc,
386 exit: status,
387 output: output.cloned(),
388 };
389
390 #[cfg(unix)]
391 fn status_to_string(status: ExitStatus) -> String {
392 use std::os::unix::process::*;
393
394 if let Some(signal) = status.signal() {
395 let name = match signal as libc::c_int {
396 libc::SIGABRT => ", SIGABRT: process abort signal",
397 libc::SIGALRM => ", SIGALRM: alarm clock",
398 libc::SIGFPE => ", SIGFPE: erroneous arithmetic operation",
399 libc::SIGHUP => ", SIGHUP: hangup",
400 libc::SIGILL => ", SIGILL: illegal instruction",
401 libc::SIGINT => ", SIGINT: terminal interrupt signal",
402 libc::SIGKILL => ", SIGKILL: kill",
403 libc::SIGPIPE => ", SIGPIPE: write on a pipe with no one to read",
404 libc::SIGQUIT => ", SIGQUIT: terminal quite signal",
405 libc::SIGSEGV => ", SIGSEGV: invalid memory reference",
406 libc::SIGTERM => ", SIGTERM: termination signal",
407 libc::SIGBUS => ", SIGBUS: access to undefined memory",
408 #[cfg(not(target_os = "haiku"))]
409 libc::SIGSYS => ", SIGSYS: bad system call",
410 libc::SIGTRAP => ", SIGTRAP: trace/breakpoint trap",
411 _ => "",
412 };
413 format!("signal: {}{}", signal, name)
414 } else {
415 status.to_string()
416 }
417 }
418
419 #[cfg(windows)]
420 fn status_to_string(status: ExitStatus) -> String {
421 use winapi::shared::minwindef::DWORD;
422 use winapi::um::winnt::*;
423
424 let mut base = status.to_string();
425 let extra = match status.code().unwrap() as DWORD {
426 STATUS_ACCESS_VIOLATION => "STATUS_ACCESS_VIOLATION",
427 STATUS_IN_PAGE_ERROR => "STATUS_IN_PAGE_ERROR",
428 STATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
429 STATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
430 STATUS_NO_MEMORY => "STATUS_NO_MEMORY",
431 STATUS_ILLEGAL_INSTRUCTION => "STATUS_ILLEGAL_INSTRUCTION",
432 STATUS_NONCONTINUABLE_EXCEPTION => "STATUS_NONCONTINUABLE_EXCEPTION",
433 STATUS_INVALID_DISPOSITION => "STATUS_INVALID_DISPOSITION",
434 STATUS_ARRAY_BOUNDS_EXCEEDED => "STATUS_ARRAY_BOUNDS_EXCEEDED",
435 STATUS_FLOAT_DENORMAL_OPERAND => "STATUS_FLOAT_DENORMAL_OPERAND",
436 STATUS_FLOAT_DIVIDE_BY_ZERO => "STATUS_FLOAT_DIVIDE_BY_ZERO",
437 STATUS_FLOAT_INEXACT_RESULT => "STATUS_FLOAT_INEXACT_RESULT",
438 STATUS_FLOAT_INVALID_OPERATION => "STATUS_FLOAT_INVALID_OPERATION",
439 STATUS_FLOAT_OVERFLOW => "STATUS_FLOAT_OVERFLOW",
440 STATUS_FLOAT_STACK_CHECK => "STATUS_FLOAT_STACK_CHECK",
441 STATUS_FLOAT_UNDERFLOW => "STATUS_FLOAT_UNDERFLOW",
442 STATUS_INTEGER_DIVIDE_BY_ZERO => "STATUS_INTEGER_DIVIDE_BY_ZERO",
443 STATUS_INTEGER_OVERFLOW => "STATUS_INTEGER_OVERFLOW",
444 STATUS_PRIVILEGED_INSTRUCTION => "STATUS_PRIVILEGED_INSTRUCTION",
445 STATUS_STACK_OVERFLOW => "STATUS_STACK_OVERFLOW",
446 STATUS_DLL_NOT_FOUND => "STATUS_DLL_NOT_FOUND",
447 STATUS_ORDINAL_NOT_FOUND => "STATUS_ORDINAL_NOT_FOUND",
448 STATUS_ENTRYPOINT_NOT_FOUND => "STATUS_ENTRYPOINT_NOT_FOUND",
449 STATUS_CONTROL_C_EXIT => "STATUS_CONTROL_C_EXIT",
450 STATUS_DLL_INIT_FAILED => "STATUS_DLL_INIT_FAILED",
451 STATUS_FLOAT_MULTIPLE_FAULTS => "STATUS_FLOAT_MULTIPLE_FAULTS",
452 STATUS_FLOAT_MULTIPLE_TRAPS => "STATUS_FLOAT_MULTIPLE_TRAPS",
453 STATUS_REG_NAT_CONSUMPTION => "STATUS_REG_NAT_CONSUMPTION",
454 STATUS_HEAP_CORRUPTION => "STATUS_HEAP_CORRUPTION",
455 STATUS_STACK_BUFFER_OVERRUN => "STATUS_STACK_BUFFER_OVERRUN",
456 STATUS_ASSERTION_FAILURE => "STATUS_ASSERTION_FAILURE",
457 _ => return base,
458 };
459 base.push_str(", ");
460 base.push_str(extra);
461 base
462 }
463}
464
465pub fn is_simple_exit_code(code: i32) -> bool {
466 code >= 0 && code <= 127
474}
475
476pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
477 InternalError::new(anyhow::format_err!("{}", error)).into()
478}