use std::pin::Pin; use anyhow::Context; use async_trait::async_trait; use crunch_traits::{errors::TransportError, EventInfo, Transport}; use futures::{Stream, StreamExt}; use grpc::{no_data_service_client::NoDataServiceClient, PublishEventRequest, SubscribeRequest}; use tonic::transport::{Channel, ClientTlsConfig}; mod grpc; pub struct NoDataTransport { host: String, } impl NoDataTransport { pub fn new(host: impl Into) -> Self { Self { host: host.into() } } async fn client(&self) -> anyhow::Result> { let channel = if self.host.starts_with("https") { Channel::from_shared(self.host.to_owned()) .context(format!("failed to connect to: {}", &self.host))? .tls_config(ClientTlsConfig::new().with_native_roots())? .connect() .await .context(format!("failed to connect to: {}", &self.host))? } else { Channel::from_shared(self.host.to_owned()) .context(format!("failed to connect to: {}", &self.host))? .connect() .await .context(format!("failed to connect to: {}", &self.host))? }; let client = NoDataServiceClient::new(channel); Ok(client) } } #[async_trait] impl Transport for NoDataTransport { type Stream = Pin> + Send>>; async fn publish( &self, event_info: &EventInfo, content: Vec, ) -> Result<(), TransportError> { let mut client = self.client().await.map_err(TransportError::Err)?; client .publish_event(PublishEventRequest { topic: event_info.transport_name(), value: content, }) .await .context("failed to send crunch(nodata) message") .map_err(TransportError::Err)?; Ok(()) } async fn subscriber( &self, event_info: &EventInfo, ) -> Result, TransportError> { let mut client = self.client().await.map_err(TransportError::Err)?; let resp_stream = client .subscribe(SubscribeRequest { topic: event_info.transport_name(), }) .await .context("failed to establish connection to nodata") .map_err(TransportError::Err)?; let sub = resp_stream.into_inner(); let stream = futures::stream::unfold(sub, |mut sub| async move { tracing::trace!("got event from nodata"); let next = sub.next().await?; match next { Ok(next) => Some((next.value, sub)), Err(e) => { tracing::error!("failed to receive event from nodata: {e}"); None } } }); Ok(Some(Box::pin(stream))) } } trait EventInfoExt { fn transport_name(&self) -> String; } impl EventInfoExt for EventInfo { fn transport_name(&self) -> String { format!( "crunch.{}.{}.{}", self.domain, self.entity_type, self.event_name ) } }