use super::events::{AppEvent, Event, MouseEvent, MouseEventKind};
use futures_lite::{io, stream::Fuse, Stream, StreamExt};
use std::{
pin::Pin,
task::{Context, Poll},
};
pub(crate) fn squash_input<S, A: AppEvent>(input: S) -> Squash<S>
where
S: Stream<Item = io::Result<Event<A>>>,
{
Squash::new(input)
}
pub(crate) struct Squash<S> {
inner: Pin<Box<Fuse<S>>>,
}
impl<S, A: AppEvent> Squash<S>
where
S: Stream<Item = io::Result<Event<A>>>,
{
pub fn new(inner: S) -> Self {
Self {
inner: Box::pin(inner.fuse()),
}
}
}
impl<S, A: AppEvent> Stream for Squash<S>
where
S: Stream<Item = io::Result<Event<A>>>,
{
type Item = Vec<io::Result<Event<A>>>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut pending = vec![];
let mut beats = 0;
let readysome = |mut pending: Vec<io::Result<Event<A>>>, beats, otherwise| {
if beats != 0 || !pending.is_empty() {
pending.push(Ok(Event::Refresh(beats)));
Poll::Ready(Some(pending))
} else {
otherwise
}
};
loop {
break match self.inner.as_mut().poll_next(cx) {
Poll::Pending => readysome(pending, beats, Poll::Pending),
Poll::Ready(None) => readysome(pending, beats, Poll::Ready(None)),
Poll::Ready(Some(next)) => {
match (pending.last(), &next) {
(_, Ok(Event::Refresh(n2))) => {
beats += n2;
continue;
}
(
Some(Ok(Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
..
}))),
Ok(Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
..
})),
) => {
pending.pop();
}
_ => {}
}
pending.push(next);
continue;
}
};
}
}
}