use lapin::{
options::*, types::FieldTable, BasicProperties,
Connection, ConnectionProperties, Consumer, Channel, Queue
};
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::error::{RpcError, RpcResult};
#[derive(Clone)]
pub struct AmqpConnection {
connection: Arc<RwLock<Option<Connection>>>,
channel: Arc<RwLock<Option<Channel>>>,
url: String,
}
impl AmqpConnection {
pub fn new(url: impl Into<String>) -> Self {
Self {
connection: Arc::new(RwLock::new(None)),
channel: Arc::new(RwLock::new(None)),
url: url.into(),
}
}
pub async fn connect(&self) -> RpcResult<()> {
let conn = Connection::connect(&self.url, ConnectionProperties::default()).await?;
let channel = conn.create_channel().await?;
*self.connection.write().await = Some(conn);
*self.channel.write().await = Some(channel);
Ok(())
}
pub async fn get_channel(&self) -> RpcResult<Channel> {
let channel_guard = self.channel.read().await;
match channel_guard.as_ref() {
Some(channel) => Ok(channel.clone()),
None => Err(RpcError::InvalidCommand { message: "No connection available".to_string() })
}
}
pub async fn declare_queue(&self, queue_name: &str, exclusive: bool) -> RpcResult<Queue> {
let channel = self.get_channel().await?;
let queue = channel
.queue_declare(
queue_name,
QueueDeclareOptions {
exclusive,
..Default::default()
},
FieldTable::default(),
)
.await?;
Ok(queue)
}
pub async fn publish(
&self,
queue_name: &str,
payload: &[u8],
correlation_id: &str,
reply_to: Option<&str>,
expiration: Option<u64>,
) -> RpcResult<()> {
let channel = self.get_channel().await?;
let mut properties = BasicProperties::default()
.with_correlation_id(correlation_id.into());
if let Some(reply_queue) = reply_to {
properties = properties.with_reply_to(reply_queue.into());
}
if let Some(exp) = expiration {
properties = properties.with_expiration(exp.to_string().into());
}
channel
.basic_publish(
"",
queue_name,
BasicPublishOptions {
mandatory: true,
..Default::default()
},
payload,
properties,
)
.await?
.await?;
Ok(())
}
pub async fn consume(&self, queue_name: &str, consumer_tag: &str) -> RpcResult<Consumer> {
let channel = self.get_channel().await?;
let consumer = channel
.basic_consume(
queue_name,
consumer_tag,
BasicConsumeOptions::default(),
FieldTable::default(),
)
.await?;
Ok(consumer)
}
pub async fn ack(&self, delivery_tag: u64) -> RpcResult<()> {
let channel = self.get_channel().await?;
channel.basic_ack(delivery_tag, BasicAckOptions::default()).await?;
Ok(())
}
pub async fn close(&self) -> RpcResult<()> {
if let Some(connection) = self.connection.write().await.take() {
connection.close(200, "Normal shutdown").await?;
}
Ok(())
}
}