use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use serde_json::Value;
use proxima_amqp::{AmqpClientConfig, AmqpClientUpstream};
use proxima_primitives::pipe::handler::{PipeHandle, into_handle};
use proxima_primitives::pipe::pipe_factory::PipeFactory;
use crate::PrimeTcpUpstream;
use crate::client::handle::ClientProtocol;
use crate::error::ProximaError;
#[derive(Debug, Default)]
pub struct AmqpPipeFactory;
impl AmqpPipeFactory {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl PipeFactory for AmqpPipeFactory {
fn name(&self) -> &str {
"amqp"
}
fn build(
&self,
spec: &Value,
_inner: Option<PipeHandle>,
) -> Pin<Box<dyn Future<Output = Result<PipeHandle, ProximaError>> + Send + '_>> {
let spec = spec.clone();
Box::pin(async move {
let config = config_from_spec(&spec)?;
let upstream = PrimeTcpUpstream::with_host(config.host.clone(), config.port);
Ok(into_handle(AmqpClientUpstream::new(upstream, config)))
})
}
}
fn config_from_spec(spec: &Value) -> Result<AmqpClientConfig, ProximaError> {
if let Some(dsn) = spec.get("dsn").and_then(Value::as_str) {
return AmqpClientConfig::from_dsn(dsn)
.map_err(|err| ProximaError::Config(format!("amqp dsn: {err}")));
}
serde_json::from_value(spec.clone())
.map_err(|err| ProximaError::Config(format!("amqp config: {err}")))
}
pub struct AmqpClientProtocol {
dsn: String,
}
impl AmqpClientProtocol {
#[must_use]
pub fn dsn(dsn: impl Into<String>) -> Self {
Self { dsn: dsn.into() }
}
}
impl ClientProtocol for AmqpClientProtocol {
fn spec(&self) -> Value {
serde_json::json!({"type": "amqp", "dsn": self.dsn})
}
fn factory(&self) -> Arc<dyn PipeFactory> {
Arc::new(AmqpPipeFactory::new())
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn config_from_dsn_spec() {
let spec = serde_json::json!({ "type": "amqp", "dsn": "amqp://broker:5673" });
let config = config_from_spec(&spec).expect("config");
assert_eq!((config.host.as_str(), config.port), ("broker", 5673));
}
#[test]
fn factory_name_is_the_spec_key() {
assert_eq!(AmqpPipeFactory::new().name(), "amqp");
}
#[test]
fn client_protocol_lowers_to_the_type_and_dsn_spec() {
let protocol = AmqpClientProtocol::dsn("amqp://broker:5672");
let spec = protocol.spec();
assert_eq!(spec["type"], "amqp");
assert_eq!(spec["dsn"], "amqp://broker:5672");
assert_eq!(protocol.factory().name(), "amqp");
}
}