use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use async_nats::Client;
use bytes::Bytes;
use ruststream::{OutgoingMessage, PairError, PublishPolicy, Publisher};
use crate::broker::{ConnectedNatsBroker, NatsConnection};
use crate::{convert::headers_to_nats, error::NatsError};
use self::sealed::Sealed;
mod sealed {
pub trait Sealed {}
impl Sealed for super::NatsPublish {}
impl Sealed for crate::jetstream::JetStreamPublish {}
}
pub trait NatsPublishPolicy: PublishPolicy<ConnectedNatsBroker> + Sealed {
#[must_use]
fn bind(self, connected: &ConnectedNatsBroker) -> Self::Live;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[must_use]
pub struct NatsPublish;
impl PublishPolicy<ConnectedNatsBroker> for NatsPublish {
type Live = NatsPublisher;
async fn pair(self, connected: &ConnectedNatsBroker) -> Result<Self::Live, PairError> {
Ok(self.bind(connected))
}
}
impl NatsPublishPolicy for NatsPublish {
fn bind(self, connected: &ConnectedNatsBroker) -> Self::Live {
NatsPublisher::new(Arc::clone(connected.connection()))
}
}
#[derive(Clone)]
pub struct NatsPublisher {
connection: Arc<NatsConnection>,
}
impl Debug for NatsPublisher {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NatsPublisher").finish_non_exhaustive()
}
}
impl NatsPublisher {
pub(crate) const fn new(connection: Arc<NatsConnection>) -> Self {
Self { connection }
}
pub(crate) fn client_for(&self, subject: &str) -> Result<Client, NatsError> {
self.connection.live_client(subject).cloned()
}
}
impl Publisher for NatsPublisher {
type Error = NatsError;
async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
let client = self.client_for(msg.name())?;
let subject = msg.name().to_owned();
let payload = Bytes::copy_from_slice(msg.payload());
let result = match headers_to_nats(msg.headers()) {
Some(headers) => client.publish_with_headers(subject, headers, payload).await,
None => client.publish(subject, payload).await,
};
result.map_err(|err| NatsError::Publish(Box::new(err)))
}
}