use crate::abi::TypeCode;
#[doc(hidden)]
pub mod _sealed {
pub trait Sealed {}
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum DeserializeError {
SchemaVersionMismatch {
expected: u32,
got: u32,
},
PayloadMalformed,
UnknownTypeCode {
observed: TypeCode,
},
LengthExceedsBudget,
}
impl core::fmt::Display for DeserializeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::SchemaVersionMismatch { expected, got } => {
write!(
f,
"schema version mismatch: expected {}, got {}",
expected, got
)
}
Self::PayloadMalformed => write!(f, "payload malformed"),
Self::UnknownTypeCode { observed } => {
write!(f, "unknown type code: {:?}", observed)
}
Self::LengthExceedsBudget => write!(f, "deserialization length exceeds budget"),
}
}
}
impl std::error::Error for DeserializeError {}
pub trait Component:
_sealed::Sealed + serde::Serialize + serde::de::DeserializeOwned + 'static
{
const TYPE_CODE: TypeCode;
const SCHEMA_VERSION: u32;
fn canonical_bytes(&self) -> Vec<u8>
where
Self: Sized,
{
postcard::to_allocvec(self).expect("postcard encode self for Component::canonical_bytes")
}
fn from_bytes(version: u32, bytes: &[u8]) -> Result<Box<Self>, DeserializeError>
where
Self: Sized,
{
if version != Self::SCHEMA_VERSION {
return Err(DeserializeError::SchemaVersionMismatch {
expected: Self::SCHEMA_VERSION,
got: version,
});
}
postcard::from_bytes::<Self>(bytes)
.map(Box::new)
.map_err(|_| DeserializeError::PayloadMalformed)
}
fn approx_size(&self) -> usize
where
Self: Sized,
{
self.canonical_bytes().len()
}
}
pub trait ActionDeriv:
_sealed::Sealed + serde::Serialize + serde::de::DeserializeOwned + 'static
{
const TYPE_CODE: TypeCode;
const SCHEMA_VERSION: u32;
}
pub trait ActionCompute: 'static {
fn compute(&self, ctx: &super::context::ActionContext) -> Vec<super::op::Op>;
}
pub trait Action: ActionDeriv + ActionCompute {
fn canonical_bytes(&self) -> Vec<u8>
where
Self: Sized,
{
postcard::to_allocvec(self).expect("postcard encode self for canonical_bytes")
}
fn from_bytes(version: u32, bytes: &[u8]) -> Result<Box<Self>, DeserializeError>
where
Self: Sized,
{
if version != Self::SCHEMA_VERSION {
return Err(DeserializeError::SchemaVersionMismatch {
expected: Self::SCHEMA_VERSION,
got: version,
});
}
postcard::from_bytes::<Self>(bytes)
.map(Box::new)
.map_err(|_| DeserializeError::PayloadMalformed)
}
fn approx_size(&self) -> usize
where
Self: Sized,
{
self.canonical_bytes().len()
}
}
impl<T: ActionDeriv + ActionCompute> Action for T {}
pub trait Event:
_sealed::Sealed + std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned + 'static
{
const TYPE_CODE: TypeCode;
const SCHEMA_VERSION: u32;
fn canonical_bytes(&self) -> Vec<u8>
where
Self: Sized,
{
postcard::to_allocvec(self).expect("postcard encode self for Event::canonical_bytes")
}
fn from_bytes(version: u32, bytes: &[u8]) -> Result<Box<Self>, DeserializeError>
where
Self: Sized,
{
if version != Self::SCHEMA_VERSION {
return Err(DeserializeError::SchemaVersionMismatch {
expected: Self::SCHEMA_VERSION,
got: version,
});
}
postcard::from_bytes::<Self>(bytes)
.map(Box::new)
.map_err(|_| DeserializeError::PayloadMalformed)
}
fn approx_size(&self) -> usize
where
Self: Sized,
{
self.canonical_bytes().len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_error_display_includes_versions() {
let e = DeserializeError::SchemaVersionMismatch {
expected: 1,
got: 2,
};
let s = format!("{}", e);
assert!(s.contains("expected 1"));
assert!(s.contains("got 2"));
}
#[test]
fn deserialize_error_payload_malformed_displays() {
let e = DeserializeError::PayloadMalformed;
assert_eq!(format!("{}", e), "payload malformed");
}
#[test]
fn deserialize_error_implements_std_error() {
fn assert_err<E: std::error::Error>() {}
assert_err::<DeserializeError>();
}
#[test]
fn sealed_trait_is_implementable_within_crate() {
struct CrateInternalProof;
impl _sealed::Sealed for CrateInternalProof {}
let _ = CrateInternalProof;
}
}