use actionqueue_core::ids::{RunId, TaskId};
use actionqueue_core::run::state::RunState;
#[derive(Debug, Clone)]
pub struct ChildState {
task_id: TaskId,
run_states: Vec<(RunId, RunState)>,
all_terminal: bool,
}
impl ChildState {
pub fn new(task_id: TaskId, run_states: Vec<(RunId, RunState)>) -> Self {
let all_terminal =
!run_states.is_empty() && run_states.iter().all(|(_, s)| s.is_terminal());
Self { task_id, run_states, all_terminal }
}
pub fn task_id(&self) -> TaskId {
self.task_id
}
pub fn run_states(&self) -> &[(RunId, RunState)] {
&self.run_states
}
pub fn all_terminal(&self) -> bool {
self.all_terminal
}
pub fn has_runs(&self) -> bool {
!self.run_states.is_empty()
}
}
#[derive(Debug, Clone, Default)]
pub struct ChildrenSnapshot {
children: Vec<ChildState>,
}
impl ChildrenSnapshot {
pub fn new(children: Vec<ChildState>) -> Self {
Self { children }
}
pub fn children(&self) -> &[ChildState] {
&self.children
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn all_children_terminal(&self) -> bool {
!self.children.is_empty() && self.children.iter().all(|c| c.all_terminal())
}
pub fn get(&self, task_id: TaskId) -> Option<&ChildState> {
self.children.iter().find(|c| c.task_id() == task_id)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn tid() -> TaskId {
TaskId::new()
}
fn rid() -> RunId {
RunId::new()
}
#[test]
fn empty_snapshot() {
let snap = ChildrenSnapshot::default();
assert!(snap.is_empty());
assert!(!snap.all_children_terminal());
assert!(snap.children().is_empty());
}
#[test]
fn single_child_all_terminal() {
let snap =
ChildrenSnapshot::new(vec![ChildState::new(tid(), vec![(rid(), RunState::Completed)])]);
assert!(!snap.is_empty());
assert!(snap.all_children_terminal());
}
#[test]
fn single_child_not_terminal() {
let snap =
ChildrenSnapshot::new(vec![ChildState::new(tid(), vec![(rid(), RunState::Running)])]);
assert!(!snap.all_children_terminal());
}
#[test]
fn multiple_children_mixed() {
let snap = ChildrenSnapshot::new(vec![
ChildState::new(tid(), vec![(rid(), RunState::Completed)]),
ChildState::new(tid(), vec![(rid(), RunState::Running)]),
]);
assert!(!snap.all_children_terminal());
}
#[test]
fn all_children_terminal_mixed_terminal_states() {
let snap = ChildrenSnapshot::new(vec![
ChildState::new(tid(), vec![(rid(), RunState::Completed)]),
ChildState::new(tid(), vec![(rid(), RunState::Failed)]),
]);
assert!(snap.all_children_terminal());
}
#[test]
fn get_found_and_not_found() {
let known = tid();
let snap =
ChildrenSnapshot::new(vec![ChildState::new(known, vec![(rid(), RunState::Completed)])]);
assert!(snap.get(known).is_some());
assert!(snap.get(tid()).is_none());
}
#[test]
fn child_with_no_runs_not_terminal() {
let snap = ChildrenSnapshot::new(vec![ChildState::new(tid(), vec![])]);
assert!(!snap.all_children_terminal());
assert!(!snap.children()[0].all_terminal());
}
#[test]
fn child_with_no_runs_reports_has_runs_false() {
let child = ChildState::new(tid(), vec![]);
assert!(!child.has_runs());
let child_with_run = ChildState::new(tid(), vec![(rid(), RunState::Completed)]);
assert!(child_with_run.has_runs());
}
}