use ek_event::Event;
use std::any::Any;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{RecvTimeoutError, Sender as StdSender};
use std::sync::Arc;
use std::time::Duration;
pub fn e_req_type(target: &str) -> String {
format!("eReq.{target}")
}
pub fn e_to_type(target: &str) -> String {
format!("eTo.{target}")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReplyErr {
InvalidPayload,
}
impl fmt::Display for ReplyErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidPayload => write!(f, "invalid payload"),
}
}
}
pub struct Envelope {
pub payload: Arc<dyn Any + Send + Sync>,
pub reply: OneshotSender<Box<dyn Any + Send + Sync>>,
}
impl Envelope {
pub fn new(payload: Arc<dyn Any + Send + Sync>) -> (Envelope, OneshotReceiver<Box<dyn Any + Send + Sync>>) {
let (tx, rx) = oneshot();
(Envelope { payload, reply: tx }, rx)
}
pub fn handle<Req, Res>(event: &Event, handler: impl FnOnce(&Req) -> Res)
where
Req: Any + Send + Sync,
Res: Any + Send + Sync,
{
let Some(env) = event.data.downcast_ref::<Envelope>() else {
return;
};
match Arc::clone(&env.payload).downcast::<Req>() {
Ok(req) => {
let res = handler(&*req);
let _ = env.reply.send(Box::new(Ok::<Res, ReplyErr>(res)));
}
Err(_) => {
let _ = env.reply.send(Box::new(Err::<Res, ReplyErr>(ReplyErr::InvalidPayload)));
}
}
}
}
pub struct OneshotSender<T> {
tx: StdSender<T>,
sent: AtomicBool,
}
impl<T> OneshotSender<T> {
pub(crate) fn new_inner(tx: StdSender<T>) -> Self {
Self { tx, sent: AtomicBool::new(false) }
}
pub fn send(&self, v: T) -> Result<(), T> {
if self.sent.swap(true, Ordering::SeqCst) {
return Err(v);
}
self.tx.send(v).map_err(|e| e.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OneshotErr {
Timeout,
Closed,
}
pub struct OneshotReceiver<T>(std::sync::mpsc::Receiver<T>);
impl<T> OneshotReceiver<T> {
pub(crate) fn new_inner(rx: std::sync::mpsc::Receiver<T>) -> Self {
Self(rx)
}
pub fn recv(self) -> Result<T, OneshotErr> {
self.0.recv().map_err(|_| OneshotErr::Closed)
}
pub fn recv_timeout(self, d: Duration) -> Result<T, OneshotErr> {
match self.0.recv_timeout(d) {
Ok(v) => Ok(v),
Err(RecvTimeoutError::Timeout) => Err(OneshotErr::Timeout),
Err(RecvTimeoutError::Disconnected) => Err(OneshotErr::Closed),
}
}
}
pub fn oneshot<T>() -> (OneshotSender<T>, OneshotReceiver<T>) {
let (tx, rx) = std::sync::mpsc::channel();
(OneshotSender::new_inner(tx), OneshotReceiver::new_inner(rx))
}