use std::sync::Arc;
use std::time::Duration;
use ruststream::{IncomingMessage, OutgoingFor, Take};
use super::bus::Bus;
use super::deliveries::Settlement;
use super::router::Delivery;
use super::{ensure_node, frame};
use crate::address::Routing;
use crate::error::AmqpError;
use crate::message::AmqpMessage;
pub(crate) async fn request(
bus: &Arc<Bus>,
msg: OutgoingFor<'_, Take>,
timeout: Duration,
) -> Result<AmqpMessage, AmqpError> {
bus.ensure_live()?;
let address = msg.name();
ensure_node(address)?;
let reply_to = bus.next_reply_address();
let correlation_id = format!("{reply_to}-corr");
let mut delivery = frame(msg);
delivery.headers.insert("reply-to", reply_to.clone());
delivery
.headers
.insert("correlation-id", correlation_id.clone());
let (id, mut replies) = bus.subscribe(reply_to, Routing::Anycast)?;
if let Err(err) = bus.route(address, &delivery) {
bus.unsubscribe(id, Vec::new());
return Err(err);
}
let reply = tokio::time::timeout(timeout, async {
loop {
let Delivery {
payload, headers, ..
} = replies.recv().await?;
let reply = AmqpMessage::settled(payload, headers, None)
.settling(Settlement::settled(Arc::clone(bus)));
if reply.headers().correlation_id() == Some(correlation_id.as_str()) {
return Some(reply);
}
}
})
.await
.ok()
.flatten();
bus.unsubscribe(id, Vec::new());
while let Ok(Delivery {
payload, headers, ..
}) = replies.try_recv()
{
drop(
AmqpMessage::settled(payload, headers, None)
.settling(Settlement::settled(Arc::clone(bus))),
);
}
reply.ok_or(AmqpError::RequestTimeout)
}