use std::{collections::VecDeque, time::Instant};
use crate::TaskSpec;
pub(super) struct SlotState {
pub status: SlotStatus,
pub queue: VecDeque<TaskSpec>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum SlotStatus {
Idle,
Running {
started_at: Instant,
},
Terminating {
cancelled_at: Instant,
},
}
impl SlotState {
pub fn new() -> Self {
Self {
status: SlotStatus::Idle,
queue: VecDeque::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_slot_is_idle_with_empty_queue() {
let slot = SlotState::new();
assert_eq!(slot.status, SlotStatus::Idle);
assert!(slot.queue.is_empty());
}
#[test]
fn idle_ne_running_ne_terminating() {
let idle = SlotStatus::Idle;
let now = Instant::now();
let running = SlotStatus::Running { started_at: now };
let terminating = SlotStatus::Terminating { cancelled_at: now };
assert_ne!(idle, running);
assert_ne!(idle, terminating);
assert_ne!(running, terminating);
}
#[test]
fn matches_idle_works_on_all_variants() {
let now = Instant::now();
assert!(matches!(SlotStatus::Idle, SlotStatus::Idle));
assert!(!matches!(
SlotStatus::Running { started_at: now },
SlotStatus::Idle
));
assert!(!matches!(
SlotStatus::Terminating { cancelled_at: now },
SlotStatus::Idle
));
}
#[test]
fn queue_push_pop_fifo() {
let mut slot = SlotState::new();
let task_a = make_spec("a");
let task_b = make_spec("b");
let task_c = make_spec("c");
slot.queue.push_back(task_a);
slot.queue.push_back(task_b);
slot.queue.push_back(task_c);
assert_eq!(slot.queue.len(), 3);
assert_eq!(slot.queue.pop_front().unwrap().name(), "a");
assert_eq!(slot.queue.pop_front().unwrap().name(), "b");
assert_eq!(slot.queue.pop_front().unwrap().name(), "c");
assert!(slot.queue.is_empty());
}
fn make_spec(name: &str) -> TaskSpec {
use crate::{BackoffPolicy, RestartPolicy, TaskFn, TaskRef};
use tokio_util::sync::CancellationToken;
let task: TaskRef = TaskFn::arc(name, |_ctx: CancellationToken| async { Ok(()) });
TaskSpec::new(task, RestartPolicy::Never, BackoffPolicy::default(), None)
}
}