use std::num::NonZeroU16;
use std::time::Duration;
use lapin::types::{AMQPValue, FieldTable, ShortString};
use ruststream::RetryDeclaration;
use crate::delay::Delay;
use crate::exchange::RabbitExchange;
const DEFAULT_BATCH_WAIT: Duration = Duration::from_millis(50);
pub(crate) const QUEUE_TYPE: &str = "x-queue-type";
pub(crate) const DEAD_LETTER_EXCHANGE: &str = "x-dead-letter-exchange";
pub(crate) const DEAD_LETTER_ROUTING_KEY: &str = "x-dead-letter-routing-key";
pub(crate) const DELIVERY_LIMIT: &str = "x-delivery-limit";
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Binding {
pub(crate) exchange: RabbitExchange,
pub(crate) routing_key: String,
pub(crate) arguments: FieldTable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum QueueKind {
Classic,
Quorum,
}
impl QueueKind {
pub(crate) const fn as_str(self) -> &'static str {
match self {
Self::Classic => "classic",
Self::Quorum => "quorum",
}
}
}
#[doc(hidden)]
#[derive(Debug, Clone, PartialEq)]
pub struct QueueSpec {
pub(crate) kind: QueueKind,
pub(crate) name: String,
pub(crate) durable: bool,
pub(crate) exclusive: bool,
pub(crate) auto_delete: bool,
pub(crate) bindings: Vec<Binding>,
pub(crate) arguments: FieldTable,
pub(crate) prefetch: Option<NonZeroU16>,
pub(crate) batch_wait: Duration,
pub(crate) delay: Option<Delay>,
}
impl QueueSpec {
pub(crate) fn new(kind: QueueKind, name: String) -> Self {
Self {
kind,
name,
durable: true,
exclusive: false,
auto_delete: false,
bindings: Vec::new(),
arguments: FieldTable::default(),
prefetch: None,
batch_wait: DEFAULT_BATCH_WAIT,
delay: None,
}
}
pub(crate) fn prefetch_or(&self, broker_default: Option<NonZeroU16>) -> Option<NonZeroU16> {
self.prefetch.or(broker_default)
}
pub(crate) fn set(&mut self, name: impl Into<String>, value: AMQPValue) {
self.arguments.insert(ShortString::from(name.into()), value);
}
}
pub(crate) fn declared_arguments(spec: &QueueSpec, retry: Option<&RetryDeclaration>) -> FieldTable {
let mut arguments = spec.arguments.clone();
arguments.insert(
ShortString::from(QUEUE_TYPE),
AMQPValue::LongString(spec.kind.as_str().into()),
);
if let Some(declared) = retry {
write_retry(&mut arguments, declared);
}
arguments
}
fn write_retry(arguments: &mut FieldTable, declared: &RetryDeclaration) {
let (Some(max_attempts), Some(dead_letter)) = (declared.max_attempts(), declared.dead_letter())
else {
return;
};
arguments.insert(
ShortString::from(DELIVERY_LIMIT),
AMQPValue::LongLongInt(i64::from(max_attempts.get()) - 1),
);
if !arguments
.inner()
.contains_key(&ShortString::from(DEAD_LETTER_EXCHANGE))
{
arguments.insert(
ShortString::from(DEAD_LETTER_EXCHANGE),
AMQPValue::LongString(String::new().into()),
);
}
arguments.insert(
ShortString::from(DEAD_LETTER_ROUTING_KEY),
AMQPValue::LongString(dead_letter.to_owned().into()),
);
}
macro_rules! queue_settings {
($queue:ident) => {
impl $queue {
#[must_use]
pub fn bind(
mut self,
exchange: RabbitExchange,
routing_key: impl Into<String>,
) -> Self {
self.spec.bindings.push(Binding {
exchange,
routing_key: routing_key.into(),
arguments: FieldTable::default(),
});
self
}
#[must_use]
pub fn bind_with(
mut self,
exchange: RabbitExchange,
routing_key: impl Into<String>,
arguments: FieldTable,
) -> Self {
self.spec.bindings.push(Binding {
exchange,
routing_key: routing_key.into(),
arguments,
});
self
}
#[must_use]
pub fn dead_letter_exchange(mut self, exchange: impl Into<String>) -> Self {
self.spec.set(
DEAD_LETTER_EXCHANGE,
AMQPValue::LongString(exchange.into().into()),
);
self
}
#[must_use]
pub fn dead_letter_routing_key(mut self, routing_key: impl Into<String>) -> Self {
self.spec.set(
DEAD_LETTER_ROUTING_KEY,
AMQPValue::LongString(routing_key.into().into()),
);
self
}
#[must_use]
pub fn argument(mut self, name: impl Into<String>, value: AMQPValue) -> Self {
self.spec.set(name, value);
self
}
#[must_use]
pub fn arguments(mut self, arguments: FieldTable) -> Self {
self.spec.arguments = arguments;
self
}
#[must_use]
pub fn prefetch(mut self, prefetch: NonZeroU16) -> Self {
self.spec.prefetch = Some(prefetch);
self
}
#[must_use]
pub fn batch_wait(mut self, batch_wait: Duration) -> Self {
self.spec.batch_wait = batch_wait;
self
}
#[must_use]
pub fn delay(mut self, delay: Delay) -> Self {
self.spec.delay = Some(delay);
self
}
#[must_use]
pub fn name(&self) -> &str {
&self.spec.name
}
}
};
}
pub(crate) use queue_settings;