use crate::convert::TYPE_URL_PREFIX;
use crate::proto::page_header::SequenceType;
use crate::proto::{
AngzarrDeferredSequence, CommandPage, EventPage, ExternalDeferredSequence, MergeStrategy,
PageHeader,
};
use prost::Name;
pub trait PageHeaderExt {
fn explicit_sequence(&self) -> Option<u32>;
fn is_deferred(&self) -> bool;
fn external_deferred(&self) -> Option<&ExternalDeferredSequence>;
fn angzarr_deferred(&self) -> Option<&AngzarrDeferredSequence>;
}
impl PageHeaderExt for PageHeader {
fn explicit_sequence(&self) -> Option<u32> {
match &self.sequence_type {
Some(SequenceType::Sequence(seq)) => Some(*seq),
_ => None,
}
}
fn is_deferred(&self) -> bool {
matches!(
&self.sequence_type,
Some(SequenceType::ExternalDeferred(_)) | Some(SequenceType::AngzarrDeferred(_))
)
}
fn external_deferred(&self) -> Option<&ExternalDeferredSequence> {
match &self.sequence_type {
Some(SequenceType::ExternalDeferred(ext)) => Some(ext),
_ => None,
}
}
fn angzarr_deferred(&self) -> Option<&AngzarrDeferredSequence> {
match &self.sequence_type {
Some(SequenceType::AngzarrDeferred(ang)) => Some(ang),
_ => None,
}
}
}
pub trait EventPageExt {
fn sequence_num(&self) -> u32;
fn header(&self) -> Option<&PageHeader>;
fn is_deferred(&self) -> bool;
fn type_url(&self) -> Option<&str>;
fn payload(&self) -> Option<&[u8]>;
fn decode_typed<M: prost::Message + Default + Name>(&self) -> Option<M>;
}
impl EventPageExt for EventPage {
fn sequence_num(&self) -> u32 {
self.header
.as_ref()
.and_then(|h| h.explicit_sequence())
.unwrap_or(0)
}
fn header(&self) -> Option<&PageHeader> {
self.header.as_ref()
}
fn is_deferred(&self) -> bool {
self.header
.as_ref()
.map(|h| h.is_deferred())
.unwrap_or(false)
}
fn type_url(&self) -> Option<&str> {
match &self.payload {
Some(crate::proto::event_page::Payload::Event(e)) => Some(e.type_url.as_str()),
_ => None,
}
}
fn payload(&self) -> Option<&[u8]> {
match &self.payload {
Some(crate::proto::event_page::Payload::Event(e)) => Some(e.value.as_slice()),
_ => None,
}
}
fn decode_typed<M: prost::Message + Default + Name>(&self) -> Option<M> {
let event = match &self.payload {
Some(crate::proto::event_page::Payload::Event(e)) => e,
_ => return None,
};
let expected = format!("{}{}", TYPE_URL_PREFIX, M::full_name());
if event.type_url != expected {
return None;
}
M::decode(event.value.as_slice()).ok()
}
}
pub trait CommandPageExt {
fn sequence_num(&self) -> u32;
fn header(&self) -> Option<&PageHeader>;
fn is_deferred(&self) -> bool;
fn type_url(&self) -> Option<&str>;
fn payload(&self) -> Option<&[u8]>;
fn decode_typed<M: prost::Message + Default + Name>(&self) -> Option<M>;
fn merge_strategy(&self) -> MergeStrategy;
}
impl CommandPageExt for CommandPage {
fn sequence_num(&self) -> u32 {
self.header
.as_ref()
.and_then(|h| h.explicit_sequence())
.unwrap_or(0)
}
fn header(&self) -> Option<&PageHeader> {
self.header.as_ref()
}
fn is_deferred(&self) -> bool {
self.header
.as_ref()
.map(|h| h.is_deferred())
.unwrap_or(false)
}
fn type_url(&self) -> Option<&str> {
match &self.payload {
Some(crate::proto::command_page::Payload::Command(c)) => Some(c.type_url.as_str()),
_ => None,
}
}
fn payload(&self) -> Option<&[u8]> {
match &self.payload {
Some(crate::proto::command_page::Payload::Command(c)) => Some(c.value.as_slice()),
_ => None,
}
}
fn decode_typed<M: prost::Message + Default + Name>(&self) -> Option<M> {
let command = match &self.payload {
Some(crate::proto::command_page::Payload::Command(c)) => c,
_ => return None,
};
let expected = format!("{}{}", TYPE_URL_PREFIX, M::full_name());
if command.type_url != expected {
return None;
}
M::decode(command.value.as_slice()).ok()
}
fn merge_strategy(&self) -> MergeStrategy {
MergeStrategy::try_from(self.merge_strategy).unwrap_or(MergeStrategy::MergeCommutative)
}
}
pub trait AngzarrDeferredSequenceExt {
fn idempotency_key(&self) -> String;
}
impl AngzarrDeferredSequenceExt for AngzarrDeferredSequence {
fn idempotency_key(&self) -> String {
use super::cover::CoverExt;
let source = self.source.as_ref().expect("source required");
format!(
"{}:{}:{}:{}",
source.edition(),
source.domain,
source.root_id_hex().unwrap_or_default(),
self.source_seq
)
}
}