namada_storage/
error.rs

1//! Storage API error type, extensible with custom user errors and static string
2//! messages.
3
4use std::convert::Infallible;
5use std::num::TryFromIntError;
6
7use namada_core::arith;
8use thiserror::Error;
9
10use crate::db;
11
12#[allow(missing_docs)]
13#[derive(Error, Debug)]
14pub enum Error {
15    #[error("{0}")]
16    SimpleMessage(&'static str),
17    #[error("{0}")]
18    AllocMessage(String),
19    #[error("{0}")]
20    Custom(CustomError),
21    #[error("{0}: {1}")]
22    CustomWithMessage(&'static str, CustomError),
23}
24
25/// Result of a storage API call.
26pub type Result<T> = std::result::Result<T, Error>;
27
28/// Result extension to easily wrap custom errors into [`enum@Error`].
29// This is separate from `ResultExt`, because the implementation requires
30// different bounds for `T`.
31pub trait ResultExt<T> {
32    /// Convert a [`std::result::Result`] into storage_api [`Result`].
33    fn into_storage_result(self) -> Result<T>;
34
35    /// Add a static message to a possible error in [`Result`].
36    fn wrap_err(self, msg: &'static str) -> Result<T>;
37}
38
39impl<T, E> ResultExt<T> for std::result::Result<T, E>
40where
41    E: std::error::Error + Send + Sync + 'static,
42{
43    fn into_storage_result(self) -> Result<T> {
44        self.map_err(Error::new)
45    }
46
47    fn wrap_err(self, msg: &'static str) -> Result<T> {
48        self.map_err(|err| Error::wrap(msg, err))
49    }
50}
51
52impl Error {
53    /// Create an [`enum@Error`] from another [`std::error::Error`].
54    pub fn new<E>(error: E) -> Self
55    where
56        E: Into<Box<dyn std::error::Error + Send + Sync>>,
57    {
58        Self::Custom(CustomError(error.into()))
59    }
60
61    /// Create an [`enum@Error`] from a static message.
62    #[inline]
63    pub const fn new_const(msg: &'static str) -> Self {
64        Self::SimpleMessage(msg)
65    }
66
67    /// Create an [`enum@Error`] from a heap allocated message.
68    #[inline]
69    pub const fn new_alloc(msg: String) -> Self {
70        Self::AllocMessage(msg)
71    }
72
73    /// Wrap another [`std::error::Error`] with a static message.
74    pub fn wrap<E>(msg: &'static str, error: E) -> Self
75    where
76        E: Into<Box<dyn std::error::Error + Send + Sync>>,
77    {
78        Self::CustomWithMessage(msg, CustomError(error.into()))
79    }
80
81    /// Attempt to downgrade the inner error to `E` if any.
82    ///
83    /// If this [`enum@Error`] was constructed via [`new`] or [`wrap`] then this
84    /// function will attempt to perform downgrade on it, otherwise it will
85    /// return [`Err`].
86    ///
87    /// [`new`]: Error::new
88    /// [`wrap`]: Error::wrap
89    ///
90    /// To match on the inner error type when the downcast is successful, you'll
91    /// typically want to [`std::ops::Deref::deref`] it out of the [`Box`].
92    pub fn downcast<E>(self) -> std::result::Result<Box<E>, Self>
93    where
94        E: std::error::Error + Send + Sync + 'static,
95    {
96        match self {
97            Self::Custom(CustomError(b))
98            | Self::CustomWithMessage(_, CustomError(b))
99                if b.is::<E>() =>
100            {
101                let res = b.downcast::<E>();
102                Ok(res.unwrap())
103            }
104            _ => Err(self),
105        }
106    }
107
108    /// Returns some reference to the inner value if it is of type `E`, or
109    /// `None` if it isn't.
110    pub fn downcast_ref<E>(&self) -> Option<&E>
111    where
112        E: std::error::Error + Send + Sync + 'static,
113    {
114        match self {
115            Self::Custom(CustomError(b))
116            | Self::CustomWithMessage(_, CustomError(b))
117                if b.is::<E>() =>
118            {
119                b.downcast_ref::<E>()
120            }
121            _ => None,
122        }
123    }
124}
125
126/// A custom error
127#[derive(Debug)]
128pub struct CustomError(pub Box<dyn std::error::Error + Send + Sync>);
129
130impl std::fmt::Display for CustomError {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        self.0.fmt(f)
133    }
134}
135
136/// An extension to [`Option`] to allow turning `None` case to an Error from a
137/// static string (handy for WASM).
138pub trait OptionExt<T> {
139    /// Transforms the [`Option<T>`] into a [`Result<T>`], mapping
140    /// [`Some(v)`] to [`Ok(v)`] and [`None`] to the given static error
141    /// message.
142    fn ok_or_err_msg(self, msg: &'static str) -> Result<T>;
143}
144
145impl<T> OptionExt<T> for Option<T> {
146    fn ok_or_err_msg(self, msg: &'static str) -> Result<T> {
147        self.ok_or_else(|| Error::new_const(msg))
148    }
149}
150
151/// Convert `namada_storage::Error` into IBC `HandlerError`.
152/// It always returns `HostError::Other` though the storage error could happen
153/// in any storage access.
154impl From<Error> for namada_core::ibc::core::host::types::error::HostError {
155    fn from(error: Error) -> Self {
156        namada_core::ibc::core::host::types::error::HostError::Other {
157            description: format!("Storage error: {error}"),
158        }
159    }
160}
161
162impl From<arith::Error> for Error {
163    fn from(value: arith::Error) -> Self {
164        Error::new(value)
165    }
166}
167
168impl From<db::Error> for Error {
169    fn from(value: db::Error) -> Self {
170        Error::new(value)
171    }
172}
173
174impl From<namada_core::storage::Error> for Error {
175    fn from(value: namada_core::storage::Error) -> Self {
176        Error::new(value)
177    }
178}
179
180impl From<namada_core::DecodeError> for Error {
181    fn from(value: namada_core::DecodeError) -> Self {
182        Error::new(value)
183    }
184}
185impl From<namada_core::string_encoding::DecodeError> for Error {
186    fn from(value: namada_core::string_encoding::DecodeError) -> Self {
187        Error::new(value)
188    }
189}
190
191impl From<namada_core::hash::Error> for Error {
192    fn from(value: namada_core::hash::Error) -> Self {
193        Error::new(value)
194    }
195}
196
197impl From<namada_merkle_tree::Error> for Error {
198    fn from(value: namada_merkle_tree::Error) -> Self {
199        Error::new(value)
200    }
201}
202
203impl From<Infallible> for Error {
204    fn from(_value: Infallible) -> Self {
205        panic!("Infallible error can never be constructed")
206    }
207}
208
209impl From<TryFromIntError> for Error {
210    fn from(value: TryFromIntError) -> Self {
211        Error::new(value)
212    }
213}