use std::error::Error as StdError;
use std::fmt;
type Source = Box<dyn StdError + Send + Sync + 'static>;
#[derive(Debug)]
pub struct Error {
inner: Box<ErrorImpl>,
}
#[derive(Debug)]
struct ErrorImpl {
kind: Kind,
cause: Option<Error>,
}
#[derive(Debug)]
enum Kind {
AdHoc(AdHocError),
Crypto(CryptoError),
Transaction(TransactionError),
CoinSelect(CoinSelectError),
ArkAddress(ArkAddressError),
}
#[derive(Debug)]
struct AdHocError {
source: Source,
}
#[derive(Debug)]
struct CryptoError {
source: Source,
}
#[derive(Debug)]
struct TransactionError {
source: Source,
}
#[derive(Debug)]
struct CoinSelectError {
source: Source,
}
#[derive(Debug)]
struct ArkAddressError {
source: Source,
}
impl Error {
fn new(kind: Kind) -> Self {
Self {
inner: Box::new(ErrorImpl { kind, cause: None }),
}
}
pub fn ad_hoc(source: impl Into<Source>) -> Self {
Error::new(Kind::AdHoc(AdHocError {
source: source.into(),
}))
}
pub(crate) fn crypto(source: impl Into<Source>) -> Self {
Error::new(Kind::Crypto(CryptoError {
source: source.into(),
}))
}
pub(crate) fn transaction(source: impl Into<Source>) -> Self {
Error::new(Kind::Transaction(TransactionError {
source: source.into(),
}))
}
pub(crate) fn coin_select(source: impl Into<Source>) -> Self {
Error::new(Kind::CoinSelect(CoinSelectError {
source: source.into(),
}))
}
pub(crate) fn address_format(source: impl Into<Source>) -> Self {
Error::new(Kind::ArkAddress(ArkAddressError {
source: source.into(),
}))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut err = self;
loop {
write!(f, "{}", err.inner.kind)?;
err = match err.inner.cause.as_ref() {
None => break,
Some(err) => err,
};
write!(f, ": ")?;
}
Ok(())
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Kind::AdHoc(ref err) => err.fmt(f),
Kind::Crypto(ref err) => err.fmt(f),
Kind::Transaction(ref err) => err.fmt(f),
Kind::CoinSelect(ref err) => err.fmt(f),
Kind::ArkAddress(ref err) => err.fmt(f),
}
}
}
impl fmt::Display for AdHocError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl fmt::Display for CryptoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl fmt::Display for TransactionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl fmt::Display for CoinSelectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl fmt::Display for ArkAddressError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
pub trait IntoError {
fn into_error(self) -> Error;
}
impl IntoError for Error {
fn into_error(self) -> Error {
self
}
}
impl IntoError for &'static str {
fn into_error(self) -> Error {
Error::ad_hoc(self)
}
}
impl IntoError for String {
fn into_error(self) -> Error {
Error::ad_hoc(self)
}
}
pub trait ErrorContext {
fn context(self, consequent: impl IntoError) -> Self;
fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Self;
}
impl ErrorContext for Error {
fn context(self, consequent: impl IntoError) -> Error {
let mut err = consequent.into_error();
assert!(
err.inner.cause.is_none(),
"cause of consequence must be `None`"
);
err.inner.cause = Some(self);
err
}
fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Error {
let mut err = consequent().into_error();
assert!(
err.inner.cause.is_none(),
"cause of consequence must be `None`"
);
err.inner.cause = Some(self);
err
}
}
impl<T> ErrorContext for Result<T, Error> {
fn context(self, consequent: impl IntoError) -> Result<T, Error> {
self.map_err(|err| err.context(consequent))
}
fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Result<T, Error> {
self.map_err(|err| err.with_context(consequent))
}
}
impl StdError for Error {}