diff --git a/packages/cw-storey/src/lib.rs b/packages/cw-storey/src/lib.rs index 69297d0..455d8c0 100644 --- a/packages/cw-storey/src/lib.rs +++ b/packages/cw-storey/src/lib.rs @@ -13,6 +13,8 @@ mod backend; pub mod containers; mod encoding; +mod std_error; pub use backend::CwStorage; pub use encoding::CwEncoding; +pub use std_error::IntoStdError; diff --git a/packages/cw-storey/src/std_error.rs b/packages/cw-storey/src/std_error.rs new file mode 100644 index 0000000..2c5a2c2 --- /dev/null +++ b/packages/cw-storey/src/std_error.rs @@ -0,0 +1,57 @@ +/// A trait for converting *Storey* errors into [`cosmwasm_std::StdError`]. +pub trait IntoStdError { + /// Converts the error into a [`cosmwasm_std::StdError`] for use with CosmWasm. + /// + /// The error ends up as a [`cosmwasm_std::StdError::GenericErr`] with the error message + /// being the result of calling `to_string` on the error. + /// + /// # Example + /// ``` + /// use cosmwasm_std::StdError; + /// use storey::containers::map::key::ArrayDecodeError; + /// use cw_storey::IntoStdError as _; + /// + /// let error = ArrayDecodeError::InvalidLength; + /// assert_eq!(error.into_std_error(), StdError::generic_err(error.to_string())); + /// ``` + fn into_std_error(self) -> cosmwasm_std::StdError; +} + +impl IntoStdError for T +where + T: storey::error::StoreyError, +{ + fn into_std_error(self) -> cosmwasm_std::StdError { + cosmwasm_std::StdError::generic_err(self.to_string()) + } +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::StdError; + use storey::error::StoreyError; + + use super::*; + + #[derive(Debug)] + struct MockError { + msg: String, + } + + impl std::fmt::Display for MockError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.msg) + } + } + + impl StoreyError for MockError {} + + #[test] + fn test_into_std_error() { + let error = MockError { + msg: "An error occurred".to_string(), + }; + let std_error: StdError = error.into_std_error(); + assert_eq!(std_error, StdError::generic_err("An error occurred")); + } +} diff --git a/packages/storey/src/containers/common.rs b/packages/storey/src/containers/common.rs index 0125ea0..473c531 100644 --- a/packages/storey/src/containers/common.rs +++ b/packages/storey/src/containers/common.rs @@ -5,3 +5,5 @@ pub enum TryGetError { #[error(transparent)] DecodeError(#[from] E), } + +impl crate::error::StoreyError for TryGetError {} diff --git a/packages/storey/src/containers/map/key.rs b/packages/storey/src/containers/map/key.rs index 861c67b..d4de44d 100644 --- a/packages/storey/src/containers/map/key.rs +++ b/packages/storey/src/containers/map/key.rs @@ -47,6 +47,8 @@ impl Key for str { #[error("invalid UTF8")] pub struct InvalidUtf8; +impl crate::error::StoreyError for InvalidUtf8 {} + impl OwnedKey for String { type Error = InvalidUtf8; @@ -128,10 +130,14 @@ impl OwnedKey for Box<[u8]> { } /// An error type for decoding arrays. +#[derive(Debug, PartialEq, Eq, Clone, Copy, thiserror::Error)] pub enum ArrayDecodeError { + #[error("invalid length")] InvalidLength, } +impl crate::error::StoreyError for ArrayDecodeError {} + impl OwnedKey for [u8; N] { type Error = ArrayDecodeError; @@ -181,6 +187,8 @@ pub enum NumericKeyDecodeError { InvalidLength, } +impl crate::error::StoreyError for NumericKeyDecodeError {} + macro_rules! impl_key_for_numeric { ($($t:ty),*) => { $( diff --git a/packages/storey/src/containers/map/mod.rs b/packages/storey/src/containers/map/mod.rs index 8f5a606..dbacf9c 100644 --- a/packages/storey/src/containers/map/mod.rs +++ b/packages/storey/src/containers/map/mod.rs @@ -179,6 +179,8 @@ pub enum MapKeyDecodeError { Inner(I), } +impl crate::error::StoreyError for MapKeyDecodeError {} + /// An accessor for a map. /// /// The accessor provides methods for interacting with the map in storage. diff --git a/packages/storey/src/containers/mod.rs b/packages/storey/src/containers/mod.rs index 0b00fda..0c8b010 100644 --- a/packages/storey/src/containers/mod.rs +++ b/packages/storey/src/containers/mod.rs @@ -64,12 +64,16 @@ pub trait Storable { } /// A key-value pair decoding error. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, thiserror::Error)] pub enum KVDecodeError { + #[error("failed to decode key: {0}")] Key(K), + #[error("failed to decode value: {0}")] Value(V), } +impl crate::error::StoreyError for KVDecodeError {} + /// A trait for collection accessors (see [`Storable::Accessor`]) that provide iteration over /// their contents. pub trait IterableAccessor: Sized { diff --git a/packages/storey/src/error.rs b/packages/storey/src/error.rs new file mode 100644 index 0000000..5e30fe0 --- /dev/null +++ b/packages/storey/src/error.rs @@ -0,0 +1,7 @@ +use std::fmt::Display; + +/// A trait representing a Storey error. +/// +/// This trait is implemented for all Storey error types, allowing third-party crates +/// to implement extension traits for all of those error types. +pub trait StoreyError: Display {} diff --git a/packages/storey/src/lib.rs b/packages/storey/src/lib.rs index a73698f..b9a36dc 100644 --- a/packages/storey/src/lib.rs +++ b/packages/storey/src/lib.rs @@ -16,4 +16,5 @@ pub mod containers; pub mod encoding; +pub mod error; pub mod storage;