use crate::error::Result; use crate::error_event::{ErrorEventData, ERROR_EVENT_NAME}; use crate::events::event::Event; use crate::ipc::context::Context; use crate::protocol::AsyncProtocolStream; use std::ops::DerefMut; use std::sync::Arc; use tokio::io::{AsyncWrite, AsyncWriteExt}; use tokio::sync::Mutex; /// An abstraction over any type that implements the AsyncProtocolStream trait /// to emit events and share a connection across multiple /// contexts. #[derive(Clone)] pub struct StreamEmitter { stream: Arc>, } impl StreamEmitter { pub fn new(stream: P::OwnedSplitWriteHalf) -> Self { Self { stream: Arc::new(Mutex::new(stream)), } } #[tracing::instrument(level = "trace", skip(self, data_bytes))] async fn _emit( &self, namespace: Option<&str>, event: &str, data_bytes: Vec, res_id: Option, ) -> Result { let event = if let Some(namespace) = namespace { Event::with_namespace(namespace.to_string(), event.to_string(), data_bytes, res_id) } else { Event::new(event.to_string(), data_bytes, res_id) }; let event_id = event.id(); let event_bytes = event.into_bytes()?; { let mut stream = self.stream.lock().await; stream.deref_mut().write_all(&event_bytes[..]).await?; tracing::trace!(bytes_len = event_bytes.len()); } Ok(EmitMetadata::new(event_id)) } /// Emits an event pub(crate) async fn emit>( &self, event: S, payload: Vec, ) -> Result { self._emit(None, event.as_ref(), payload, None).await } /// Emits an event to a specific namespace pub(crate) async fn emit_to, S2: AsRef>( &self, namespace: S1, event: S2, payload: Vec, ) -> Result { self._emit(Some(namespace.as_ref()), event.as_ref(), payload, None) .await } /// Emits a response to an event pub(crate) async fn emit_response>( &self, event_id: u64, event: S, payload: Vec, ) -> Result { self._emit(None, event.as_ref(), payload, Some(event_id)) .await } /// Emits a response to an event to a namespace pub(crate) async fn emit_response_to, S2: AsRef>( &self, event_id: u64, namespace: S1, event: S2, payload: Vec, ) -> Result { self._emit( Some(namespace.as_ref()), event.as_ref(), payload, Some(event_id), ) .await } } /// A metadata object returned after emitting an event. /// This object can be used to wait for a response to an event. pub struct EmitMetadata { message_id: u64, } impl EmitMetadata { pub(crate) fn new(message_id: u64) -> Self { Self { message_id } } /// The ID of the emitted message pub fn message_id(&self) -> u64 { self.message_id } /// Waits for a reply to the given message. #[tracing::instrument(skip(self, ctx), fields(self.message_id))] pub async fn await_reply(&self, ctx: &Context) -> Result { let reply = ctx.await_reply(self.message_id).await?; if reply.name() == ERROR_EVENT_NAME { Err(reply.payload::()?.into()) } else { Ok(reply) } } }