use super::events::{AppEvent, Event};
use async_channel::{unbounded, Receiver, Sender};
use futures_lite::{ready, Future, Stream, StreamExt};
use smol_timeout::TimeoutExt;
use std::marker::PhantomData;
use std::{
pin::Pin,
task::Poll,
time::{Duration, Instant},
};
pub(crate) struct Refresh<A> {
time_sender: Sender<Duration>,
last: Instant,
every: Duration,
phantom_a: PhantomData<A>,
timeout: Pin<Box<dyn Future<Output = (Receiver<Duration>, Option<Option<Duration>>)>>>,
}
impl<A> Refresh<A> {
pub fn new(mut every: Duration) -> Self {
let last = Instant::now();
every = every;
let (time_sender, time_receiver) = unbounded();
let timeout = wait(time_receiver, every);
Self {
last,
every,
timeout,
time_sender,
phantom_a: PhantomData,
}
}
pub fn time_sender(&self) -> Sender<Duration> {
self.time_sender.clone()
}
}
impl<A: AppEvent> Stream for Refresh<A> {
type Item = Event<A>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
loop {
let (time_receiver, res) = ready!(Pin::new(&mut self.timeout).poll(cx));
let now = Instant::now();
match res {
Some(None) => break Poll::Ready(None),
Some(Some(every)) => {
self.every = every;
self.timeout = wait(time_receiver, self.last + self.every - now);
}
None => {
let count = if self.every == Duration::ZERO {
1
} else {
(now - self.last).as_millis() / self.every.as_millis()
};
self.last = self.last + self.every * count as u32;
self.timeout = wait(time_receiver, self.last + self.every - now);
break Poll::Ready(Some(Event::Refresh(count as usize)));
}
};
}
}
}
fn wait(
mut time_receiver: Receiver<Duration>,
delay: Duration,
) -> Pin<Box<dyn Future<Output = (Receiver<Duration>, Option<Option<Duration>>)>>> {
Box::pin(async move {
let new_every = time_receiver
.next()
.timeout(delay.clamp(Duration::from_micros(1), Duration::from_secs(60 * 60)))
.await;
(time_receiver, new_every)
})
}