use std::{any::Any, sync::mpsc, thread, thread::JoinHandle};
use thiserror::Error;
#[derive(Error, Debug)]
#[error("sending event on a stopped runloop")]
pub struct Error;
pub(crate) trait Handle {
type Event;
fn handle(&mut self, event: Self::Event, context: &mut Context);
#[inline]
fn run(self) -> Runloop<Self::Event>
where
Self: Sized + Send + 'static,
Self::Event: Send + 'static,
{
Runloop::run(self)
}
}
#[derive(Debug)]
pub(crate) struct Context {
is_stopped: bool,
}
impl Context {
#[inline]
fn new() -> Self {
Self { is_stopped: false }
}
#[inline]
pub(crate) fn stop(&mut self) {
self.is_stopped = true;
}
#[inline]
pub(crate) fn is_stopped(&self) -> bool {
self.is_stopped
}
}
pub(crate) struct Runloop<Event> {
sender: mpsc::Sender<Event>,
thread_handle: JoinHandle<()>,
}
impl<Event> Runloop<Event>
where
Event: Send + 'static,
{
pub(crate) fn run<H>(mut handler: H) -> Self
where
H: Handle<Event = Event> + Send + 'static,
{
let (sender, receiver) = mpsc::channel();
let thread_handle = thread::spawn(move || {
let mut context = Context::new();
while !context.is_stopped() && let Ok(event) = receiver.recv() {
handler.handle(event, &mut context);
}
});
Self { sender, thread_handle }
}
#[inline]
pub(crate) fn on(&self, event: Event) -> Result<(), Error> {
self.sender.send(event).map_err(|_| Error)
}
#[inline]
pub(crate) fn join(self) -> Result<(), Box<dyn Any + Send + 'static>> {
self.thread_handle.join()
}
}