use std::collections::{HashMap, VecDeque};
use chrono::{DateTime, Duration, Utc};
use uuid::Uuid;
use crate::model::{Task, TaskFinished, TaskKind};
const DELAY_SECONDS: i64 = 2500;
#[derive(Debug, Clone, Copy)]
pub(crate) enum TaskStatus {
Pending,
Running,
}
#[derive(Default)]
pub(crate) struct Queue {
status: HashMap<TaskKind, TaskStatus>,
data: VecDeque<Task>,
modified: bool,
}
impl Queue {
#[inline]
pub(crate) fn status(&self, kind: &TaskKind) -> Option<TaskStatus> {
self.status.get(kind).copied()
}
#[inline]
pub(crate) fn complete(&mut self, kind: &TaskKind) -> Option<TaskStatus> {
self.status.remove(kind)
}
#[inline]
pub(crate) fn data(&self) -> impl ExactSizeIterator<Item = &Task> {
self.data.iter()
}
pub(crate) fn remove_tasks_by<P>(&mut self, mut predicate: P) -> usize
where
P: FnMut(&Task) -> bool,
{
let mut removed = 0;
for data in &self.data {
if predicate(data) {
let _ = self.status.remove(&data.kind);
removed += 1;
}
}
self.data.retain(move |task| !predicate(task));
self.modified |= removed > 0;
removed
}
pub(crate) fn sort(&mut self) {
self.data.rotate_right(self.data.as_slices().1.len());
debug_assert!(self.data.as_slices().1.is_empty());
self.data
.as_mut_slices()
.0
.sort_by(|a, b| a.scheduled.cmp(&b.scheduled));
self.modified = true;
}
#[inline]
pub(crate) fn take_modified(&mut self) -> bool {
std::mem::take(&mut self.modified)
}
pub(crate) fn next_task(
&mut self,
now: &DateTime<Utc>,
timed_out: Option<Uuid>,
) -> Option<Task> {
let task = self.data.front()?;
if !matches!(timed_out, Some(id) if id == task.id) && task.scheduled > *now {
return None;
}
let task = self.data.pop_front()?;
self.status.insert(task.kind, TaskStatus::Running);
Some(task)
}
pub(crate) fn next_sleep(&self, now: &DateTime<Utc>) -> Option<(u64, Uuid)> {
let task = self.data.front()?;
let seconds = u64::try_from(
task.scheduled
.signed_duration_since(*now)
.num_seconds()
.max(0),
)
.ok()?;
let id = task.id;
Some((seconds, id))
}
pub(crate) fn push(&mut self, kind: TaskKind, finished: TaskFinished) -> bool {
if self.status.contains_key(&kind) {
return false;
}
let scheduled = self
.data
.back()
.map(|t| t.scheduled)
.unwrap_or_else(Utc::now)
+ Duration::milliseconds(DELAY_SECONDS);
self.status.insert(kind, TaskStatus::Pending);
self.data.push_back(Task {
id: Uuid::new_v4(),
scheduled,
kind,
finished,
});
self.modified = true;
true
}
pub(crate) fn push_raw(&mut self, task: Task) {
if self.status.contains_key(&task.kind) {
return;
}
self.status.insert(task.kind, TaskStatus::Pending);
self.data.push_back(task);
self.modified = true;
}
}