use std::future::Future;
use serde::Serialize;
use tracing::warn;
use super::{
BatchPublishTransform, PublishContext, PublishPipeline, Transactional, TypedPublisher,
};
use crate::codec::Codec;
use crate::runtime::lifecycle::BoxError;
use crate::runtime::publish::sealed::Sealed;
use crate::{PayloadForm, Publisher, TransactionalPublisher};
pub trait ReplyPublisher<Cx = ()>: Sealed + Send + Sync {
type Payload: PayloadForm;
type Codec: Codec;
#[doc(hidden)]
fn reply_codec(&self) -> &Self::Codec;
#[doc(hidden)]
fn publish_batch<'a, T, PP>(
&'a self,
name: &'a str,
replies: &'a [T],
pipeline: &'a PP,
cx: &'a PublishContext<'a, Cx>,
encode: <Self::Payload as PayloadForm>::Encode<'a>,
) -> impl Future<Output = Result<(), BoxError>> + Send
where
T: Serialize + Sync,
PP: PublishPipeline;
}
impl<P, C, PL, BL, Cx> ReplyPublisher<Cx> for TypedPublisher<P, C, PL, BL>
where
P: Publisher,
C: Codec,
PL: Send + Sync,
BL: BatchPublishTransform<Cx, P::Options>,
Cx: Sync,
{
type Payload = P::Payload;
type Codec = C;
fn reply_codec(&self) -> &C {
self.codec()
}
async fn publish_batch<'a, T, PP>(
&'a self,
name: &'a str,
replies: &'a [T],
pipeline: &'a PP,
cx: &'a PublishContext<'a, Cx>,
mut encode: <P::Payload as PayloadForm>::Encode<'a>,
) -> Result<(), BoxError>
where
T: Serialize + Sync,
PP: PublishPipeline,
{
for reply in replies {
let slot = <P::Payload as PayloadForm>::encode_again(&mut encode);
self.publish_batched(name, reply, pipeline, cx, slot)
.await?;
}
Ok(())
}
}
impl<P, C, PL, BL, Cx> ReplyPublisher<Cx> for Transactional<P, C, PL, BL>
where
P: TransactionalPublisher,
C: Codec,
PL: Send + Sync,
BL: BatchPublishTransform<Cx, P::Options>,
Cx: Sync,
{
type Payload = P::Payload;
type Codec = C;
fn reply_codec(&self) -> &C {
self.inner.codec()
}
async fn publish_batch<'a, T, PP>(
&'a self,
name: &'a str,
replies: &'a [T],
pipeline: &'a PP,
cx: &'a PublishContext<'a, Cx>,
mut encode: <P::Payload as PayloadForm>::Encode<'a>,
) -> Result<(), BoxError>
where
T: Serialize + Sync,
PP: PublishPipeline,
{
let publisher = &self.inner.publisher;
publisher
.begin_transaction()
.await
.map_err(|e| Box::new(e) as BoxError)?;
for reply in replies {
let slot = <P::Payload as PayloadForm>::encode_again(&mut encode);
if let Err(err) = self
.inner
.publish_batched(name, reply, pipeline, cx, slot)
.await
{
abort_quietly(publisher).await;
return Err(err);
}
}
publisher
.commit()
.await
.map_err(|err| Box::new(err) as BoxError)
}
}
async fn abort_quietly<P: TransactionalPublisher>(publisher: &P) {
if let Err(err) = publisher.abort().await {
warn!(target: "ruststream::dispatch", error = %err, "transaction abort failed");
}
}