use crate::context::{Context, Receiver};
use crate::error::{Mecha10Error, Result};
use crate::messages::Message;
use crate::prelude::now_micros;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::future::Future;
use std::marker::PhantomData;
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::{debug, error, info, warn};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamMessage<T> {
pub stream_id: String,
pub sequence: u64,
pub payload: StreamPayload<T>,
pub timestamp: u64,
}
impl<T: Message> Message for StreamMessage<T> {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StreamPayload<T> {
Data(T),
Start,
End,
Error(StreamError),
Cancel,
Heartbeat,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamError {
pub message: String,
pub code: Option<String>,
}
pub struct StreamReceiver<T> {
stream_id: String,
receiver: Receiver<StreamMessage<T>>,
sequence: u64,
ended: bool,
}
impl<T: Message + DeserializeOwned + Send + 'static> StreamReceiver<T> {
pub(crate) fn new(stream_id: String, receiver: Receiver<StreamMessage<T>>) -> Self {
Self {
stream_id,
receiver,
sequence: 0,
ended: false,
}
}
pub async fn recv(&mut self) -> Option<T> {
if self.ended {
return None;
}
loop {
let msg = self.receiver.recv().await?;
if msg.stream_id != self.stream_id {
continue;
}
if msg.sequence < self.sequence {
warn!(
"Out of order message in stream {} (expected {}, got {})",
self.stream_id, self.sequence, msg.sequence
);
}
self.sequence = msg.sequence + 1;
match msg.payload {
StreamPayload::Data(data) => {
debug!("Stream {} received message #{}", self.stream_id, msg.sequence);
return Some(data);
}
StreamPayload::End => {
info!("Stream {} ended normally", self.stream_id);
self.ended = true;
return None;
}
StreamPayload::Error(err) => {
error!("Stream {} error: {}", self.stream_id, err.message);
self.ended = true;
return None;
}
StreamPayload::Cancel => {
info!("Stream {} cancelled", self.stream_id);
self.ended = true;
return None;
}
StreamPayload::Start => {
debug!("Stream {} started", self.stream_id);
}
StreamPayload::Heartbeat => {
debug!("Stream {} heartbeat", self.stream_id);
}
}
}
}
pub fn is_ended(&self) -> bool {
self.ended
}
pub fn stream_id(&self) -> &str {
&self.stream_id
}
}
#[derive(Clone)]
pub struct StreamSender<T: Message + Serialize + Clone> {
stream_id: String,
topic: String,
ctx: Arc<Context>,
sequence: Arc<Mutex<u64>>,
ended: Arc<Mutex<bool>>,
_phantom: PhantomData<T>,
}
impl<T: Message + Serialize + Clone> StreamSender<T> {
pub(crate) fn new(stream_id: String, topic: String, ctx: Arc<Context>) -> Self {
Self {
stream_id,
topic,
ctx,
sequence: Arc::new(Mutex::new(0)),
ended: Arc::new(Mutex::new(false)),
_phantom: PhantomData,
}
}
pub async fn send(&self, data: &T) -> Result<()> {
if *self.ended.lock().await {
return Err(Mecha10Error::MessagingError {
message: "Stream already ended".to_string(),
suggestion: "Cannot send after stream end".to_string(),
});
}
let mut seq = self.sequence.lock().await;
let message = StreamMessage {
stream_id: self.stream_id.clone(),
sequence: *seq,
payload: StreamPayload::Data(data.clone()),
timestamp: now_micros(),
};
*seq += 1;
drop(seq);
self.ctx.publish_raw(&self.topic, &message).await?;
debug!("Stream {} sent message #{}", self.stream_id, message.sequence);
Ok(())
}
pub async fn end(&self) -> Result<()> {
let mut ended = self.ended.lock().await;
if *ended {
return Ok(());
}
let seq = self.sequence.lock().await;
let message = StreamMessage {
stream_id: self.stream_id.clone(),
sequence: *seq,
payload: StreamPayload::<T>::End,
timestamp: now_micros(),
};
self.ctx.publish_raw(&self.topic, &message).await?;
*ended = true;
info!("Stream {} ended", self.stream_id);
Ok(())
}
pub async fn error(&self, error_message: &str) -> Result<()> {
let mut ended = self.ended.lock().await;
if *ended {
return Ok(());
}
let seq = self.sequence.lock().await;
let message = StreamMessage {
stream_id: self.stream_id.clone(),
sequence: *seq,
payload: StreamPayload::<T>::Error(StreamError {
message: error_message.to_string(),
code: None,
}),
timestamp: now_micros(),
};
self.ctx.publish_raw(&self.topic, &message).await?;
*ended = true;
error!("Stream {} error: {}", self.stream_id, error_message);
Ok(())
}
pub async fn heartbeat(&self) -> Result<()> {
if *self.ended.lock().await {
return Ok(());
}
let seq = self.sequence.lock().await;
let message = StreamMessage {
stream_id: self.stream_id.clone(),
sequence: *seq,
payload: StreamPayload::<T>::Heartbeat,
timestamp: now_micros(),
};
self.ctx.publish_raw(&self.topic, &message).await?;
debug!("Stream {} heartbeat", self.stream_id);
Ok(())
}
pub async fn is_ended(&self) -> bool {
*self.ended.lock().await
}
pub fn stream_id(&self) -> &str {
&self.stream_id
}
}
impl<T: Message + Serialize + Clone> Drop for StreamSender<T> {
fn drop(&mut self) {
debug!("StreamSender dropped for stream {}", self.stream_id);
}
}
pub trait StreamingRpcExt {
fn stream_request<Req, Resp>(
&self,
topic: &str,
request: &Req,
) -> impl Future<Output = Result<StreamReceiver<Resp>>> + Send
where
Req: Message + Serialize + Clone,
Resp: Message + DeserializeOwned + Send + 'static;
fn stream_respond<Req, Resp, F, Fut>(&self, topic: &str, handler: F) -> impl Future<Output = Result<()>> + Send
where
Req: Message + DeserializeOwned + Send + 'static,
Resp: Message + Serialize + Clone + Send + 'static,
F: Fn(Req, StreamSender<Resp>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send + 'static;
fn bidirectional_stream<T>(
&self,
topic: &str,
) -> impl Future<Output = Result<(StreamSender<T>, StreamReceiver<T>)>> + Send
where
T: Message + Serialize + DeserializeOwned + Clone + Send + 'static;
}
impl StreamingRpcExt for Context {
async fn stream_request<Req, Resp>(&self, topic: &str, request: &Req) -> Result<StreamReceiver<Resp>>
where
Req: Message + Serialize + Clone,
Resp: Message + DeserializeOwned + Send + 'static,
{
let stream_id = Uuid::new_v4().to_string();
let request_topic = format!("{}/stream/request", topic);
let response_topic = format!("{}/stream/response", topic);
let receiver = self.subscribe_raw::<StreamMessage<Resp>>(&response_topic).await?;
let start_msg = StreamMessage {
stream_id: stream_id.clone(),
sequence: 0,
payload: StreamPayload::Data(request.clone()),
timestamp: now_micros(),
};
self.publish_raw(&request_topic, &start_msg).await?;
info!("Stream request started: {} (stream_id: {})", topic, stream_id);
Ok(StreamReceiver::new(stream_id, receiver))
}
async fn stream_respond<Req, Resp, F, Fut>(&self, topic: &str, handler: F) -> Result<()>
where
Req: Message + DeserializeOwned + Send + 'static,
Resp: Message + Serialize + Clone + Send + 'static,
F: Fn(Req, StreamSender<Resp>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send + 'static,
{
let request_topic = format!("{}/stream/request", topic);
let response_topic = format!("{}/stream/response", topic);
let mut requests = self.subscribe_raw::<StreamMessage<Req>>(&request_topic).await?;
let ctx = Arc::new(self.clone());
let handler = Arc::new(handler);
info!("Stream responder registered for: {}", topic);
tokio::spawn(async move {
while let Some(msg) = requests.recv().await {
match msg.payload {
StreamPayload::Data(request) => {
let stream_id = msg.stream_id.clone();
let sender = StreamSender::new(stream_id.clone(), response_topic.clone(), Arc::clone(&ctx));
let handler = Arc::clone(&handler);
tokio::spawn(async move {
debug!("Handling stream request: {}", stream_id);
if let Err(e) = handler(request, sender.clone()).await {
error!("Stream handler error ({}): {}", stream_id, e);
let _ = sender.error(&e.to_string()).await;
} else {
let _ = sender.end().await;
}
});
}
StreamPayload::Cancel => {
debug!("Stream cancelled: {}", msg.stream_id);
}
_ => {}
}
}
});
Ok(())
}
async fn bidirectional_stream<T>(&self, topic: &str) -> Result<(StreamSender<T>, StreamReceiver<T>)>
where
T: Message + Serialize + DeserializeOwned + Clone + Send + 'static,
{
let stream_id = Uuid::new_v4().to_string();
let client_topic = format!("{}/bidir/client", topic);
let server_topic = format!("{}/bidir/server", topic);
let receiver = self.subscribe_raw::<StreamMessage<T>>(&server_topic).await?;
let sender = StreamSender::new(stream_id.clone(), client_topic, Arc::new(self.clone()));
info!("Bidirectional stream created: {} (stream_id: {})", topic, stream_id);
Ok((sender, StreamReceiver::new(stream_id, receiver)))
}
}