use std::pin::Pin;
use tokio_stream::Stream;
use tokio_util::sync::CancellationToken;
use super::Subscription;
use crate::input::Event;
pub struct TerminalEventSubscription<M, F>
where
F: Fn(Event) -> Option<M> + Send + 'static,
{
pub(crate) event_handler: F,
_phantom: std::marker::PhantomData<M>,
}
impl<M, F> TerminalEventSubscription<M, F>
where
F: Fn(Event) -> Option<M> + Send + 'static,
{
pub fn new(event_handler: F) -> Self {
Self {
event_handler,
_phantom: std::marker::PhantomData,
}
}
}
impl<M, F> Subscription<M> for TerminalEventSubscription<M, F>
where
M: Send + 'static,
F: Fn(Event) -> Option<M> + Send + 'static,
{
fn into_stream(
self: Box<Self>,
cancel: CancellationToken,
) -> Pin<Box<dyn Stream<Item = M> + Send>> {
use crossterm::event::EventStream;
use tokio_stream::StreamExt;
let handler = self.event_handler;
Box::pin(async_stream::stream! {
let mut reader = EventStream::new();
loop {
tokio::select! {
maybe_event = reader.next() => {
match maybe_event {
Some(Ok(ct_event)) => {
if let Some(event) = crate::input::convert::from_crossterm_event(ct_event) {
if let Some(msg) = (handler)(event) {
yield msg;
}
}
}
Some(Err(_)) => break,
None => break,
}
}
_ = cancel.cancelled() => break,
}
}
})
}
}
pub fn terminal_events<M, F>(handler: F) -> TerminalEventSubscription<M, F>
where
F: Fn(Event) -> Option<M> + Send + 'static,
{
TerminalEventSubscription::new(handler)
}