termit 0.7.0

Terminal UI over crossterm
Documentation
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)
    })
}