use std::marker::PhantomData;
use fe2o3_amqp_types::messaging::{
message::__private::Serializable, ApplicationProperties, Batch, Data, DeliveryAnnotations,
Footer, Header, Message, MessageAnnotations, Properties,
};
use serde_amqp::serialized_size;
pub(crate) const U8_MAX: usize = u8::MAX as usize;
pub(crate) const U8_MAX_MINUS_1: usize = u8::MAX as usize - 1;
pub(crate) const U32_MAX_MINUS_4: usize = u32::MAX as usize - 4;
#[derive(Debug)]
pub(crate) struct Phantom<T> {
serialized_len: usize,
marker: PhantomData<T>,
}
impl<T> Clone for Phantom<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Phantom<T> {}
impl<T> Phantom<T> {
pub(crate) fn new(serialized_len: usize) -> Self {
Self {
serialized_len,
marker: PhantomData,
}
}
}
macro_rules! impl_try_from_type_for_phantom {
($ty:ty) => {
impl<'a> TryFrom<&'a $ty> for Phantom<$ty> {
type Error = serde_amqp::Error;
fn try_from(value: &'a $ty) -> Result<Self, Self::Error> {
let serialized_len = serialized_size(value)?;
Ok(Self::new(serialized_len))
}
}
};
($($ty:ty),+) => {
$(impl_try_from_type_for_phantom!($ty);)+
};
}
impl_try_from_type_for_phantom! {
Header,
DeliveryAnnotations,
MessageAnnotations,
Properties,
ApplicationProperties,
Data,
Batch<Data>,
Footer
}
macro_rules! impl_try_from_optional_type_for_phantom {
(Option<$ty:ty>) => {
impl<'a> TryFrom<&'a Option<$ty>> for Phantom<Option<$ty>> {
type Error = serde_amqp::Error;
fn try_from(value: &'a Option<$ty>) -> Result<Self, Self::Error> {
match value {
Some(value) => {
Ok(Self::new(Phantom::try_from(value)?.serialized_len))
},
None => Ok(Phantom::new(0)),
}
}
}
};
($(Option<$ty:ty>),+) => {
$(impl_try_from_optional_type_for_phantom!{Option<$ty>})+
};
}
impl_try_from_optional_type_for_phantom! {
Option<Header>,
Option<DeliveryAnnotations>,
Option<MessageAnnotations>,
Option<Properties>,
Option<ApplicationProperties>,
Option<Footer>
}
impl Phantom<Data> {
pub(crate) fn with_message_size(message_size: usize) -> Result<Self, serde_amqp::Error> {
let length_size = match message_size {
0..=U8_MAX_MINUS_1 => 1usize,
U8_MAX..=U32_MAX_MINUS_4 => 4usize,
_ => return Err(serde_amqp::Error::InvalidLength),
};
let overhead = 1 + 1 + 1 + 1 + length_size;
Ok(Self::new(message_size + overhead))
}
}
impl<'a> TryFrom<&'a Message<Data>> for Phantom<Data> {
type Error = serde_amqp::Error;
fn try_from(value: &'a Message<Data>) -> Result<Self, Self::Error> {
let message_size = serialized_size(&Serializable(value))?;
Self::with_message_size(message_size)
}
}
impl Phantom<Batch<Data>> {
pub(crate) fn push(&mut self, data: Phantom<Data>) {
self.serialized_len += data.serialized_len;
}
pub(crate) fn pop(&mut self, data: Phantom<Data>) {
self.serialized_len -= data.serialized_len;
}
pub(crate) fn clear(&mut self) {
self.serialized_len = 0;
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct PhantomMessage<T> {
pub(crate) header: Phantom<Option<Header>>,
pub(crate) delivery_annotations: Phantom<Option<DeliveryAnnotations>>,
pub(crate) message_annotations: Phantom<Option<MessageAnnotations>>,
pub(crate) properties: Phantom<Option<Properties>>,
pub(crate) application_properties: Phantom<Option<ApplicationProperties>>,
pub(crate) body: Phantom<T>,
pub(crate) footer: Phantom<Option<Footer>>,
}
impl<T> PhantomMessage<T> {
pub(crate) fn serialized_size(&self) -> usize {
self.header.serialized_len
+ self.delivery_annotations.serialized_len
+ self.message_annotations.serialized_len
+ self.properties.serialized_len
+ self.application_properties.serialized_len
+ self.body.serialized_len
+ self.footer.serialized_len
}
}
impl<'a> TryFrom<&'a Message<Data>> for PhantomMessage<Data> {
type Error = serde_amqp::Error;
fn try_from(value: &'a Message<Data>) -> Result<Self, Self::Error> {
let header = Phantom::try_from(&value.header)?;
let delivery_annotations = Phantom::try_from(&value.delivery_annotations)?;
let message_annotations = Phantom::try_from(&value.message_annotations)?;
let properties = Phantom::try_from(&value.properties)?;
let application_properties = Phantom::try_from(&value.application_properties)?;
let body = Phantom::try_from(&value.body)?;
let footer = Phantom::try_from(&value.footer)?;
Ok(Self {
header,
delivery_annotations,
message_annotations,
properties,
application_properties,
body,
footer,
})
}
}
impl<'a> TryFrom<&'a Message<Batch<Data>>> for PhantomMessage<Batch<Data>> {
type Error = serde_amqp::Error;
fn try_from(value: &'a Message<Batch<Data>>) -> Result<Self, Self::Error> {
let header = Phantom::try_from(&value.header)?;
let delivery_annotations = Phantom::try_from(&value.delivery_annotations)?;
let message_annotations = Phantom::try_from(&value.message_annotations)?;
let properties = Phantom::try_from(&value.properties)?;
let application_properties = Phantom::try_from(&value.application_properties)?;
let body = Phantom::try_from(&value.body)?;
let footer = Phantom::try_from(&value.footer)?;
Ok(Self {
header,
delivery_annotations,
message_annotations,
properties,
application_properties,
body,
footer,
})
}
}
#[cfg(test)]
mod tests {
use fe2o3_amqp_types::messaging::message::__private::Serializable;
use serde_amqp::{serialized_size, to_value, to_vec, Value};
use crate::{
amqp::amqp_message_converter::{build_amqp_batch_from_messages, SendableEnvelope},
EventData,
};
use super::{Phantom, PhantomMessage};
fn phantom_size_and_serialized_size_of_sendable_envelope(
sendable: SendableEnvelope,
) -> (usize, usize) {
match sendable {
SendableEnvelope::Single(sendable) => {
let phantom_message = PhantomMessage::try_from(&sendable.message).unwrap();
let phantom_size = phantom_message.serialized_size();
let serializable = Serializable(sendable.message);
let ssize = serialized_size(&serializable).unwrap();
(phantom_size, ssize)
}
SendableEnvelope::Batch(sendable) => {
let phantom_message = PhantomMessage::try_from(&sendable.message).unwrap();
let phantom_size = phantom_message.serialized_size();
let serializable = Serializable(sendable.message);
let ssize = serialized_size(&serializable).unwrap();
(phantom_size, ssize)
}
}
}
fn serialized_value_of_sendable(sendable: SendableEnvelope) -> Value {
match sendable {
SendableEnvelope::Single(sendable) => {
let serializable = Serializable(sendable.message);
to_value(&serializable).unwrap()
}
SendableEnvelope::Batch(sendable) => {
let serializable = Serializable(sendable.message);
to_value(&serializable).unwrap()
}
}
}
fn serialized_bytes_of_sendable(sendable: SendableEnvelope) -> Vec<u8> {
match sendable {
SendableEnvelope::Single(sendable) => {
let serializable = Serializable(sendable.message);
to_vec(&serializable).unwrap()
}
SendableEnvelope::Batch(sendable) => {
let serializable = Serializable(sendable.message);
to_vec(&serializable).unwrap()
}
}
}
#[test]
fn phantom_message_size_with_one_event() {
let data = "abcdefghij";
let event = EventData::from(data);
let message_iter = std::iter::once(event.amqp_message);
let batch = build_amqp_batch_from_messages(message_iter.clone(), None).unwrap();
let _serialized_value = serialized_value_of_sendable(batch.sendable);
let batch = build_amqp_batch_from_messages(message_iter.clone(), None).unwrap();
let _serialized_bytes = serialized_bytes_of_sendable(batch.sendable);
let batch = build_amqp_batch_from_messages(message_iter, None).unwrap();
let (phantom_size, ssize) =
phantom_size_and_serialized_size_of_sendable_envelope(batch.sendable);
assert_eq!(phantom_size, ssize)
}
#[test]
fn phantom_message_size_with_multiple_events() {
let data = "abcdefghij";
let event = EventData::from(data);
let messages = vec![event.amqp_message.clone(); 2];
let batch = build_amqp_batch_from_messages(messages.clone().into_iter(), None).unwrap();
let serialized_bytes = serialized_bytes_of_sendable(batch.sendable);
let batch = build_amqp_batch_from_messages(messages.into_iter(), None).unwrap();
let (_, serialized_size) =
phantom_size_and_serialized_size_of_sendable_envelope(batch.sendable);
assert_eq!(serialized_size, serialized_bytes.len());
let mut phantom_envelope = PhantomMessage {
header: Phantom::try_from(&None).unwrap(),
delivery_annotations: Phantom::try_from(&None).unwrap(),
message_annotations: Phantom::try_from(&None).unwrap(),
properties: Phantom::try_from(&None).unwrap(),
application_properties: Phantom::try_from(&None).unwrap(),
body: Phantom::new(0),
footer: Phantom::try_from(&None).unwrap(),
};
let phantom_event_body = Phantom::try_from(&event.amqp_message).unwrap();
phantom_envelope.body.push(phantom_event_body);
let phantom_event_body = Phantom::try_from(&event.amqp_message).unwrap();
phantom_envelope.body.push(phantom_event_body);
let phantom_size = phantom_envelope.serialized_size();
assert_eq!(phantom_size, serialized_size)
}
}