use std::fmt;
use serde::Serialize;
use super::{
BatchPublishTransform, BatchTransformIdentity, ForReply, Outgoing, PublishContext,
PublishPipeline, PublishTransform, PublishTransformIdentity,
};
use crate::codec::Codec;
use crate::runtime::lifecycle::BoxError;
#[cfg(feature = "testing")]
use crate::testing::coordinator::record_reply_publish;
use crate::{HeaderMap, PayloadForm, Publisher};
pub struct TypedPublisher<P, C, PL = PublishTransformIdentity, BL = BatchTransformIdentity> {
pub(super) publisher: P,
pub(super) codec: C,
layers: PL,
batch_layers: BL,
}
impl<P, C, PL, BL> TypedPublisher<P, C, PL, BL> {
pub(crate) const fn live(publisher: P, codec: C, layers: PL, batch_layers: BL) -> Self {
Self {
publisher,
codec,
layers,
batch_layers,
}
}
pub(crate) const fn codec(&self) -> &C {
&self.codec
}
}
#[cfg(test)]
impl<P, C> TypedPublisher<P, C, PublishTransformIdentity, BatchTransformIdentity> {
pub(crate) const fn with_codec(publisher: P, codec: C) -> Self {
Self::live(
publisher,
codec,
PublishTransformIdentity,
BatchTransformIdentity,
)
}
}
impl<P: Publisher, C: Codec, PL, BL> TypedPublisher<P, C, PL, BL> {
fn outgoing<'n>(
&self,
name: &'n str,
payload: <P::Payload as PayloadForm>::Form<'n>,
) -> Outgoing<'n> {
let headers = self
.publisher
.base_headers()
.cloned()
.unwrap_or_else(HeaderMap::new);
<P::Payload as PayloadForm>::rebuilt(name.into(), payload, headers)
}
pub(crate) async fn publish<'e, T: Serialize + Sync, Cx, PP>(
&self,
name: &'e str,
value: &T,
pipeline: &PP,
cx: &PublishContext<'_, Cx>,
encode: <P::Payload as PayloadForm>::Encode<'e>,
) -> Result<(), BoxError>
where
PL: PublishTransform<ForReply<Cx>, P::Options>,
BL: Sync,
Cx: Sync,
PP: PublishPipeline,
{
let payload = <P::Payload as PayloadForm>::encoded(&self.codec, value, encode)
.map_err(|e| Box::new(e) as BoxError)?;
let mut out = self.outgoing(name, payload);
let mut options = None;
self.layers.apply(&mut out, &mut options, cx);
#[cfg(feature = "testing")]
record_reply_publish(out.name(), options.as_ref());
pipeline
.run(&mut out, &self.publisher, options.as_ref())
.await
}
pub(crate) async fn publish_pair<'e, Hd: Serialize + Sync, T: Serialize + Sync, Cx, PP>(
&self,
name: &'e str,
headers: &Hd,
value: &T,
pipeline: &PP,
cx: &PublishContext<'_, Cx>,
encode: <P::Payload as PayloadForm>::Encode<'e>,
) -> Result<(), BoxError>
where
PL: PublishTransform<ForReply<Cx>, P::Options>,
BL: Sync,
Cx: Sync,
PP: PublishPipeline,
{
let payload = <P::Payload as PayloadForm>::encoded(&self.codec, value, encode)
.map_err(|e| Box::new(e) as BoxError)?;
let mut out = self.outgoing(name, payload);
out.headers_mut()
.insert_typed(headers)
.map_err(|e| Box::new(e) as BoxError)?;
let mut options = None;
self.layers.apply(&mut out, &mut options, cx);
#[cfg(feature = "testing")]
record_reply_publish(out.name(), options.as_ref());
pipeline
.run(&mut out, &self.publisher, options.as_ref())
.await
}
pub(crate) async fn publish_batched<'e, T: Serialize + Sync, Cx, PP>(
&self,
name: &'e str,
value: &T,
pipeline: &PP,
cx: &PublishContext<'_, Cx>,
encode: <P::Payload as PayloadForm>::Encode<'e>,
) -> Result<(), BoxError>
where
PL: Sync,
BL: BatchPublishTransform<Cx, P::Options>,
Cx: Sync,
PP: PublishPipeline,
{
let payload = <P::Payload as PayloadForm>::encoded(&self.codec, value, encode)
.map_err(|e| Box::new(e) as BoxError)?;
let mut out = self.outgoing(name, payload);
let mut options = None;
self.batch_layers.apply(&mut out, &mut options, cx);
#[cfg(feature = "testing")]
record_reply_publish(out.name(), options.as_ref());
pipeline
.run(&mut out, &self.publisher, options.as_ref())
.await
}
}
impl<P, C, PL, BL> fmt::Debug for TypedPublisher<P, C, PL, BL> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypedPublisher").finish_non_exhaustive()
}
}
pub struct Transactional<P, C, PL = PublishTransformIdentity, BL = BatchTransformIdentity> {
pub(super) inner: TypedPublisher<P, C, PL, BL>,
}
impl<P, C, PL, BL> Transactional<P, C, PL, BL> {
pub(crate) const fn live(inner: TypedPublisher<P, C, PL, BL>) -> Self {
Self { inner }
}
}
impl<P, C, PL, BL> fmt::Debug for Transactional<P, C, PL, BL> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Transactional").finish_non_exhaustive()
}
}