use std::sync::{Arc, Mutex};
use bytes::Bytes;
use ruststream::{Headers, OutgoingMessage, Publisher, TransactionalPublisher};
use super::broker::TestBrokerState;
use crate::error::AmqpError;
type Buffered = (String, Bytes, Headers);
#[derive(Debug, Clone)]
pub struct LapinTestPublisher {
state: Arc<TestBrokerState>,
txn: Arc<Mutex<Option<Vec<Buffered>>>>,
}
impl LapinTestPublisher {
pub(crate) fn new(state: Arc<TestBrokerState>) -> Self {
Self {
state,
txn: Arc::new(Mutex::new(None)),
}
}
fn route(&self, queue: &str, payload: &Bytes, headers: &Headers) {
self.state
.router
.publish(queue, payload, headers, self.state.coordinator().as_ref());
}
}
impl Publisher for LapinTestPublisher {
type Error = AmqpError;
async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
if msg.name().is_empty() {
return Err(AmqpError::InvalidOptions(
"routing key must not be empty; on the default exchange it names the target queue"
.to_owned(),
));
}
{
let mut txn = self.txn.lock().expect("transaction buffer mutex poisoned");
if let Some(buffer) = txn.as_mut() {
buffer.push((
msg.name().to_owned(),
Bytes::copy_from_slice(msg.payload()),
msg.headers().clone(),
));
return Ok(());
}
}
self.route(
msg.name(),
&Bytes::copy_from_slice(msg.payload()),
msg.headers(),
);
Ok(())
}
}
impl TransactionalPublisher for LapinTestPublisher {
async fn begin_transaction(&self) -> Result<(), Self::Error> {
self.txn
.lock()
.expect("transaction buffer mutex poisoned")
.get_or_insert_with(Vec::new);
Ok(())
}
async fn commit(&self) -> Result<(), Self::Error> {
let buffered = {
let mut txn = self.txn.lock().expect("transaction buffer mutex poisoned");
txn.take()
};
if let Some(buffered) = buffered {
for (queue, payload, headers) in buffered {
self.route(&queue, &payload, &headers);
}
}
Ok(())
}
async fn abort(&self) -> Result<(), Self::Error> {
self.txn
.lock()
.expect("transaction buffer mutex poisoned")
.take();
Ok(())
}
}