1use alloc::string::ToString;
2use core::fmt;
3use std::{error::Error, ops::Deref, str, string};
4
5use super::BT;
6
7use crate::{
8 errors::{RecoverPubkeyError, VerificationError},
9 Decimal256RangeExceeded, DecimalRangeExceeded, SignedDecimal256RangeExceeded,
10 SignedDecimalRangeExceeded,
11};
12
13mod sealed {
14 pub trait Sealed {}
15
16 impl<T> Sealed for Result<T, super::StdError> {}
17}
18
19pub trait StdResultExt<T>: sealed::Sealed {
20 fn unwrap_std_error(self) -> Result<T, Box<dyn Error + Send + Sync>>;
21}
22
23impl<T> StdResultExt<T> for Result<T, super::StdError> {
24 fn unwrap_std_error(self) -> Result<T, Box<dyn Error + Send + Sync>> {
25 self.map_err(|err| err.0.inner)
26 }
27}
28
29#[derive(Debug)]
45pub struct StdError(Box<InnerError>);
46
47#[derive(Debug)]
48struct InnerError {
49 backtrace: BT,
50 kind: ErrorKind,
51 inner: Box<dyn Error + Send + Sync>,
52}
53
54const _: () = {
55 assert!(std::mem::size_of::<StdError>() == std::mem::size_of::<usize>());
57};
58
59impl AsRef<dyn Error + Send + Sync> for StdError {
60 fn as_ref(&self) -> &(dyn Error + Send + Sync + 'static) {
61 &*self.0.inner
62 }
63}
64
65impl Deref for StdError {
66 type Target = dyn Error + Send + Sync;
67
68 fn deref(&self) -> &Self::Target {
69 &*self.0.inner
70 }
71}
72
73impl StdError {
74 pub fn msg<D>(msg: D) -> Self
75 where
76 D: fmt::Display,
77 {
78 Self(Box::new(InnerError {
79 backtrace: BT::capture(),
80 kind: ErrorKind::Other,
81 inner: msg.to_string().into(),
82 }))
83 }
84
85 pub fn backtrace(&self) -> &BT {
86 &self.0.backtrace
87 }
88
89 pub fn is<T>(&self) -> bool
90 where
91 T: Error + 'static,
92 {
93 self.0.inner.is::<T>()
94 }
95
96 pub fn kind(&self) -> ErrorKind {
97 self.0.kind
98 }
99
100 pub fn with_kind(mut self, kind: ErrorKind) -> Self {
101 self.0.kind = kind;
102 self
103 }
104}
105
106impl fmt::Display for StdError {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 write!(f, "kind: {:?}, error: {}", self.0.kind, self.0.inner)
109 }
110}
111
112impl<E> From<E> for StdError
120where
121 E: Error + Send + Sync + 'static,
122{
123 fn from(value: E) -> Self {
124 let inner: Box<dyn Error + Send + Sync> = Box::new(value);
125
126 let kind = if inner.is::<str::Utf8Error>()
130 || inner.is::<string::FromUtf8Error>()
131 || inner.is::<core::num::ParseIntError>()
132 || inner.is::<CoinFromStrError>()
133 {
134 ErrorKind::Parsing
135 } else if inner.is::<ConversionOverflowError>()
136 || inner.is::<OverflowError>()
137 || inner.is::<RoundUpOverflowError>()
138 || inner.is::<RoundDownOverflowError>()
139 || inner.is::<DecimalRangeExceeded>()
140 || inner.is::<Decimal256RangeExceeded>()
141 || inner.is::<SignedDecimalRangeExceeded>()
142 || inner.is::<SignedDecimal256RangeExceeded>()
143 {
144 ErrorKind::Overflow
145 } else if inner.is::<serde_json::Error>()
146 || inner.is::<rmp_serde::encode::Error>()
147 || inner.is::<rmp_serde::decode::Error>()
148 {
149 ErrorKind::Serialization
150 } else if inner.is::<RecoverPubkeyError>() || inner.is::<VerificationError>() {
151 ErrorKind::Cryptography
152 } else if inner.is::<hex::FromHexError>() || inner.is::<base64::DecodeError>() {
153 ErrorKind::Encoding
154 } else {
155 ErrorKind::Other
156 };
157
158 Self(Box::new(InnerError {
159 backtrace: BT::capture(),
160 kind,
161 inner,
162 }))
163 }
164}
165
166#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
167#[non_exhaustive]
168pub enum ErrorKind {
169 Cryptography,
170 Encoding,
171 InvalidData,
172 Overflow,
173 Parsing,
174 Serialization,
175
176 Other,
177}
178
179pub type StdResult<T, E = StdError> = core::result::Result<T, E>;
185
186#[derive(Debug, PartialEq, Eq)]
187pub enum OverflowOperation {
188 Add,
189 Sub,
190 Mul,
191 Pow,
192 Shr,
193 Shl,
194}
195
196impl fmt::Display for OverflowOperation {
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198 write!(f, "{self:?}")
199 }
200}
201
202#[derive(Debug, PartialEq, Eq, thiserror::Error)]
203#[error("Cannot {operation} with given operands")]
204pub struct OverflowError {
205 pub operation: OverflowOperation,
206}
207
208impl OverflowError {
209 pub fn new(operation: OverflowOperation) -> Self {
210 Self { operation }
211 }
212}
213
214#[derive(Debug, PartialEq, Eq, thiserror::Error)]
221#[error("Error converting {source_type} to {target_type}")]
222pub struct ConversionOverflowError {
223 pub source_type: &'static str,
224 pub target_type: &'static str,
225}
226
227impl ConversionOverflowError {
228 pub fn new(source_type: &'static str, target_type: &'static str) -> Self {
229 Self {
230 source_type,
231 target_type,
232 }
233 }
234}
235
236#[derive(Debug, Default, PartialEq, Eq, thiserror::Error)]
237#[error("Cannot divide by zero")]
238pub struct DivideByZeroError;
239
240impl DivideByZeroError {
241 pub fn new() -> Self {
242 Self
243 }
244}
245
246#[derive(Debug, PartialEq, Eq, thiserror::Error)]
247pub enum DivisionError {
248 #[error("Divide by zero")]
249 DivideByZero,
250
251 #[error("Overflow in division")]
252 Overflow,
253}
254
255#[derive(Debug, PartialEq, Eq, thiserror::Error)]
256pub enum CheckedMultiplyFractionError {
257 #[error("{_0}")]
258 DivideByZero(#[from] DivideByZeroError),
259
260 #[error("{_0}")]
261 ConversionOverflow(#[from] ConversionOverflowError),
262
263 #[error("{_0}")]
264 Overflow(#[from] OverflowError),
265}
266
267#[derive(Debug, PartialEq, Eq, thiserror::Error)]
268pub enum CheckedMultiplyRatioError {
269 #[error("Denominator must not be zero")]
270 DivideByZero,
271
272 #[error("Multiplication overflow")]
273 Overflow,
274}
275
276#[derive(Debug, PartialEq, Eq, thiserror::Error)]
277pub enum CheckedFromRatioError {
278 #[error("Denominator must not be zero")]
279 DivideByZero,
280
281 #[error("Overflow")]
282 Overflow,
283}
284
285#[derive(Debug, PartialEq, Eq, thiserror::Error)]
286#[error("Round up operation failed because of overflow")]
287pub struct RoundUpOverflowError;
288
289#[derive(Debug, PartialEq, Eq, thiserror::Error)]
290#[error("Round down operation failed because of overflow")]
291pub struct RoundDownOverflowError;
292
293#[derive(Debug, PartialEq, Eq, thiserror::Error)]
294pub enum CoinsError {
295 #[error("Duplicate denom")]
296 DuplicateDenom,
297}
298
299#[derive(Debug, PartialEq, Eq, thiserror::Error)]
300pub enum CoinFromStrError {
301 #[error("Missing denominator")]
302 MissingDenom,
303 #[error("Missing amount or non-digit characters in amount")]
304 MissingAmount,
305 #[error("Invalid amount: {_0}")]
306 InvalidAmount(core::num::ParseIntError),
307}
308
309impl From<core::num::ParseIntError> for CoinFromStrError {
310 fn from(value: core::num::ParseIntError) -> Self {
311 Self::InvalidAmount(value)
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318 use core::str;
319 use std::string;
320
321 #[derive(Debug, thiserror::Error)]
322 enum AssertThiserrorWorks {
323 #[error(transparent)]
324 Std(#[from] StdError),
325 }
326
327 #[test]
328 fn implements_debug() {
329 let error: StdError = StdError::from(OverflowError::new(OverflowOperation::Sub));
330 let embedded = format!("Debug: {error:?}");
331 assert!(embedded
332 .starts_with("Debug: StdError(InnerError { backtrace: <disabled>, kind: Overflow, inner: OverflowError { operation: Sub } })"), "{embedded}");
333 }
334
335 #[test]
336 fn implements_display() {
337 let error: StdError = StdError::from(OverflowError::new(OverflowOperation::Sub));
338 let embedded = format!("Display: {error}");
339 assert_eq!(
340 embedded, "Display: kind: Overflow, error: Cannot Sub with given operands",
341 "{embedded}"
342 );
343 }
344
345 #[test]
346 fn from_std_str_utf8error_works() {
347 let broken = Vec::from(b"Hello \xF0\x90\x80World" as &[u8]);
348 let error: StdError = str::from_utf8(&broken).unwrap_err().into();
349 assert!(error.is::<str::Utf8Error>());
350
351 assert!(error
352 .to_string()
353 .ends_with("invalid utf-8 sequence of 3 bytes from index 6"));
354 }
355
356 #[test]
357 fn from_std_string_from_utf8error_works() {
358 let error: StdError = String::from_utf8(b"Hello \xF0\x90\x80World".to_vec())
359 .unwrap_err()
360 .into();
361
362 assert!(error.is::<string::FromUtf8Error>());
363 assert!(error
364 .to_string()
365 .ends_with("invalid utf-8 sequence of 3 bytes from index 6"));
366 }
367}