use std::fmt::{Display, Formatter};
#[cfg(feature = "backtrace")]
use std::backtrace::{Backtrace, BacktraceStatus};
use std::error::Error as StdError;
use thiserror::Error;
use crate::S;
type BoxedError = Box<dyn StdError + Send + Sync>;
#[derive(Debug)]
pub enum ErrorKind
{
FirmwareFileIo( Option<String>),
InvalidFirmware( Option<String>),
TooManyDevices,
DeviceNotFound,
DeviceDisconnectDuringOperation,
DeviceReboot,
DeviceSeemsInvalid( String),
External(ErrorSource),
}
impl ErrorKind
{
#[inline(always)]
pub fn error(self) -> Error
{
Error::new(self, None)
}
#[inline(always)]
pub fn error_from<E: StdError + Send + Sync + 'static>(self, source: E) -> Error
{
Error::new(self, Some(Box::new(source)))
}
}
impl From<ErrorKind> for Error
{
fn from(other: ErrorKind) -> Self
{
other.error()
}
}
impl Display for ErrorKind
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result
{
use ErrorKind::*;
match self {
FirmwareFileIo(None) => write!(f, "failed to read firmware file")?,
FirmwareFileIo(Some(filename)) => write!(f, "failed to read firmware file {}", filename)?,
TooManyDevices => write!(f, "current operation only supports one Black Magic Probe device but more than one device was found")?,
DeviceNotFound => write!(f, "Black Magic Probe device not found (check connection?)")?,
DeviceDisconnectDuringOperation => write!(f, "Black Magic Probe device found disconnected")?,
DeviceReboot => write!(f, "Black Magic Probe device did not come back online (invalid firmware?)")?,
DeviceSeemsInvalid(thing) => {
write!(
f,
"\nBlack Magic Probe device returned bad data ({}) during configuration.\n\
This generally shouldn't be possible. Maybe cable is bad, or OS is messing with things?",
thing,
)?;
},
InvalidFirmware(None) => write!(f, "specified firmware does not seem valid")?,
InvalidFirmware(Some(why)) => write!(f, "specified firmware does not seem valid: {}", why)?,
External(source) => {
use ErrorSource::*;
match source {
StdIo(e) => {
write!(f, "unhandled std::io::Error: {}", e)?;
},
Libusb(e) => {
write!(f, "unhandled libusb error: {}", e)?;
},
DfuLibusb(e) => {
write!(f, "unhandled dfu_libusb error: {}", e)?;
},
DfuCore(e) => {
write!(f, "unhandled dfu_core error: {}", e)?;
},
Goblin(e) => {
write!(f, "unhandled ELF parsing error: {}", e)?;
},
};
},
};
Ok(())
}
}
#[derive(Debug)]
pub struct Error
{
pub kind: ErrorKind,
pub source: Option<BoxedError>,
#[cfg(feature = "backtrace")]
pub backtrace: Box<Backtrace>,
pub context: Option<String>,
}
impl Error
{
#[inline(always)]
pub fn new(kind: ErrorKind, source: Option<BoxedError>) -> Self
{
Self {
kind,
source,
context: None,
#[cfg(feature = "backtrace")]
backtrace: Box::new(Backtrace::capture()),
}
}
#[allow(dead_code)]
pub fn with_ctx(mut self, ctx: &str) -> Self
{
self.context = Some(ctx.to_string());
self
}
#[allow(dead_code)]
pub fn without_ctx(mut self) -> Self
{
self.context = None;
self
}
#[cfg(feature = "backtrace")]
#[allow(dead_code)]
fn backtrace(&self) -> Option<&Backtrace>
{
Some(&self.backtrace)
}
}
impl Display for Error
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result
{
if let Some(ctx) = &self.context {
write!(f, "(while {}): {}", ctx, self.kind)?;
} else {
write!(f, "{}", self.kind)?;
}
#[cfg(feature = "backtrace")]
{
if self.backtrace.status() == BacktraceStatus::Captured {
write!(f, "\nBacktrace:\n{}", self.backtrace)?;
}
}
if let Some(source) = &self.source {
writeln!(f, "\nCaused by: {}", source)?;
}
Ok(())
}
}
impl StdError for Error
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)>
{
self.source.as_deref().map(|e| e as &dyn StdError)
}
}
impl From<rusb::Error> for Error
{
fn from(other: rusb::Error) -> Self
{
use ErrorKind::*;
match other {
rusb::Error::NoDevice => DeviceNotFound.error_from(other),
other => External(ErrorSource::Libusb(other)).error()
}
}
}
impl From<dfu_libusb::Error> for Error
{
fn from(other: dfu_libusb::Error) -> Self
{
use ErrorKind::*;
use dfu_libusb::Error as Source;
match other {
Source::LibUsb(source) => {
External(ErrorSource::Libusb(source)).error_from(other)
},
Source::MissingLanguage => {
DeviceSeemsInvalid(S!("no string descriptor languages"))
.error_from(other)
},
Source::InvalidAlt => {
DeviceSeemsInvalid(S!("DFU interface (alt mode) not found"))
.error_from(other)
},
Source::InvalidInterface => {
DeviceSeemsInvalid(S!("DFU interface not found"))
.error_from(other)
},
Source::FunctionalDescriptor(source) => {
DeviceSeemsInvalid(S!("DFU functional interface descriptor"))
.error_from(source)
},
anything_else => {
External(ErrorSource::DfuLibusb(anything_else))
.error()
},
}
}
}
impl From<dfu_core::Error> for Error
{
fn from(other: dfu_core::Error) -> Self
{
use ErrorKind::*;
use dfu_core::Error as Source;
match other {
Source::MemoryLayout(source) => {
DeviceSeemsInvalid(String::from("DFU interface memory layout string"))
.error_from(source)
},
Source::InvalidAddress => {
DeviceSeemsInvalid(S!("DFU interface memory layout string"))
.error_from(other)
},
Source::InvalidInterfaceString => {
DeviceSeemsInvalid(S!("DFU interface memory layout string"))
.error_from(other)
},
anything_else => {
External(ErrorSource::DfuCore(anything_else))
.error()
},
}
}
}
impl From<goblin::error::Error> for Error
{
fn from(other: goblin::error::Error) -> Self
{
use ErrorKind::*;
InvalidFirmware(None)
.error_from(External(ErrorSource::Goblin(other)).error())
}
}
#[derive(Debug, Error)]
pub enum ErrorSource
{
#[error(transparent)]
StdIo(#[from] std::io::Error),
#[error(transparent)]
Libusb(#[from] rusb::Error),
#[error(transparent)]
DfuLibusb(#[from] dfu_libusb::Error),
#[error(transparent)]
DfuCore(#[from] dfu_core::Error),
#[error(transparent)]
Goblin(#[from] goblin::error::Error),
}
pub trait ResErrorKind<T>
{
type Kind;
fn err_kind(&self) -> Result<&T, &Self::Kind>;
}
impl<T> ResErrorKind<T> for Result<T, Error>
{
type Kind = ErrorKind;
fn err_kind(&self) -> Result<&T, &Self::Kind>
{
self.as_ref().map_err(|e| &e.kind)
}
}
#[macro_export]
macro_rules! log_and_return
{
($err:expr) => {
let err = $err;
log::error!("{}", err);
return Err(err);
}
}