use std::fmt;
use crossbeam::channel::{Receiver, Sender};
pub struct Executor<T> {
rx: Receiver<Box<dyn FnOnce(&mut T) + Send>>,
}
type SyncFn<T> = Box<dyn FnOnce(&mut T) + Send>;
impl<T> std::ops::Deref for Executor<T> {
type Target = Receiver<SyncFn<T>>;
fn deref(&self) -> &Self::Target {
&self.rx
}
}
pub struct Runner<T> {
tx: Sender<SyncFn<T>>,
}
impl<T> Clone for Runner<T> {
fn clone(&self) -> Self {
Self {
tx: self.tx.clone(),
}
}
}
pub fn channel<T>() -> (Runner<T>, Executor<T>) {
let (tx, rx) = crossbeam::channel::unbounded();
(Runner { tx }, Executor { rx })
}
#[derive(Debug)]
pub struct DispatchError;
impl fmt::Display for DispatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The channel is closed")
}
}
impl std::error::Error for DispatchError {}
impl<T> Runner<T> {
pub fn dispatch_command<R: Send + 'static>(
&self,
f: impl FnOnce(&mut T) -> R + Send + 'static,
) -> Result<R, DispatchError> {
let (cb, rx) = oneshot::channel();
self.tx
.send(Box::new(move |s| {
let _ = cb.send(f(s));
}))
.map_err(|_| DispatchError)?;
rx.recv().map_err(|_| DispatchError)
}
pub fn dispatch_event(
&self,
f: impl FnOnce(&mut T) + Send + 'static,
) -> Result<(), DispatchError> {
self.tx.send(Box::new(f)).map_err(|_| DispatchError)
}
}
#[macro_export]
macro_rules! make_runner_command_ext {
($parent:ty => $vis:vis trait $ext:ident {
$(
fn $fn:ident(&mut $self:ident $(,$field:ident : $ty:ty)*) -> $ret:ty $body:block
)*
}) => {
$vis trait $ext {
$(
fn $fn(&self, $($field : $ty),*) -> Result<$ret, $crate::runners::DispatchError>;
)*
}
impl $parent {
$(
$vis fn $fn(&mut $self, $($field : $ty),*) -> $ret {
$body
}
)*
}
impl $ext for $crate::runners::Runner<$parent> {
$(
fn $fn(&self, $($field : $ty),*) -> Result<$ret, $crate::runners::DispatchError> {
self.dispatch_command(move |s| s.$fn($($field),*))
}
)*
}
};
}
#[macro_export]
macro_rules! make_runner_event_ext {
($parent:ty => $vis:vis trait $ext:ident {
$(
fn $fn:ident(&mut $self:ident $(,$field:ident : $ty:ty)*) $body:block
)*
}) => {
$vis trait $ext {
$(
fn $fn(&self, $($field : $ty),*) -> Result<(), $crate::runners::DispatchError>;
)*
}
impl $parent {
$(
$vis fn $fn(&mut $self, $($field : $ty),*) {
$body
}
)*
}
impl $ext for $crate::runners::Runner<$parent> {
$(
fn $fn(&self, $($field : $ty),*) -> Result<(), $crate::runners::DispatchError> {
self.dispatch_event(move |s| s.$fn($($field),*))
}
)*
}
};
}
pub fn spawn_background_thread<F1, F2, T>(f: F1, on_return: Option<F2>)
where
F1: FnOnce() -> T + Send + 'static,
F2: FnOnce(T) + Send + 'static,
{
std::thread::spawn(move || {
if let Some(on_return) = on_return {
on_return(f())
} else {
f();
}
});
}