use google_cloud_pubsub::client::Publisher as GcpPublisher;
use ruststream::{OutgoingMessage, PairError, PublishPolicy, Publisher};
use crate::broker::{ConnectedPubSubBroker, Core, CoreCell};
use crate::error::{PubSubError, box_err};
use crate::message::to_gcp_message;
#[derive(Clone)]
pub struct PubSubPublisher {
cell: CoreCell,
}
impl std::fmt::Debug for PubSubPublisher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PubSubPublisher").finish_non_exhaustive()
}
}
impl PubSubPublisher {
pub(crate) fn new(cell: CoreCell) -> Self {
Self { cell }
}
fn core(&self) -> Result<&Core, PubSubError> {
let core = self.cell.get().ok_or(PubSubError::NotConnected)?;
core.ensure_open()?;
Ok(core)
}
async fn publisher_for(&self, core: &Core, topic: &str) -> GcpPublisher {
let name = core.topic_name(topic);
let mut publishers = core.publishers.lock().await;
if let Some(publisher) = publishers.get(&name) {
return publisher.clone();
}
let publisher = core.base_publisher.publisher(name.clone()).build();
publishers.insert(name, publisher.clone());
publisher
}
}
impl Publisher for PubSubPublisher {
type Error = PubSubError;
async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
let core = self.core()?;
let publisher = self.publisher_for(core, msg.name()).await;
let (message, ordering_key) = to_gcp_message(&msg);
match publisher.publish(message).await {
Ok(_message_id) => Ok(()),
Err(err) => {
if !ordering_key.is_empty() {
publisher.resume_publish(ordering_key);
}
Err(PubSubError::Publish {
topic: core.topic_name(msg.name()),
source: box_err(err),
})
}
}
}
}
#[derive(Debug, Clone, Copy, Default)]
#[must_use]
pub struct PubSubPublish;
impl PublishPolicy<ConnectedPubSubBroker> for PubSubPublish {
type Live = PubSubPublisher;
async fn pair(self, connected: &ConnectedPubSubBroker) -> Result<Self::Live, PairError> {
Ok(connected.publisher())
}
}