use async_trait::async_trait;
use super::{SubscriptionError, types::SubscriptionEvent};
#[async_trait]
pub trait TransportAdapter: Send + Sync {
async fn deliver(
&self,
event: &SubscriptionEvent,
subscription_name: &str,
) -> Result<(), SubscriptionError>;
fn name(&self) -> &'static str;
async fn health_check(&self) -> bool;
}
pub type BoxDynTransportAdapter = Box<dyn TransportAdapter>;
pub struct TransportManager {
adapters: Vec<BoxDynTransportAdapter>,
}
impl TransportManager {
#[must_use]
pub fn new() -> Self {
Self {
adapters: Vec::new(),
}
}
pub fn add_adapter(&mut self, adapter: BoxDynTransportAdapter) {
tracing::info!(adapter = adapter.name(), "Added transport adapter");
self.adapters.push(adapter);
}
#[must_use]
pub fn adapter_count(&self) -> usize {
self.adapters.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.adapters.is_empty()
}
pub async fn deliver_all(
&self,
event: &SubscriptionEvent,
subscription_name: &str,
) -> Result<DeliveryResult, SubscriptionError> {
if self.adapters.is_empty() {
return Ok(DeliveryResult {
successful: 0,
failed: 0,
errors: Vec::new(),
});
}
let futures: Vec<_> = self
.adapters
.iter()
.map(|adapter| {
let name = adapter.name().to_string();
async move {
let result = adapter.deliver(event, subscription_name).await;
(name, result)
}
})
.collect();
let results = futures::future::join_all(futures).await;
let mut successful = 0;
let mut failed = 0;
let mut errors = Vec::new();
for (name, result) in results {
match result {
Ok(()) => successful += 1,
Err(e) => {
failed += 1;
errors.push((name, e.to_string()));
},
}
}
Ok(DeliveryResult {
successful,
failed,
errors,
})
}
pub async fn health_check_all(&self) -> Vec<(String, bool)> {
let futures: Vec<_> = self
.adapters
.iter()
.map(|adapter| {
let name = adapter.name().to_string();
async move {
let healthy = adapter.health_check().await;
(name, healthy)
}
})
.collect();
futures::future::join_all(futures).await
}
}
impl Default for TransportManager {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for TransportManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransportManager")
.field("adapter_count", &self.adapters.len())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct DeliveryResult {
pub successful: usize,
pub failed: usize,
pub errors: Vec<(String, String)>,
}
impl DeliveryResult {
#[must_use]
pub const fn all_succeeded(&self) -> bool {
self.failed == 0
}
#[must_use]
pub const fn any_succeeded(&self) -> bool {
self.successful > 0
}
}