use std::{
collections::HashMap,
pin::Pin,
sync::atomic::{AtomicU64, Ordering},
time::Duration,
};
use tokio::time::{Instant, Sleep};
pub type RequestSequence = u64;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum RequestId {
FetchEntries,
FetchSubscription,
SubscribeFeed,
UnsubscribeFeed,
}
pub struct InFlight {
next_request_sequence: AtomicU64,
in_flights: HashMap<RequestSequence, InFlightEntry>,
throbber_timer: Pin<Box<Sleep>>,
throbber_step: i8,
throbber_timer_interval: Duration,
}
impl InFlight {
pub fn new() -> Self {
Self {
next_request_sequence: AtomicU64::new(0),
in_flights: HashMap::new(),
throbber_timer: Box::pin(tokio::time::sleep(Duration::from_secs(3600 * 24))),
throbber_step: 0,
throbber_timer_interval: Duration::from_millis(250),
}
}
#[must_use]
pub fn with_throbber_timer_interval(self, interval: Duration) -> Self {
Self {
throbber_timer_interval: interval,
..self
}
}
pub fn recent_in_flight(&self) -> Option<RequestId> {
self.in_flights
.iter()
.max_by_key(|(_, entry)| entry.start)
.map(|(_, entry)| entry.request_id)
}
pub async fn throbber_timer(&mut self) {
self.throbber_timer.as_mut().await;
}
pub fn reset_throbber_timer(&mut self) {
self.throbber_timer
.as_mut()
.reset(Instant::now() + self.throbber_timer_interval);
}
pub fn inc_throbber_step(&mut self) {
self.throbber_step = self.throbber_step.wrapping_add(1);
}
pub fn throbber_step(&self) -> i8 {
self.throbber_step
}
pub fn add(&mut self, request_id: RequestId) -> RequestSequence {
let seq = self.next_request_sequence();
self.in_flights.insert(
seq,
InFlightEntry {
start: Instant::now(),
request_id,
},
);
self.reset_throbber_timer();
seq
}
pub fn remove(&mut self, seq: RequestSequence) -> Option<RequestId> {
let req_id = self.in_flights.remove(&seq).map(|entry| entry.request_id);
if self.in_flights.is_empty() {
self.throbber_timer
.as_mut()
.reset(Instant::now() + Duration::from_secs(3600 * 24));
}
req_id
}
fn next_request_sequence(&self) -> RequestSequence {
self.next_request_sequence.fetch_add(1, Ordering::Relaxed)
}
}
struct InFlightEntry {
start: Instant,
request_id: RequestId,
}