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),
ArkServer(ArkServerError),
Core(CoreError),
CoinSelect(CoinSelectError),
Wallet(WalletError),
Consumer(ConsumerError),
}
#[derive(Debug)]
struct AdHocError {
source: Source,
}
#[derive(Debug)]
struct ArkServerError {
source: Source,
}
#[derive(Debug)]
struct CoreError {
source: ark_core::Error,
}
#[derive(Debug)]
struct CoinSelectError {
source: Source,
}
#[derive(Debug)]
struct WalletError {
source: Source,
}
#[derive(Debug)]
struct ConsumerError {
source: Source,
}
impl Error {
fn new(kind: Kind) -> Self {
Self {
inner: Box::new(ErrorImpl { kind, cause: None }),
}
}
pub(crate) fn ad_hoc(source: impl Into<Source>) -> Self {
Error::new(Kind::AdHoc(AdHocError {
source: source.into(),
}))
}
pub(crate) fn ark_server(source: impl Into<Source>) -> Self {
Error::new(Kind::ArkServer(ArkServerError {
source: source.into(),
}))
}
pub(crate) fn coin_select(source: impl Into<Source>) -> Self {
Error::new(Kind::CoinSelect(CoinSelectError {
source: source.into(),
}))
}
pub fn wallet(source: impl Into<Source>) -> Self {
Error::new(Kind::Wallet(WalletError {
source: source.into(),
}))
}
pub fn consumer(source: impl Into<Source>) -> Self {
Error::new(Kind::Consumer(ConsumerError {
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::ArkServer(ref err) => err.fmt(f),
Kind::Core(ref err) => err.fmt(f),
Kind::CoinSelect(ref err) => err.fmt(f),
Kind::Wallet(ref err) => err.fmt(f),
Kind::Consumer(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 ArkServerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl fmt::Display for CoreError {
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 WalletError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl fmt::Display for ConsumerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl From<ark_core::Error> for Error {
fn from(value: ark_core::Error) -> Self {
Self::new(Kind::Core(CoreError { source: value }))
}
}
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 {
type Output;
fn context(self, consequent: impl IntoError) -> Self::Output;
fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Self::Output;
}
impl ErrorContext for Error {
type Output = 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, E> ErrorContext for Result<T, E>
where
E: StdError + Send + Sync + 'static,
{
type Output = Result<T, Error>;
fn context(self, consequent: impl IntoError) -> Result<T, Error> {
self.map_err(|err| {
let err: Box<dyn StdError + Send + Sync + 'static> = Box::new(err);
match err.downcast::<Error>() {
Ok(err) => (*err).context(consequent),
Err(err) => Error::ad_hoc(err).context(consequent),
}
})
}
fn with_context<C: IntoError>(self, consequent: impl FnOnce() -> C) -> Result<T, Error> {
self.map_err(|err| {
let err: Box<dyn StdError + Send + Sync + 'static> = Box::new(err);
match err.downcast::<Error>() {
Ok(err) => (*err).with_context(consequent),
Err(err) => Error::ad_hoc(err).with_context(consequent),
}
})
}
}
impl From<ark_grpc::Error> for Error {
fn from(value: ark_grpc::Error) -> Self {
Self::ark_server(value)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.inner
.cause
.as_ref()
.map(|e| e as &(dyn StdError + 'static))
}
}