use crate::event::upcast::{self, UpcastError};
use crate::event::{Event, EventKind, EventPayload};
#[derive(Debug)]
#[non_exhaustive]
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,
},
FutureVersion {
kind: EventKind,
stored: u16,
current: u16,
},
Upcast {
kind: EventKind,
source: UpcastError,
},
}
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}")
}
Self::FutureVersion {
kind,
stored,
current,
} => write!(
f,
"future payload version for kind {kind:?}: stored frame is version {stored} but \
this decoder understands at most version {current}; upgrade the reader"
),
Self::Upcast { kind, source } => {
write!(f, "upcast failed for kind {kind:?}: {source}")
}
}
}
}
impl std::error::Error for TypedDecodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::KindMismatch { .. } | Self::FutureVersion { .. } => None,
Self::DecodeFailure { source, .. } => Some(source),
Self::Upcast { 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.header.payload_version,
&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.header.payload_version,
&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.header.payload_version,
&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.header.payload_version,
&self.payload,
)
}
}
fn decode_versioned<T, FCurrent, FValue>(
kind: EventKind,
stored: u16,
decode_current: FCurrent,
value: FValue,
) -> Result<T, TypedDecodeError>
where
T: EventPayload,
FCurrent: FnOnce() -> Result<T, TypedDecodeError>,
FValue: FnOnce() -> Result<rmpv::Value, UpcastError>,
{
let current = T::PAYLOAD_VERSION;
if stored == current || stored == 0 {
return decode_current();
}
if stored > current {
return Err(TypedDecodeError::FutureVersion {
kind,
stored,
current,
});
}
let value = value().map_err(|source| TypedDecodeError::Upcast { kind, source })?;
upcast::upcast_and_decode::<T>(value, stored, current)
.map_err(|source| TypedDecodeError::Upcast { kind, source })
}
fn decode_json<T: EventPayload>(
kind: EventKind,
stored: u16,
value: &serde_json::Value,
) -> Result<T, TypedDecodeError> {
decode_versioned::<T, _, _>(
kind,
stored,
|| {
T::deserialize(value).map_err(|e| TypedDecodeError::DecodeFailure {
kind,
source: DecodeSource::Json(e),
})
},
|| upcast::value_from_json(value),
)
}
fn decode_msgpack<T: EventPayload>(
kind: EventKind,
stored: u16,
bytes: &[u8],
) -> Result<T, TypedDecodeError> {
decode_versioned::<T, _, _>(
kind,
stored,
|| {
crate::encoding::from_bytes::<T>(bytes).map_err(|e| TypedDecodeError::DecodeFailure {
kind,
source: DecodeSource::Msgpack(e),
})
},
|| upcast::value_from_msgpack(bytes),
)
}
#[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 }));
}
}