ark_core/
error.rs

1use std::error::Error as StdError;
2use std::fmt;
3
4type Source = Box<dyn StdError + Send + Sync + 'static>;
5
6#[derive(Debug)]
7pub struct Error {
8    inner: Box<ErrorImpl>,
9}
10
11#[derive(Debug)]
12struct ErrorImpl {
13    kind: Kind,
14    cause: Option<Error>,
15}
16
17#[derive(Debug)]
18enum Kind {
19    /// Ad-hoc error,
20    AdHoc(AdHocError),
21    /// An error related to cryptography.
22    Crypto(CryptoError),
23    /// An error related to constructing Bitcoin transactions.
24    Transaction(TransactionError),
25    /// An error related to coin selection of VTXOs and boarding outputs.
26    CoinSelect(CoinSelectError),
27    /// An error related to encoding or decoding an Ark address.
28    ArkAddress(ArkAddressError),
29}
30
31#[derive(Debug)]
32struct AdHocError {
33    source: Source,
34}
35
36#[derive(Debug)]
37struct CryptoError {
38    source: Source,
39}
40
41#[derive(Debug)]
42struct TransactionError {
43    source: Source,
44}
45
46#[derive(Debug)]
47struct CoinSelectError {
48    source: Source,
49}
50
51#[derive(Debug)]
52struct ArkAddressError {
53    source: Source,
54}
55
56impl Error {
57    fn new(kind: Kind) -> Self {
58        Self {
59            inner: Box::new(ErrorImpl { kind, cause: None }),
60        }
61    }
62
63    pub fn ad_hoc(source: impl Into<Source>) -> Self {
64        Error::new(Kind::AdHoc(AdHocError {
65            source: source.into(),
66        }))
67    }
68
69    pub(crate) fn crypto(source: impl Into<Source>) -> Self {
70        Error::new(Kind::Crypto(CryptoError {
71            source: source.into(),
72        }))
73    }
74
75    pub(crate) fn transaction(source: impl Into<Source>) -> Self {
76        Error::new(Kind::Transaction(TransactionError {
77            source: source.into(),
78        }))
79    }
80
81    pub(crate) fn coin_select(source: impl Into<Source>) -> Self {
82        Error::new(Kind::CoinSelect(CoinSelectError {
83            source: source.into(),
84        }))
85    }
86
87    pub(crate) fn address_format(source: impl Into<Source>) -> Self {
88        Error::new(Kind::ArkAddress(ArkAddressError {
89            source: source.into(),
90        }))
91    }
92}
93
94impl fmt::Display for Error {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        let mut err = self;
97        loop {
98            write!(f, "{}", err.inner.kind)?;
99            err = match err.inner.cause.as_ref() {
100                None => break,
101                Some(err) => err,
102            };
103            write!(f, ": ")?;
104        }
105        Ok(())
106    }
107}
108
109impl fmt::Display for Kind {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match *self {
112            Kind::AdHoc(ref err) => err.fmt(f),
113            Kind::Crypto(ref err) => err.fmt(f),
114            Kind::Transaction(ref err) => err.fmt(f),
115            Kind::CoinSelect(ref err) => err.fmt(f),
116            Kind::ArkAddress(ref err) => err.fmt(f),
117        }
118    }
119}
120
121impl fmt::Display for AdHocError {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        self.source.fmt(f)
124    }
125}
126
127impl fmt::Display for CryptoError {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        self.source.fmt(f)
130    }
131}
132
133impl fmt::Display for TransactionError {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        self.source.fmt(f)
136    }
137}
138
139impl fmt::Display for CoinSelectError {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        self.source.fmt(f)
142    }
143}
144
145impl fmt::Display for ArkAddressError {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        self.source.fmt(f)
148    }
149}
150
151pub trait IntoError {
152    fn into_error(self) -> Error;
153}
154
155impl IntoError for Error {
156    fn into_error(self) -> Error {
157        self
158    }
159}
160
161impl IntoError for &'static str {
162    fn into_error(self) -> Error {
163        Error::ad_hoc(self)
164    }
165}
166
167impl IntoError for String {
168    fn into_error(self) -> Error {
169        Error::ad_hoc(self)
170    }
171}
172
173/// A trait for contextualizing error values.
174///
175/// This makes it easy to contextualize either `Error` or `Result<T, Error>`.
176/// Specifically, in the latter case, it absolves one of the need to call
177/// `map_err` everywhere one wants to add context to an error.
178///
179/// This trick was borrowed from `jiff`, which borrowed it from `anyhow`.
180pub trait ErrorContext {
181    /// Contextualize the given consequent error with this (`self`) error as
182    /// the cause.
183    ///
184    /// This is equivalent to saying that "consequent is caused by self."
185    ///
186    /// Note that if an `Error` is given for `kind`, then this panics if it has
187    /// a cause. (Because the cause would otherwise be dropped. An error causal
188    /// chain is just a linked list, not a tree.)
189    fn context(self, consequent: impl IntoError) -> Self;
190
191    /// Like `context`, but hides error construction within a closure.
192    ///
193    /// This is useful if the creation of the consequent error is not otherwise
194    /// guarded and when error construction is potentially "costly" (i.e., it
195    /// allocates). The closure avoids paying the cost of contextual error
196    /// creation in the happy path.
197    ///
198    /// Usually this only makes sense to use on a `Result<T, Error>`, otherwise
199    /// the closure is just executed immediately anyway.
200    fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Self;
201}
202
203impl ErrorContext for Error {
204    fn context(self, consequent: impl IntoError) -> Error {
205        let mut err = consequent.into_error();
206        assert!(
207            err.inner.cause.is_none(),
208            "cause of consequence must be `None`"
209        );
210
211        err.inner.cause = Some(self);
212        err
213    }
214
215    fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Error {
216        let mut err = consequent().into_error();
217        assert!(
218            err.inner.cause.is_none(),
219            "cause of consequence must be `None`"
220        );
221
222        err.inner.cause = Some(self);
223        err
224    }
225}
226
227impl<T> ErrorContext for Result<T, Error> {
228    fn context(self, consequent: impl IntoError) -> Result<T, Error> {
229        self.map_err(|err| err.context(consequent))
230    }
231
232    fn with_context<E: IntoError>(self, consequent: impl FnOnce() -> E) -> Result<T, Error> {
233        self.map_err(|err| err.with_context(consequent))
234    }
235}
236
237impl StdError for Error {}