use std::fmt;
use std::ops::{Deref, DerefMut};
#[cfg(any(
all(feature = "lapin-2", feature = "lapin-3"),
all(feature = "lapin-2", feature = "lapin-4"),
all(feature = "lapin-3", feature = "lapin-4")
))]
compile_error!("Enable exactly one lapin version feature: `lapin-2`, `lapin-3`, or `lapin-4`.");
#[cfg(not(any(feature = "lapin-2", feature = "lapin-3", feature = "lapin-4")))]
compile_error!("Enable one lapin version feature: `lapin-2`, `lapin-3`, or `lapin-4`.");
#[cfg(feature = "lapin-2")]
use lapin2 as lapin;
#[cfg(feature = "lapin-3")]
use lapin3 as lapin;
#[cfg(feature = "lapin-4")]
use lapin4 as lapin;
#[cfg(feature = "lapin-2")]
type LapinConfirmation = lapin::publisher_confirm::Confirmation;
#[cfg(feature = "lapin-3")]
type LapinConfirmation = lapin::publisher_confirm::Confirmation;
#[cfg(feature = "lapin-4")]
type LapinConfirmation = lapin::Confirmation;
pub use vld;
pub struct LapinChannel {
inner: lapin::Channel,
}
impl LapinChannel {
pub fn new(inner: lapin::Channel) -> Self {
Self { inner }
}
pub fn into_inner(self) -> lapin::Channel {
self.inner
}
fn encode_payload<V>(value: &V) -> Result<Vec<u8>, VldLapinError>
where
V: serde::Serialize + vld::schema::VldParse,
{
let json =
serde_json::to_value(value).map_err(|e| VldLapinError::Serialization(e.to_string()))?;
<V as vld::schema::VldParse>::vld_parse_value(&json).map_err(VldLapinError::Validation)?;
serde_json::to_vec(&json).map_err(|e| VldLapinError::Serialization(e.to_string()))
}
pub fn decode_bytes<T>(&self, payload: &[u8]) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
let value: serde_json::Value = serde_json::from_slice(payload)
.map_err(|e| VldLapinError::Deserialization(e.to_string()))?;
<T as vld::schema::VldParse>::vld_parse_value(&value).map_err(VldLapinError::Validation)
}
#[allow(clippy::useless_conversion)]
pub async fn queue_declare(
&self,
queue: &str,
options: lapin::options::QueueDeclareOptions,
arguments: lapin::types::FieldTable,
) -> Result<lapin::Queue, VldLapinError> {
self.inner
.queue_declare(queue.into(), options, arguments)
.await
.map_err(VldLapinError::Lapin)
}
#[allow(clippy::useless_conversion)]
pub async fn basic_get(
&self,
queue: &str,
options: lapin::options::BasicGetOptions,
) -> Result<Option<lapin::message::BasicGetMessage>, VldLapinError> {
self.inner
.basic_get(queue.into(), options)
.await
.map_err(VldLapinError::Lapin)
}
#[allow(clippy::useless_conversion)]
pub async fn publish<V>(
&self,
exchange: &str,
routing_key: &str,
options: lapin::options::BasicPublishOptions,
properties: lapin::BasicProperties,
value: &V,
) -> Result<LapinConfirmation, VldLapinError>
where
V: serde::Serialize + vld::schema::VldParse,
{
let payload = Self::encode_payload(value)?;
let confirm = self
.inner
.basic_publish(
exchange.into(),
routing_key.into(),
options,
&payload,
properties,
)
.await
.map_err(VldLapinError::Lapin)?;
confirm.await.map_err(VldLapinError::Lapin)
}
pub fn decode_delivery<T>(
&self,
delivery: &lapin::message::Delivery,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
self.decode_bytes(&delivery.data)
}
pub fn decode_get<T>(
&self,
message: &lapin::message::BasicGetMessage,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
self.decode_delivery(message)
}
pub async fn ack_decode<T>(
&self,
delivery: &lapin::message::Delivery,
options: lapin::options::BasicAckOptions,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
let parsed = self.decode_delivery(delivery)?;
delivery.ack(options).await.map_err(VldLapinError::Lapin)?;
Ok(parsed)
}
pub async fn nack_decode<T>(
&self,
delivery: &lapin::message::Delivery,
options: lapin::options::BasicNackOptions,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
let parsed = self.decode_delivery(delivery)?;
delivery.nack(options).await.map_err(VldLapinError::Lapin)?;
Ok(parsed)
}
pub async fn reject_decode<T>(
&self,
delivery: &lapin::message::Delivery,
options: lapin::options::BasicRejectOptions,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
let parsed = self.decode_delivery(delivery)?;
delivery
.reject(options)
.await
.map_err(VldLapinError::Lapin)?;
Ok(parsed)
}
pub async fn ack_decode_get<T>(
&self,
message: &lapin::message::BasicGetMessage,
options: lapin::options::BasicAckOptions,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
self.ack_decode(message, options).await
}
pub async fn nack_decode_get<T>(
&self,
message: &lapin::message::BasicGetMessage,
options: lapin::options::BasicNackOptions,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
self.nack_decode(message, options).await
}
pub async fn reject_decode_get<T>(
&self,
message: &lapin::message::BasicGetMessage,
options: lapin::options::BasicRejectOptions,
) -> Result<T, VldLapinError>
where
T: vld::schema::VldParse,
{
self.reject_decode(message, options).await
}
}
impl Deref for LapinChannel {
type Target = lapin::Channel;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for LapinChannel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[derive(Debug)]
pub enum VldLapinError {
Validation(vld::error::VldError),
Serialization(String),
Deserialization(String),
Lapin(lapin::Error),
}
impl fmt::Display for VldLapinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VldLapinError::Validation(e) => write!(f, "Validation error: {e}"),
VldLapinError::Serialization(e) => write!(f, "Serialization error: {e}"),
VldLapinError::Deserialization(e) => write!(f, "Deserialization error: {e}"),
VldLapinError::Lapin(e) => write!(f, "Lapin error: {e}"),
}
}
}
impl std::error::Error for VldLapinError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
VldLapinError::Validation(e) => Some(e),
VldLapinError::Lapin(e) => Some(e),
VldLapinError::Serialization(_) | VldLapinError::Deserialization(_) => None,
}
}
}
impl From<vld::error::VldError> for VldLapinError {
fn from(value: vld::error::VldError) -> Self {
Self::Validation(value)
}
}
impl From<lapin::Error> for VldLapinError {
fn from(value: lapin::Error) -> Self {
Self::Lapin(value)
}
}
#[macro_export]
macro_rules! impl_to_lapin {
($channel:ident) => {
let $channel = $crate::LapinChannel::new($channel);
};
}
pub mod prelude {
pub use crate::{impl_to_lapin, LapinChannel, VldLapinError};
pub use vld::prelude::*;
}