diff --git a/packages/mocks/src/encoding.rs b/packages/mocks/src/encoding.rs index ffb2d03..ae18730 100644 --- a/packages/mocks/src/encoding.rs +++ b/packages/mocks/src/encoding.rs @@ -9,9 +9,18 @@ use storey_encoding::{Cover, DecodableWithImpl, EncodableWithImpl, Encoding}; pub struct TestEncoding; +#[derive(Debug, PartialEq)] +pub struct MockError; + +impl std::fmt::Display for MockError { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Ok(()) + } +} + impl Encoding for TestEncoding { - type DecodeError = (); - type EncodeError = (); + type DecodeError = MockError; + type EncodeError = MockError; } // This is how we would implement `EncodableWith` and `DecodableWith` for @@ -39,16 +48,16 @@ where // Imagine `MyTestEncoding` is a third-party trait that we don't control. trait MyTestEncoding: Sized { - fn my_encode(&self) -> Result, ()>; - fn my_decode(data: &[u8]) -> Result; + fn my_encode(&self) -> Result, MockError>; + fn my_decode(data: &[u8]) -> Result; } impl MyTestEncoding for u64 { - fn my_encode(&self) -> Result, ()> { + fn my_encode(&self) -> Result, MockError> { Ok(self.to_le_bytes().to_vec()) } - fn my_decode(data: &[u8]) -> Result { + fn my_decode(data: &[u8]) -> Result { let mut bytes = [0u8; 8]; bytes.copy_from_slice(data); Ok(u64::from_le_bytes(bytes)) diff --git a/packages/storey-encoding/src/lib.rs b/packages/storey-encoding/src/lib.rs index 15317f5..8c65c9e 100644 --- a/packages/storey-encoding/src/lib.rs +++ b/packages/storey-encoding/src/lib.rs @@ -1,9 +1,9 @@ pub trait Encoding { /// The error type returned when encoding fails. - type EncodeError; + type EncodeError: std::fmt::Display; /// The error type returned when decoding fails. - type DecodeError; + type DecodeError: std::fmt::Display; } pub trait EncodableWith: sealed::SealedE { diff --git a/packages/storey/src/containers/item.rs b/packages/storey/src/containers/item.rs index 4180d06..465a5eb 100644 --- a/packages/storey/src/containers/item.rs +++ b/packages/storey/src/containers/item.rs @@ -212,7 +212,7 @@ impl ItemAccess where E: Encoding, T: EncodableWith + DecodableWith, - S: StorageMut, + S: Storage + StorageMut, { /// Set the value of the item. /// @@ -234,6 +234,14 @@ where Ok(()) } + pub fn update(&mut self, f: F) -> Result<(), UpdateError> + where + F: FnOnce(Option) -> T, + { + let new_value = f(self.get().map_err(UpdateError::Decode)?); + self.set(&new_value).map_err(UpdateError::Encode) + } + /// Remove the value of the item. /// /// # Example @@ -254,6 +262,19 @@ where } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, thiserror::Error)] +pub enum UpdateError +where + E: Encoding, + E::DecodeError: std::fmt::Display, + E::EncodeError: std::fmt::Display, +{ + #[error("decode error: {0}")] + Decode(E::DecodeError), + #[error("encode error: {0}")] + Encode(E::EncodeError), +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/storey/src/encoding.rs b/packages/storey/src/encoding.rs index 0b311c2..85e1d8c 100644 --- a/packages/storey/src/encoding.rs +++ b/packages/storey/src/encoding.rs @@ -34,15 +34,15 @@ //! struct DisplayEncoding; //! //! impl Encoding for DisplayEncoding { -//! type DecodeError = (); -//! type EncodeError = (); +//! type DecodeError = String; +//! type EncodeError = String; //! } //! //! impl EncodableWithImpl for Cover<&T,> //! where //! T: std::fmt::Display, //! { -//! fn encode_impl(self) -> Result, ()> { +//! fn encode_impl(self) -> Result, String> { //! Ok(format!("{}", self.0).into_bytes()) //! } //! } @@ -67,17 +67,18 @@ //! struct DisplayEncoding; //! //! impl Encoding for DisplayEncoding { -//! type DecodeError = (); -//! type EncodeError = (); +//! type DecodeError = String; +//! type EncodeError = String; //! } //! //! impl DecodableWithImpl for Cover //! where //! T: std::str::FromStr, //! { -//! fn decode_impl(data: &[u8]) -> Result { -//! let string = String::from_utf8(data.to_vec()).map_err(|_| ())?; -//! let value = string.parse().map_err(|_| ())?; +//! fn decode_impl(data: &[u8]) -> Result { +//! let string = +//! String::from_utf8(data.to_vec()).map_err(|_| "string isn't UTF-8".to_string())?; +//! let value = string.parse().map_err(|_| "parsing failed".to_string())?; //! Ok(Cover(value)) //! } //! }