use ruststream::SubscriptionSource;
use crate::broker::ConnectedAmqpBroker;
use crate::error::AmqpError;
use crate::subscriber::AmqpSubscriber;
pub const DEFAULT_CREDIT: u32 = 256;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Settle {
#[default]
AtLeastOnce,
AtMostOnce,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Kind {
Queue,
Topic,
Raw,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use]
pub struct AmqpAddress {
address: String,
kind: Kind,
credit: u32,
settle: Settle,
}
impl AmqpAddress {
fn of(address: String, kind: Kind) -> Self {
Self {
address,
kind,
credit: DEFAULT_CREDIT,
settle: Settle::default(),
}
}
pub fn queue(name: impl Into<String>) -> Self {
Self::of(name.into(), Kind::Queue)
}
pub fn topic(name: impl Into<String>) -> Self {
Self::of(name.into(), Kind::Topic)
}
pub fn raw(address: impl Into<String>) -> Self {
Self::of(address.into(), Kind::Raw)
}
pub fn credit(mut self, credit: u32) -> Self {
self.credit = credit;
self
}
pub fn settle(mut self, settle: Settle) -> Self {
self.settle = settle;
self
}
#[must_use]
pub fn address(&self) -> &str {
&self.address
}
pub(crate) fn credit_value(&self) -> u32 {
self.credit
}
pub(crate) fn settle_value(&self) -> Settle {
self.settle
}
pub(crate) fn capability(&self) -> Option<&'static str> {
match self.kind {
Kind::Queue => Some("queue"),
Kind::Topic => Some("topic"),
Kind::Raw => None,
}
}
pub(crate) fn validate(&self) -> Result<(), AmqpError> {
if self.address.is_empty() {
return Err(AmqpError::InvalidAddress(
"address must be non-empty".into(),
));
}
if self.credit == 0 {
return Err(AmqpError::InvalidAddress(
"credit must be at least 1".into(),
));
}
Ok(())
}
}
impl SubscriptionSource<ConnectedAmqpBroker> for AmqpAddress {
type Subscriber = AmqpSubscriber;
fn name(&self) -> &str {
self.address()
}
async fn subscribe(self, connected: &ConnectedAmqpBroker) -> Result<AmqpSubscriber, AmqpError> {
connected.subscribe_address(self).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_address_is_rejected_before_io() {
assert!(matches!(
AmqpAddress::queue("").validate(),
Err(AmqpError::InvalidAddress(_))
));
}
#[test]
fn zero_credit_is_rejected_before_io() {
assert!(matches!(
AmqpAddress::queue("orders").credit(0).validate(),
Err(AmqpError::InvalidAddress(_))
));
}
#[test]
fn constructors_pick_the_matching_capability() {
assert_eq!(AmqpAddress::queue("q").capability(), Some("queue"));
assert_eq!(AmqpAddress::topic("t").capability(), Some("topic"));
assert_eq!(AmqpAddress::raw("/queues/q").capability(), None);
}
}