use futures::Stream;
use pin_project::pin_project;
use rc_x509_proto::{DecodeError, protocol::v1};
use thiserror::Error;
use tokio::sync::mpsc::{self, error::TrySendError};
use tokio_stream::wrappers::ReceiverStream;
use tokio_util::bytes::Bytes;
use crate::host_runtime::CorrelationId;
const DISPATCH_QUEUE_LEN: usize = 255;
#[derive(Debug)]
pub struct Dispatch {
pub correlation_id: CorrelationId,
pub payload: Bytes,
}
#[derive(Debug)]
pub struct DispatchResult {
pub correlation_id: CorrelationId,
pub result: Result<v1::DispatchResponsePayload, DispatchError>,
}
#[derive(Debug, Error)]
pub enum DispatchError {
#[error("unknown payload type")]
UnknownPayload,
#[error("no handler registered for the dispatched payload type")]
NoDispatchHandler,
#[error("dispatch handler delivery queue is full")]
HandlerQueueFull,
#[error("dispatch request queue is full")]
DispatchRequestQueueFull,
#[error("dispatch task is not running")]
DispatchClosed,
#[error("deserialisation error processing dispatch result from FFI host: {0}")]
ReplyDeserialisation(DecodeError),
#[error("unknown dispatch error from host app")]
UnknownHostDispatchError,
}
#[derive(Debug)]
pub struct DispatchPublisher {
tx: mpsc::Sender<Dispatch>,
rx: Option<mpsc::Receiver<DispatchResult>>,
}
impl DispatchPublisher {
pub fn dispatch(&self, payload: Dispatch) -> Result<(), DispatchError> {
match self.tx.try_send(payload) {
Ok(()) => Ok(()),
Err(TrySendError::Closed(_)) => Err(DispatchError::DispatchClosed),
Err(TrySendError::Full(_)) => Err(DispatchError::DispatchRequestQueueFull),
}
}
pub fn take_recv_stream(&mut self) -> Option<impl Stream<Item = DispatchResult> + 'static> {
self.rx.take().map(ReceiverStream::new)
}
}
#[derive(Debug)]
#[pin_project]
pub struct DispatchStream {
#[pin]
rx: ReceiverStream<Dispatch>,
}
impl Stream for DispatchStream {
type Item = Dispatch;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
let this = self.project();
this.rx.poll_next(cx)
}
}
#[derive(Debug, Error)]
#[error("dispatch response queue is closed")]
pub struct DispatchResponseQueueClosed {}
#[derive(Debug, Clone)]
pub struct DispatchResponder {
tx: mpsc::Sender<DispatchResult>,
}
impl DispatchResponder {
pub fn send_response(
&self,
payload: DispatchResult,
) -> Result<(), DispatchResponseQueueClosed> {
match self.tx.blocking_send(payload) {
Ok(()) => Ok(()),
Err(_) => Err(DispatchResponseQueueClosed {}),
}
}
}
pub fn new_dispatcher_interconnect() -> (DispatchPublisher, DispatchStream, DispatchResponder) {
let (tx1, rx1) = mpsc::channel(DISPATCH_QUEUE_LEN);
let (tx2, rx2) = mpsc::channel(DISPATCH_QUEUE_LEN + 1);
let publisher = DispatchPublisher {
tx: tx1,
rx: Some(rx2),
};
let stream = DispatchStream {
rx: ReceiverStream::new(rx1),
};
let responder = DispatchResponder { tx: tx2 };
(publisher, stream, responder)
}