mod bus;
mod deliveries;
mod request;
mod router;
#[cfg(feature = "transaction")]
mod txn;
use std::sync::Arc;
use bytes::Bytes;
use fe2o3_amqp_types::messaging::{Data, Message};
use ruststream::{OutgoingFor, OutgoingMessage, Take};
use url::Url;
pub(crate) use bus::Bus;
pub(crate) use deliveries::{BusDeliveries, Settlement};
pub(crate) use request::request;
#[cfg(feature = "transaction")]
pub(crate) use txn::TxnBuffer;
pub(crate) use self::router::Delivery;
use crate::address::AmqpAddress;
use crate::broker::is_at_most_once;
use crate::error::{AmqpError, box_err};
use crate::message::{build_message, headers_from_amqp, to_amqp_message};
pub(crate) fn check_url(url: &str) -> Result<(), AmqpError> {
let url = Url::parse(url).map_err(|e| AmqpError::Connect(box_err(e)))?;
let tls = cfg!(any(feature = "rustls", feature = "native-tls"));
match url.scheme() {
"amqp" => {}
"amqps" if tls => {}
"amqps" => {
return Err(AmqpError::Connect(Box::from(
"an amqps:// URL needs the `rustls` or `native-tls` feature",
)));
}
other => {
return Err(AmqpError::Connect(
format!("the scheme {other:?} is not an AMQP scheme").into(),
));
}
}
if url.host_str().is_none_or(str::is_empty) {
return Err(AmqpError::Connect(Box::from("the URL names no host")));
}
Ok(())
}
pub(crate) fn subscribe(bus: &Arc<Bus>, address: &AmqpAddress) -> Result<BusDeliveries, AmqpError> {
bus.ensure_live()?;
let (id, receiver) = bus.subscribe(address.address().to_owned(), address.routing())?;
Ok(BusDeliveries::new(
Arc::clone(bus),
id,
address.address().to_owned(),
receiver,
is_at_most_once(address.settle_value()),
))
}
pub(crate) fn publish(bus: &Bus, msg: OutgoingFor<'_, Take>) -> Result<(), AmqpError> {
bus.ensure_live()?;
let address = msg.name();
ensure_node(address)?;
bus.route(address, &frame(msg))
}
pub(crate) fn ensure_node(address: &str) -> Result<(), AmqpError> {
if address.is_empty() {
return Err(AmqpError::PublishNotAccepted {
address: String::new(),
outcome: "rejected: a message sent to the empty address names no node".to_owned(),
});
}
Ok(())
}
pub(crate) fn inject(bus: &Bus, msg: &OutgoingMessage<'_>) -> Result<(), AmqpError> {
bus.ensure_live()?;
ensure_node(msg.name())?;
let message = build_message(msg.headers(), msg.payload().to_vec());
bus.route(msg.name(), &delivered(message))
}
fn frame(msg: OutgoingFor<'_, Take>) -> Delivery {
delivered(to_amqp_message(msg))
}
fn delivered(message: Message<Data>) -> Delivery {
Delivery {
headers: headers_from_amqp(&message),
payload: Bytes::from(message.body.0.into_vec()),
count: None,
}
}