use anyhow::Error;
use curl::easy::Easy;
use std::fmt::{self, Write};
use std::path::PathBuf;
use super::truncate_with_ellipsis;
pub type CargoResult<T> = anyhow::Result<T>;
pub const DEBUG_HEADERS: &[&str] = &[
"x-amz-cf-id",
"x-amz-cf-pop",
"x-amz-request-id",
"x-amz-id-2",
"x-cache",
"x-served-by",
];
#[derive(Debug)]
pub struct HttpNotSuccessful {
pub code: u32,
pub url: String,
pub ip: Option<String>,
pub body: Vec<u8>,
pub headers: Vec<String>,
}
impl HttpNotSuccessful {
pub fn new_from_handle(
handle: &mut Easy,
initial_url: &str,
body: Vec<u8>,
headers: Vec<String>,
) -> HttpNotSuccessful {
let ip = handle.primary_ip().ok().flatten().map(|s| s.to_string());
let url = handle
.effective_url()
.ok()
.flatten()
.unwrap_or(initial_url)
.to_string();
HttpNotSuccessful {
code: handle.response_code().unwrap_or(0),
url,
ip,
body,
headers,
}
}
pub fn display_short(&self) -> String {
self.render(false)
}
fn render(&self, show_headers: bool) -> String {
let mut result = String::new();
let body = std::str::from_utf8(&self.body)
.map(|s| truncate_with_ellipsis(s, 512))
.unwrap_or_else(|_| format!("[{} non-utf8 bytes]", self.body.len()));
write!(
result,
"failed to get successful HTTP response from `{}`",
self.url
)
.unwrap();
if let Some(ip) = &self.ip {
write!(result, " ({ip})").unwrap();
}
write!(result, ", got {}\n", self.code).unwrap();
if show_headers {
let headers: Vec<_> = self
.headers
.iter()
.filter(|header| {
let Some((name, _)) = header.split_once(":") else {
return false;
};
DEBUG_HEADERS.contains(&name.to_ascii_lowercase().trim())
})
.collect();
if !headers.is_empty() {
writeln!(result, "debug headers:").unwrap();
for header in headers {
writeln!(result, "{header}").unwrap();
}
}
}
write!(result, "body:\n{body}").unwrap();
result
}
}
impl fmt::Display for HttpNotSuccessful {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.render(true))
}
}
impl std::error::Error for HttpNotSuccessful {}
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)
}
}
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)
}
}
pub struct AlreadyPrintedError {
inner: Error,
}
impl AlreadyPrintedError {
pub fn new(inner: Error) -> Self {
AlreadyPrintedError { inner }
}
}
impl std::error::Error for AlreadyPrintedError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.source()
}
}
impl fmt::Debug for AlreadyPrintedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl fmt::Display for AlreadyPrintedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
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
}
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)
}
}
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> {}
pub type CliResult = Result<(), CliError>;
#[derive(Debug)]
pub struct CliError {
pub error: Option<anyhow::Error>,
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)
}
}
impl From<std::io::Error> for CliError {
fn from(err: std::io::Error) -> CliError {
CliError::new(err.into(), 1)
}
}
pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
InternalError::new(anyhow::format_err!("{}", error)).into()
}