use std::error::Error as StdError;
use std::future::Future;
use thiserror::Error;
use super::{
DestinationUse, ForSlot, Names, PublishIdentity, PublishLayer, PublishPipeline, PublishStack,
PublishTransform, PublishTransformIdentity, PublishTransformStack, Reads, SlotContext,
};
#[cfg(feature = "asyncapi")]
use crate::asyncapi::Bindings;
use crate::runtime::lifecycle::BoxError;
#[cfg(feature = "testing")]
use crate::testing::coordinator::PipelinePublish;
use crate::{
ConnectedBroker, HeaderMap, OutgoingFor, PairError, PayloadForm, PublishPolicy, Publisher,
};
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct SlotTransforms<Stack, Pipeline> {
slot: &'static str,
stack: Stack,
pipeline: Pipeline,
}
#[doc(hidden)]
pub trait LowerOutTransforms<Pipeline> {
type Out;
fn lower(self, slot: &'static str, pipeline: Pipeline) -> Self::Out;
}
impl<Pipeline> LowerOutTransforms<Pipeline> for PublishTransformIdentity {
type Out = Pipeline;
fn lower(self, _slot: &'static str, pipeline: Pipeline) -> Pipeline {
pipeline
}
}
impl<Pipeline, Inner, Outer> LowerOutTransforms<Pipeline> for PublishTransformStack<Inner, Outer> {
type Out = SlotTransforms<Self, Pipeline>;
fn lower(self, slot: &'static str, pipeline: Pipeline) -> Self::Out {
SlotTransforms {
slot,
stack: self,
pipeline,
}
}
}
#[doc(hidden)]
pub trait SlotStackUse<Live> {
type Destination: DestinationUse;
}
impl<Live> SlotStackUse<Live> for PublishTransformIdentity {
type Destination = Reads;
}
impl<Live: Publisher, Inner, Outer> SlotStackUse<Live> for PublishTransformStack<Inner, Outer>
where
Self: PublishTransform<ForSlot, Live::Options>,
{
type Destination = <Self as PublishTransform<ForSlot, Live::Options>>::Destination;
}
#[doc(hidden)]
pub trait NarrowToUse<Policy> {
type Out;
fn narrow(policy: Policy) -> Self::Out;
}
impl<Policy> NarrowToUse<Policy> for Reads {
type Out = Policy;
fn narrow(policy: Policy) -> Policy {
policy
}
}
impl<Policy> NarrowToUse<Policy> for Names {
type Out = SendOnlyPolicy<Policy>;
fn narrow(policy: Policy) -> Self::Out {
SendOnlyPolicy(policy)
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct SendOnlyPolicy<Policy>(Policy);
impl<CB: ConnectedBroker, Policy: PublishPolicy<CB> + Send> PublishPolicy<CB>
for SendOnlyPolicy<Policy>
{
type Live = NamedDestinationSend<Policy::Live>;
async fn pair(self, connected: &CB) -> Result<Self::Live, PairError> {
Ok(NamedDestinationSend(self.0.pair(connected).await?))
}
#[cfg(feature = "asyncapi")]
fn channel_bindings(&self, channel: &str) -> Bindings {
self.0.channel_bindings(channel)
}
#[cfg(feature = "asyncapi")]
fn operation_bindings(&self, channel: &str) -> Bindings {
self.0.operation_bindings(channel)
}
#[cfg(feature = "asyncapi")]
fn message_bindings(&self, channel: &str) -> Bindings {
self.0.message_bindings(channel)
}
#[cfg(feature = "asyncapi")]
fn reply_address_location(&self) -> Option<&'static str> {
self.0.reply_address_location()
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct NamedDestinationSend<P>(P);
impl<P: Publisher> Publisher for NamedDestinationSend<P> {
type Payload = P::Payload;
type Error = P::Error;
type Options = P::Options;
async fn publish(
&self,
msg: OutgoingFor<'_, Self::Payload>,
options: Option<&Self::Options>,
) -> Result<(), Self::Error> {
self.0.publish(msg, options).await
}
fn base_headers(&self) -> Option<&HeaderMap> {
self.0.base_headers()
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not the publish path of a slot over `{W}`",
note = "a slot publishes through the app's own pipeline - `PublishIdentity` when the app adds \
no `publish_layer`, and the `PublishStack` those calls build otherwise - with the \
`SlotTransforms` its `.transform(..)` steps composed above it. A transform there \
writes the wired publisher's own `Publisher::Options`, so a stack written for another \
broker's options is not this slot's path"
)]
pub trait OutPipeline<W>: Send + Sync {
type Error<E: StdError + Send + Sync + 'static>: StdError + Send + Sync + 'static;
fn from_publish_error<E: StdError + Send + Sync + 'static>(err: E) -> Self::Error<E>;
fn send<P>(
&self,
leaf: &P,
msg: OutgoingFor<'_, W::Payload>,
options: Option<&P::Options>,
) -> impl Future<Output = Result<(), Self::Error<P::Error>>> + Send
where
W: Publisher,
P: Publisher<Options = W::Options, Payload = W::Payload>;
}
impl<W> OutPipeline<W> for PublishIdentity {
type Error<E: StdError + Send + Sync + 'static> = E;
fn from_publish_error<E: StdError + Send + Sync + 'static>(err: E) -> E {
err
}
async fn send<P>(
&self,
leaf: &P,
msg: OutgoingFor<'_, W::Payload>,
options: Option<&P::Options>,
) -> Result<(), P::Error>
where
W: Publisher,
P: Publisher<Options = W::Options, Payload = W::Payload>,
{
#[cfg(feature = "testing")]
let recorded = PipelinePublish::capture(&msg);
let sent = leaf.publish(msg, options).await;
#[cfg(feature = "testing")]
if sent.is_ok() {
recorded.sent();
}
sent
}
}
impl<W, Head: PublishLayer, Tail: PublishPipeline> OutPipeline<W> for PublishStack<Head, Tail> {
type Error<E: StdError + Send + Sync + 'static> = PipelinePublishError;
fn from_publish_error<E: StdError + Send + Sync + 'static>(err: E) -> PipelinePublishError {
PipelinePublishError(Box::new(err))
}
async fn send<P>(
&self,
leaf: &P,
msg: OutgoingFor<'_, W::Payload>,
options: Option<&P::Options>,
) -> Result<(), PipelinePublishError>
where
W: Publisher,
P: Publisher<Options = W::Options, Payload = W::Payload>,
{
let (name, payload, headers) = msg.into_parts();
let mut out = <W::Payload as PayloadForm>::rebuilt(name.into(), payload, headers);
self.run(&mut out, leaf, options)
.await
.map_err(PipelinePublishError)
}
}
impl<W, Stack, Pipeline> OutPipeline<W> for SlotTransforms<Stack, Pipeline>
where
W: Publisher,
Stack: PublishTransform<ForSlot, W::Options>,
Pipeline: OutPipeline<W>,
{
type Error<E: StdError + Send + Sync + 'static> = Pipeline::Error<E>;
fn from_publish_error<E: StdError + Send + Sync + 'static>(err: E) -> Self::Error<E> {
Pipeline::from_publish_error(err)
}
async fn send<P>(
&self,
leaf: &P,
msg: OutgoingFor<'_, W::Payload>,
options: Option<&P::Options>,
) -> Result<(), Self::Error<P::Error>>
where
W: Publisher,
P: Publisher<Options = W::Options, Payload = W::Payload>,
{
let (name, payload, headers) = msg.into_parts();
let mut out = <W::Payload as PayloadForm>::rebuilt(name.into(), payload, headers);
let mut resolved = options.cloned();
self.stack
.apply(&mut out, &mut resolved, &SlotContext::new(self.slot));
let mut kept = <W::Payload as PayloadForm>::Spent::default();
let sent = <W::Payload as PayloadForm>::handed_on(out, &mut kept);
self.pipeline.send(leaf, sent, resolved.as_ref()).await
}
}
#[derive(Debug, Error)]
#[error("failed to publish through the publish pipeline")]
pub struct PipelinePublishError(#[source] BoxError);
impl PipelinePublishError {
#[must_use]
pub fn into_source(self) -> BoxError {
self.0
}
}