use async_trait::async_trait; use fred::clients::{Client, SubscriberClient}; use fred::interfaces::{ClientLike, EventInterface, PubsubInterface}; use fred::prelude::*; use tokio::sync::mpsc; use crate::socket::message_bus::{MessageBus, MessageBusError}; pub struct RedisMessageBus { client: Client, subscriber: SubscriberClient, } impl RedisMessageBus { pub async fn new(redis_url: &str) -> Result { let config = Config::from_url(redis_url).map_err(|e| MessageBusError::Redis(e.to_string()))?; let client = Client::new(config.clone(), None, None, None); let subscriber = SubscriberClient::new(config, None, None, None); // connect() starts the connection task; result is checked by wait_for_connect() let _ = client.connect().await; let _ = subscriber.connect().await; client .wait_for_connect() .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; subscriber .wait_for_connect() .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; Ok(Self { client, subscriber }) } pub fn client(&self) -> &Client { &self.client } } #[async_trait] impl MessageBus for RedisMessageBus { async fn publish(&self, channel: &str, message: &[u8]) -> Result<(), MessageBusError> { self.client .publish::<(), _, Vec>(channel, message.to_vec()) .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; Ok(()) } async fn subscribe(&self, channel: &str) -> Result>, MessageBusError> { let (tx, rx) = mpsc::channel::>(256); self.subscriber .subscribe(channel.to_string()) .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; let subscriber = self.subscriber.clone(); let channel_owned = channel.to_string(); let mut message_rx = subscriber.message_rx(); tokio::spawn(async move { while let Ok(message) = message_rx.recv().await { if message.channel == channel_owned { let data: Vec = FromValue::from_value(message.value).unwrap_or_default(); if tx.send(data).await.is_err() { break; } } } }); Ok(rx) } async fn unsubscribe(&self, channel: &str) -> Result<(), MessageBusError> { self.subscriber .unsubscribe(channel.to_string()) .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; Ok(()) } async fn close(&self) -> Result<(), MessageBusError> { self.client .quit() .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; self.subscriber .quit() .await .map_err(|e| MessageBusError::Redis(e.to_string()))?; Ok(()) } }