mirror of https://github.com/rust-lang/cargo
330 lines
8.6 KiB
Rust
330 lines
8.6 KiB
Rust
#![allow(unknown_lints)]
|
|
|
|
use crate::core::{TargetKind, Workspace};
|
|
use crate::ops::CompileOptions;
|
|
use anyhow::Error;
|
|
use cargo_util::ProcessError;
|
|
use std::fmt;
|
|
use std::path::PathBuf;
|
|
|
|
pub type CargoResult<T> = anyhow::Result<T>;
|
|
|
|
// TODO: should delete this trait and just use `with_context` instead
|
|
pub trait CargoResultExt<T, E> {
|
|
fn chain_err<F, D>(self, f: F) -> CargoResult<T>
|
|
where
|
|
F: FnOnce() -> D,
|
|
D: fmt::Display + Send + Sync + 'static;
|
|
}
|
|
|
|
impl<T, E> CargoResultExt<T, E> for Result<T, E>
|
|
where
|
|
E: Into<Error>,
|
|
{
|
|
fn chain_err<F, D>(self, f: F) -> CargoResult<T>
|
|
where
|
|
F: FnOnce() -> D,
|
|
D: fmt::Display + Send + Sync + 'static,
|
|
{
|
|
self.map_err(|e| e.into().context(f()))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct HttpNot200 {
|
|
pub code: u32,
|
|
pub url: String,
|
|
}
|
|
|
|
impl fmt::Display for HttpNot200 {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"failed to get 200 response from `{}`, got {}",
|
|
self.url, self.code
|
|
)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for HttpNot200 {}
|
|
|
|
// =============================================================================
|
|
// Verbose error
|
|
|
|
/// An error wrapper for errors that should only be displayed with `--verbose`.
|
|
///
|
|
/// This should only be used in rare cases. When emitting this error, you
|
|
/// should have a normal error higher up the error-cause chain (like "could
|
|
/// not compile `foo`"), so at least *something* gets printed without
|
|
/// `--verbose`.
|
|
pub struct VerboseError {
|
|
inner: Error,
|
|
}
|
|
|
|
impl VerboseError {
|
|
pub fn new(inner: Error) -> VerboseError {
|
|
VerboseError { inner }
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for VerboseError {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
self.inner.source()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for VerboseError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.inner.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for VerboseError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.inner.fmt(f)
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Internal error
|
|
|
|
/// An unexpected, internal error.
|
|
///
|
|
/// This should only be used for unexpected errors. It prints a message asking
|
|
/// the user to file a bug report.
|
|
pub struct InternalError {
|
|
inner: Error,
|
|
}
|
|
|
|
impl InternalError {
|
|
pub fn new(inner: Error) -> InternalError {
|
|
InternalError { inner }
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for InternalError {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
self.inner.source()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for InternalError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.inner.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for InternalError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.inner.fmt(f)
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Manifest error
|
|
|
|
/// Error wrapper related to a particular manifest and providing it's path.
|
|
///
|
|
/// This error adds no displayable info of it's own.
|
|
pub struct ManifestError {
|
|
cause: Error,
|
|
manifest: PathBuf,
|
|
}
|
|
|
|
impl ManifestError {
|
|
pub fn new<E: Into<Error>>(cause: E, manifest: PathBuf) -> Self {
|
|
Self {
|
|
cause: cause.into(),
|
|
manifest,
|
|
}
|
|
}
|
|
|
|
pub fn manifest_path(&self) -> &PathBuf {
|
|
&self.manifest
|
|
}
|
|
|
|
/// Returns an iterator over the `ManifestError` chain of causes.
|
|
///
|
|
/// So if this error was not caused by another `ManifestError` this will be empty.
|
|
pub fn manifest_causes(&self) -> ManifestCauses<'_> {
|
|
ManifestCauses { current: self }
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for ManifestError {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
self.cause.source()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for ManifestError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.cause.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for ManifestError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.cause.fmt(f)
|
|
}
|
|
}
|
|
|
|
/// An iterator over the `ManifestError` chain of causes.
|
|
pub struct ManifestCauses<'a> {
|
|
current: &'a ManifestError,
|
|
}
|
|
|
|
impl<'a> Iterator for ManifestCauses<'a> {
|
|
type Item = &'a ManifestError;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.current = self.current.cause.downcast_ref()?;
|
|
Some(self.current)
|
|
}
|
|
}
|
|
|
|
impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
|
|
|
|
// =============================================================================
|
|
// Cargo test errors.
|
|
|
|
/// Error when testcases fail
|
|
#[derive(Debug)]
|
|
pub struct CargoTestError {
|
|
pub test: Test,
|
|
pub desc: String,
|
|
pub code: Option<i32>,
|
|
pub causes: Vec<ProcessError>,
|
|
}
|
|
|
|
impl fmt::Display for CargoTestError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.desc.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for CargoTestError {}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Test {
|
|
Multiple,
|
|
Doc,
|
|
UnitTest {
|
|
kind: TargetKind,
|
|
name: String,
|
|
pkg_name: String,
|
|
},
|
|
}
|
|
|
|
impl CargoTestError {
|
|
pub fn new(test: Test, errors: Vec<ProcessError>) -> Self {
|
|
if errors.is_empty() {
|
|
panic!("Cannot create CargoTestError from empty Vec")
|
|
}
|
|
let desc = errors
|
|
.iter()
|
|
.map(|error| error.desc.clone())
|
|
.collect::<Vec<String>>()
|
|
.join("\n");
|
|
CargoTestError {
|
|
test,
|
|
desc,
|
|
code: errors[0].code,
|
|
causes: errors,
|
|
}
|
|
}
|
|
|
|
pub fn hint(&self, ws: &Workspace<'_>, opts: &CompileOptions) -> String {
|
|
match self.test {
|
|
Test::UnitTest {
|
|
ref kind,
|
|
ref name,
|
|
ref pkg_name,
|
|
} => {
|
|
let pkg_info = if opts.spec.needs_spec_flag(ws) {
|
|
format!("-p {} ", pkg_name)
|
|
} else {
|
|
String::new()
|
|
};
|
|
|
|
match *kind {
|
|
TargetKind::Bench => {
|
|
format!("test failed, to rerun pass '{}--bench {}'", pkg_info, name)
|
|
}
|
|
TargetKind::Bin => {
|
|
format!("test failed, to rerun pass '{}--bin {}'", pkg_info, name)
|
|
}
|
|
TargetKind::Lib(_) => format!("test failed, to rerun pass '{}--lib'", pkg_info),
|
|
TargetKind::Test => {
|
|
format!("test failed, to rerun pass '{}--test {}'", pkg_info, name)
|
|
}
|
|
TargetKind::ExampleBin | TargetKind::ExampleLib(_) => {
|
|
format!("test failed, to rerun pass '{}--example {}", pkg_info, name)
|
|
}
|
|
_ => "test failed.".into(),
|
|
}
|
|
}
|
|
Test::Doc => "test failed, to rerun pass '--doc'".into(),
|
|
_ => "test failed.".into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// CLI errors
|
|
|
|
pub type CliResult = Result<(), CliError>;
|
|
|
|
#[derive(Debug)]
|
|
/// The CLI error is the error type used at Cargo's CLI-layer.
|
|
///
|
|
/// All errors from the lib side of Cargo will get wrapped with this error.
|
|
/// Other errors (such as command-line argument validation) will create this
|
|
/// directly.
|
|
pub struct CliError {
|
|
/// The error to display. This can be `None` in rare cases to exit with a
|
|
/// code without displaying a message. For example `cargo run -q` where
|
|
/// the resulting process exits with a nonzero code (on Windows), or an
|
|
/// external subcommand that exits nonzero (we assume it printed its own
|
|
/// message).
|
|
pub error: Option<anyhow::Error>,
|
|
/// The process exit code.
|
|
pub exit_code: i32,
|
|
}
|
|
|
|
impl CliError {
|
|
pub fn new(error: anyhow::Error, code: i32) -> CliError {
|
|
CliError {
|
|
error: Some(error),
|
|
exit_code: code,
|
|
}
|
|
}
|
|
|
|
pub fn code(code: i32) -> CliError {
|
|
CliError {
|
|
error: None,
|
|
exit_code: code,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<anyhow::Error> for CliError {
|
|
fn from(err: anyhow::Error) -> CliError {
|
|
CliError::new(err, 101)
|
|
}
|
|
}
|
|
|
|
impl From<clap::Error> for CliError {
|
|
fn from(err: clap::Error) -> CliError {
|
|
let code = if err.use_stderr() { 1 } else { 0 };
|
|
CliError::new(err.into(), code)
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Construction helpers
|
|
|
|
pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
|
|
InternalError::new(anyhow::format_err!("{}", error)).into()
|
|
}
|