use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::time::Duration;
#[derive(Debug, Clone)]
pub struct IntensitasRestitutio {
max_restarts: u32,
window_secs: u64,
restart_times: Vec<u64>,
}
impl IntensitasRestitutio {
pub fn new(max_restarts: u32, window_secs: u64) -> Self {
IntensitasRestitutio {
max_restarts,
window_secs,
restart_times: Vec::with_capacity(max_restarts as usize),
}
}
#[inline]
pub fn record_restart(&mut self, current_time: u64) -> bool {
let cutoff = current_time.saturating_sub(self.window_secs);
self.restart_times.retain(|&t| t >= cutoff);
if self.restart_times.len() >= self.max_restarts as usize {
return false;
}
self.restart_times.push(current_time);
true
}
#[inline]
pub fn current_count(&self) -> usize {
self.restart_times.len()
}
#[inline]
pub fn window_secs(&self) -> u64 {
self.window_secs
}
#[inline]
pub fn reset(&mut self) {
self.restart_times.clear();
}
}
impl Default for IntensitasRestitutio {
fn default() -> Self {
Self::new(3, 60)
}
}
#[derive(Debug, Clone)]
pub enum StrategiaSupervisionis {
UnusProUno {
intensity: IntensitasRestitutio,
},
OmnesProUno {
intensity: IntensitasRestitutio,
},
ReliquiProUno {
intensity: IntensitasRestitutio,
},
Simplex,
Proprius {
name: String,
max_restarts: u32,
},
}
impl StrategiaSupervisionis {
pub fn unus_pro_uno(max_restarts: u32, window: Duration) -> Self {
StrategiaSupervisionis::UnusProUno {
intensity: IntensitasRestitutio::new(max_restarts, window.as_secs()),
}
}
pub fn omnes_pro_uno(max_restarts: u32, window: Duration) -> Self {
StrategiaSupervisionis::OmnesProUno {
intensity: IntensitasRestitutio::new(max_restarts, window.as_secs()),
}
}
pub fn reliqui_pro_uno(max_restarts: u32, window: Duration) -> Self {
StrategiaSupervisionis::ReliquiProUno {
intensity: IntensitasRestitutio::new(max_restarts, window.as_secs()),
}
}
pub fn simplex() -> Self {
StrategiaSupervisionis::Simplex
}
pub fn proprius(name: impl Into<String>, max_restarts: u32) -> Self {
StrategiaSupervisionis::Proprius {
name: name.into(),
max_restarts,
}
}
#[inline]
pub fn window_secs(&self) -> Option<u64> {
match self {
StrategiaSupervisionis::UnusProUno { intensity }
| StrategiaSupervisionis::OmnesProUno { intensity }
| StrategiaSupervisionis::ReliquiProUno { intensity } => Some(intensity.window_secs()),
StrategiaSupervisionis::Simplex | StrategiaSupervisionis::Proprius { .. } => None,
}
}
#[inline]
pub fn name(&self) -> &str {
match self {
StrategiaSupervisionis::UnusProUno { .. } => "UnusProUno",
StrategiaSupervisionis::OmnesProUno { .. } => "OmnesProUno",
StrategiaSupervisionis::ReliquiProUno { .. } => "ReliquiProUno",
StrategiaSupervisionis::Simplex => "Simplex",
StrategiaSupervisionis::Proprius { name, .. } => name,
}
}
pub fn decide(
&mut self,
failed_child_index: usize,
total_children: usize,
current_time: u64,
) -> RestartDecision {
match self {
StrategiaSupervisionis::UnusProUno { intensity } => {
if intensity.record_restart(current_time) {
RestartDecision::Restart(vec![failed_child_index])
} else {
RestartDecision::Escalate
}
}
StrategiaSupervisionis::OmnesProUno { intensity } => {
if intensity.record_restart(current_time) {
RestartDecision::Restart((0..total_children).collect())
} else {
RestartDecision::Escalate
}
}
StrategiaSupervisionis::ReliquiProUno { intensity } => {
if intensity.record_restart(current_time) {
RestartDecision::Restart((failed_child_index..total_children).collect())
} else {
RestartDecision::Escalate
}
}
StrategiaSupervisionis::Simplex => RestartDecision::Restart(vec![failed_child_index]),
StrategiaSupervisionis::Proprius { max_restarts, .. } => {
if *max_restarts > 0 {
*max_restarts -= 1;
RestartDecision::Restart(vec![failed_child_index])
} else {
RestartDecision::Escalate
}
}
}
}
}
impl Default for StrategiaSupervisionis {
fn default() -> Self {
Self::unus_pro_uno(3, Duration::from_mins(1))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RestartDecision {
Restart(Vec<usize>),
Escalate,
Terminate,
Ignore,
}
impl RestartDecision {
#[inline]
pub fn is_restart(&self) -> bool {
matches!(self, RestartDecision::Restart(_))
}
#[inline]
pub fn is_escalate(&self) -> bool {
matches!(self, RestartDecision::Escalate)
}
#[inline]
pub fn children_to_restart(&self) -> Option<&[usize]> {
match self {
RestartDecision::Restart(children) => Some(children),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ModusRestitutio {
#[default]
Permanens,
Transiens,
Temporarius,
}
impl ModusRestitutio {
#[inline]
pub fn should_restart(&self, normal_exit: bool) -> bool {
match self {
ModusRestitutio::Permanens => true,
ModusRestitutio::Transiens => !normal_exit,
ModusRestitutio::Temporarius => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OrdoTerminatio {
#[default]
Primus,
Ultimus,
Simul,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intensity_within_limit() {
let mut intensity = IntensitasRestitutio::new(3, 60);
assert!(intensity.record_restart(100));
assert!(intensity.record_restart(110));
assert!(intensity.record_restart(120));
assert!(!intensity.record_restart(130)); }
#[test]
fn test_intensity_window_expiry() {
let mut intensity = IntensitasRestitutio::new(3, 60);
assert!(intensity.record_restart(100));
assert!(intensity.record_restart(110));
assert!(intensity.record_restart(120));
assert!(!intensity.record_restart(130));
assert!(intensity.record_restart(200)); }
#[test]
fn test_strategy_unus_pro_uno() {
let mut strategy = StrategiaSupervisionis::unus_pro_uno(2, Duration::from_secs(60));
let decision = strategy.decide(1, 3, 100);
assert_eq!(decision, RestartDecision::Restart(vec![1]));
let decision = strategy.decide(1, 3, 110);
assert_eq!(decision, RestartDecision::Restart(vec![1]));
let decision = strategy.decide(1, 3, 120);
assert_eq!(decision, RestartDecision::Escalate);
}
#[test]
fn test_strategy_omnes_pro_uno() {
let mut strategy = StrategiaSupervisionis::omnes_pro_uno(1, Duration::from_secs(60));
let decision = strategy.decide(1, 3, 100);
assert_eq!(decision, RestartDecision::Restart(vec![0, 1, 2]));
}
#[test]
fn test_strategy_reliqui_pro_uno() {
let mut strategy = StrategiaSupervisionis::reliqui_pro_uno(1, Duration::from_secs(60));
let decision = strategy.decide(1, 4, 100);
assert_eq!(decision, RestartDecision::Restart(vec![1, 2, 3]));
}
#[test]
fn test_modus_restitutio() {
assert!(ModusRestitutio::Permanens.should_restart(true));
assert!(ModusRestitutio::Permanens.should_restart(false));
assert!(!ModusRestitutio::Transiens.should_restart(true));
assert!(ModusRestitutio::Transiens.should_restart(false));
assert!(!ModusRestitutio::Temporarius.should_restart(true));
assert!(!ModusRestitutio::Temporarius.should_restart(false));
}
}