use crate::event::{Event, EventKind, EventPayload};
use serde::de::DeserializeOwned;
#[derive(Debug)]
pub enum DecodeSource {
Json(serde_json::Error),
Msgpack(rmp_serde::decode::Error),
}
impl std::fmt::Display for DecodeSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Json(e) => write!(f, "json decode: {e}"),
Self::Msgpack(e) => write!(f, "msgpack decode: {e}"),
}
}
}
impl std::error::Error for DecodeSource {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Json(e) => Some(e),
Self::Msgpack(e) => Some(e),
}
}
}
#[derive(Debug)]
pub enum TypedDecodeError {
KindMismatch {
expected: EventKind,
got: EventKind,
},
DecodeFailure {
kind: EventKind,
source: DecodeSource,
},
}
impl std::fmt::Display for TypedDecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::KindMismatch { expected, got } => {
write!(f, "kind mismatch: expected {expected:?}, got {got:?}")
}
Self::DecodeFailure { kind, source } => {
write!(f, "decode failed for kind {kind:?}: {source}")
}
}
}
}
impl std::error::Error for TypedDecodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::KindMismatch { .. } => None,
Self::DecodeFailure { source, .. } => Some(source),
}
}
}
pub trait DecodeTyped {
fn route_typed<T: EventPayload>(&self) -> Result<Option<T>, TypedDecodeError>;
fn decode_typed<T: EventPayload>(&self) -> Result<T, TypedDecodeError>;
}
impl DecodeTyped for Event<serde_json::Value> {
fn route_typed<T: EventPayload>(&self) -> Result<Option<T>, TypedDecodeError> {
if self.header.event_kind != T::KIND {
return Ok(None);
}
decode_json::<T>(self.header.event_kind, &self.payload).map(Some)
}
fn decode_typed<T: EventPayload>(&self) -> Result<T, TypedDecodeError> {
if self.header.event_kind != T::KIND {
return Err(TypedDecodeError::KindMismatch {
expected: T::KIND,
got: self.header.event_kind,
});
}
decode_json::<T>(self.header.event_kind, &self.payload)
}
}
impl DecodeTyped for Event<Vec<u8>> {
fn route_typed<T: EventPayload>(&self) -> Result<Option<T>, TypedDecodeError> {
if self.header.event_kind != T::KIND {
return Ok(None);
}
decode_msgpack::<T>(self.header.event_kind, &self.payload).map(Some)
}
fn decode_typed<T: EventPayload>(&self) -> Result<T, TypedDecodeError> {
if self.header.event_kind != T::KIND {
return Err(TypedDecodeError::KindMismatch {
expected: T::KIND,
got: self.header.event_kind,
});
}
decode_msgpack::<T>(self.header.event_kind, &self.payload)
}
}
fn decode_json<T: DeserializeOwned>(
kind: EventKind,
value: &serde_json::Value,
) -> Result<T, TypedDecodeError> {
T::deserialize(value).map_err(|e| TypedDecodeError::DecodeFailure {
kind,
source: DecodeSource::Json(e),
})
}
fn decode_msgpack<T: DeserializeOwned>(
kind: EventKind,
bytes: &[u8],
) -> Result<T, TypedDecodeError> {
rmp_serde::from_slice::<T>(bytes).map_err(|e| TypedDecodeError::DecodeFailure {
kind,
source: DecodeSource::Msgpack(e),
})
}
#[cfg(test)]
mod in_crate_derive_proof {
use super::DecodeTyped;
use ::batpak::EventPayload;
#[derive(Clone, serde::Serialize, serde::Deserialize, PartialEq, Debug, EventPayload)]
#[batpak(category = 0xE, type_id = 0xAB1)]
struct InCrateProof {
value: u64,
}
#[test]
fn derive_resolves_from_inside_crate() {
let expected = ::batpak::event::EventKind::custom(0xE, 0xAB1);
assert_eq!(
<InCrateProof as ::batpak::event::EventPayload>::KIND,
expected,
"PROPERTY: ::batpak::... paths must resolve from inside the crate (pub extern crate self as batpak)"
);
}
#[test]
fn route_typed_works_from_inside_crate() {
use crate::event::{Event, EventHeader};
let header = EventHeader::new(
1,
0,
None,
0,
crate::coordinate::DagPosition::root(),
0,
<InCrateProof as ::batpak::event::EventPayload>::KIND,
);
let event: Event<serde_json::Value> =
Event::new(header, serde_json::json!({ "value": 99 }));
let routed: Option<InCrateProof> = event.route_typed().expect("route_typed");
assert_eq!(routed, Some(InCrateProof { value: 99 }));
}
}