use crate::error::types::TaskFailure;
use crate::event::payload::{PolicyDecision, SupervisorEvent, What};
use crate::journal::ring::EventJournal;
use crate::state::supervisor::SupervisorState;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RunSummary {
pub started_at_unix_nanos: u128,
pub finished_at_unix_nanos: u128,
pub shutdown_cause: Option<String>,
pub restart_count: u64,
pub failure_count: u64,
pub recent_failures: Vec<TaskFailure>,
pub recent_events: Vec<SupervisorEvent>,
pub final_state: SupervisorState,
pub final_decision: Option<PolicyDecision>,
}
#[derive(Debug, Clone)]
pub struct RunSummaryBuilder {
pub recent_event_limit: usize,
}
impl RunSummaryBuilder {
pub fn new(recent_event_limit: usize) -> Self {
Self { recent_event_limit }
}
pub fn build(
&self,
journal: &EventJournal,
final_state: SupervisorState,
shutdown_cause: Option<String>,
) -> RunSummary {
let recent_events = journal.recent(self.recent_event_limit);
let started_at_unix_nanos = started_at(&recent_events);
let finished_at_unix_nanos = finished_at(&recent_events);
let recent_failures = collect_failures(&recent_events);
RunSummary {
started_at_unix_nanos,
finished_at_unix_nanos,
shutdown_cause,
restart_count: count_restarts(&recent_events),
failure_count: recent_failures.len() as u64,
final_decision: last_decision(&recent_events),
recent_failures,
recent_events,
final_state,
}
}
}
impl Default for RunSummaryBuilder {
fn default() -> Self {
Self::new(32)
}
}
fn started_at(events: &[SupervisorEvent]) -> u128 {
events
.first()
.map(|event| event.when.time.unix_nanos)
.unwrap_or(0)
}
fn finished_at(events: &[SupervisorEvent]) -> u128 {
events
.last()
.map(|event| event.when.time.unix_nanos)
.unwrap_or(0)
}
fn collect_failures(events: &[SupervisorEvent]) -> Vec<TaskFailure> {
events
.iter()
.filter_map(|event| match &event.what {
What::ChildFailed { failure } => Some(failure.clone()),
_ => None,
})
.collect()
}
fn count_restarts(events: &[SupervisorEvent]) -> u64 {
events
.iter()
.filter(|event| matches!(event.what, What::ChildRestarted { .. }))
.count() as u64
}
fn last_decision(events: &[SupervisorEvent]) -> Option<PolicyDecision> {
events.iter().rev().find_map(|event| event.policy.clone())
}