use std::{fmt, future::Future, sync::Arc};
use super::{Outgoing, PublishFut};
use crate::runtime::lifecycle::BoxError;
#[cfg(feature = "testing")]
use crate::testing::coordinator::PipelinePublish;
use crate::{PayloadForm, Publisher};
pub trait PublishPipeline: Send + Sync {
fn run<'a, P: Publisher>(
&'a self,
out: &'a mut Outgoing<'a>,
send: &'a P,
options: Option<&'a P::Options>,
) -> impl Future<Output = Result<(), BoxError>> + Send + 'a;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct PublishIdentity;
impl PublishPipeline for PublishIdentity {
async fn run<'a, P: Publisher>(
&'a self,
out: &'a mut Outgoing<'a>,
send: &'a P,
options: Option<&'a P::Options>,
) -> Result<(), BoxError> {
let msg = <P::Payload as PayloadForm>::leaving(out);
#[cfg(feature = "testing")]
let recorded = PipelinePublish::capture(&msg);
let result = send
.publish(msg, options)
.await
.map_err(|e| Box::new(e) as BoxError);
#[cfg(feature = "testing")]
if result.is_ok() {
recorded.sent();
}
result
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct PublishStack<Head, Tail> {
head: Head,
tail: Tail,
}
impl<Head, Tail> PublishStack<Head, Tail> {
pub(crate) const fn new(head: Head, tail: Tail) -> Self {
Self { head, tail }
}
}
impl<Head: PublishLayer, Tail: PublishPipeline> PublishPipeline for PublishStack<Head, Tail> {
fn run<'a, P: Publisher>(
&'a self,
out: &'a mut Outgoing<'a>,
send: &'a P,
options: Option<&'a P::Options>,
) -> impl Future<Output = Result<(), BoxError>> + Send + 'a {
self.head.on_publish(
out,
PublishNext {
tail: &self.tail,
send,
options,
},
)
}
}
pub trait PublishLayer: Send + Sync {
fn on_publish<'a, N: PublishPipeline, P: Publisher>(
&'a self,
out: &'a mut Outgoing<'a>,
next: PublishNext<'a, N, P>,
) -> impl Future<Output = Result<(), BoxError>> + Send + 'a;
}
pub struct PublishNext<'a, N, P: Publisher> {
tail: &'a N,
send: &'a P,
options: Option<&'a P::Options>,
}
impl<'a, N: PublishPipeline, P: Publisher> PublishNext<'a, N, P> {
pub fn run(
self,
out: &'a mut Outgoing<'a>,
) -> impl Future<Output = Result<(), BoxError>> + Send + 'a {
self.tail.run(out, self.send, self.options)
}
}
impl<N, P: Publisher> fmt::Debug for PublishNext<'_, N, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PublishNext").finish_non_exhaustive()
}
}
pub trait PublishDynLayer: Send + Sync {
fn on_publish<'a>(
&'a self,
out: &'a mut Outgoing<'a>,
next: PublishDynNext<'a>,
) -> PublishFut<'a>;
}
pub struct PublishDynNext<'a> {
rest: &'a [Arc<dyn PublishDynLayer>],
tail: Box<dyn FnOnce(&'a mut Outgoing<'a>) -> PublishFut<'a> + Send + 'a>,
}
impl<'a> PublishDynNext<'a> {
pub fn run(self, out: &'a mut Outgoing<'a>) -> PublishFut<'a> {
match self.rest.split_first() {
Some((middleware, rest)) => middleware.on_publish(
out,
PublishDynNext {
rest,
tail: self.tail,
},
),
None => (self.tail)(out),
}
}
}
impl fmt::Debug for PublishDynNext<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PublishDynNext")
.field("remaining", &self.rest.len())
.finish_non_exhaustive()
}
}
pub struct PublishDynStack(Arc<[Arc<dyn PublishDynLayer>]>);
impl Clone for PublishDynStack {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl PublishDynStack {
#[must_use]
pub fn new(middleware: impl IntoIterator<Item = Arc<dyn PublishDynLayer>>) -> Self {
Self(middleware.into_iter().collect())
}
}
impl fmt::Debug for PublishDynStack {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PublishDynStack")
.field("middleware", &self.0.len())
.finish_non_exhaustive()
}
}
impl PublishLayer for PublishDynStack {
fn on_publish<'a, N: PublishPipeline, P: Publisher>(
&'a self,
out: &'a mut Outgoing<'a>,
next: PublishNext<'a, N, P>,
) -> impl Future<Output = Result<(), BoxError>> + Send + 'a {
PublishDynNext {
rest: &self.0,
tail: Box::new(move |out| Box::pin(next.run(out)) as PublishFut<'a>),
}
.run(out)
}
}