use std::error::Error; use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use futures_channel::oneshot; use recycle_box::{coerce_box, RecycleBox}; use crate::channel; use crate::model::Model; use crate::ports::{InputFn, ReplierFn}; pub(super) type SenderFuture = Pin> + Send>>; /// An event or query sender abstracting over the target model and input method. pub(super) trait Sender: Send { /// Asynchronously send the event or request. fn send(&mut self, arg: T) -> SenderFuture; } /// An object that can send events to an input port. pub(super) struct InputSender { func: F, sender: channel::Sender, _phantom_closure: PhantomData, _phantom_closure_marker: PhantomData, } impl InputSender where M: Model, F: for<'a> InputFn<'a, M, T, S>, T: Send + 'static, { pub(super) fn new(func: F, sender: channel::Sender) -> Self { Self { func, sender, _phantom_closure: PhantomData, _phantom_closure_marker: PhantomData, } } } impl Sender for InputSender where M: Model, F: for<'a> InputFn<'a, M, T, S> + Clone, T: Send + 'static, S: Send + 'static, { fn send(&mut self, arg: T) -> SenderFuture<()> { let func = self.func.clone(); let sender = self.sender.clone(); Box::pin(async move { sender .send(move |model, scheduler, recycle_box| { let fut = func.call(model, arg, scheduler); coerce_box!(RecycleBox::recycle(recycle_box, fut)) }) .await .map_err(|_| SendError {}) }) } } /// An object that can send a request to a replier port and retrieve a response. pub(super) struct ReplierSender { func: F, sender: channel::Sender, _phantom_closure: PhantomData R>, _phantom_closure_marker: PhantomData, } impl ReplierSender where M: Model, F: for<'a> ReplierFn<'a, M, T, R, S>, T: Send + 'static, R: Send + 'static, { pub(super) fn new(func: F, sender: channel::Sender) -> Self { Self { func, sender, _phantom_closure: PhantomData, _phantom_closure_marker: PhantomData, } } } impl Sender for ReplierSender where M: Model, F: for<'a> ReplierFn<'a, M, T, R, S> + Clone, T: Send + 'static, R: Send + 'static, S: Send, { fn send(&mut self, arg: T) -> SenderFuture { let func = self.func.clone(); let sender = self.sender.clone(); let (reply_sender, reply_receiver) = oneshot::channel(); Box::pin(async move { sender .send(move |model, scheduler, recycle_box| { let fut = async move { let reply = func.call(model, arg, scheduler).await; let _ = reply_sender.send(reply); }; coerce_box!(RecycleBox::recycle(recycle_box, fut)) }) .await .map_err(|_| SendError {})?; reply_receiver.await.map_err(|_| SendError {}) }) } } /// Error returned when the mailbox was closed or dropped. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(super) struct SendError {} impl fmt::Display for SendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "sending message into a closed mailbox") } } impl Error for SendError {}