use log::error;
use std::time::{Duration, Instant};
use std::sync::{Arc, Mutex};
use super::{notify::Notify, monitor::{Monitor, Action}};
struct Entry<'a> {
fire_message: String,
recover_message: String,
last_checked: Option<Instant>,
wait_time: Duration,
monitor: &'a mut dyn Monitor,
notifier: &'a dyn Notify,
has_fired: bool,
}
#[must_use]
pub struct Schedule<'a> {
entries: Vec<Arc<Mutex<Entry<'a>>>>,
}
impl Default for Schedule<'_> {
fn default() -> Self {
Schedule {
entries: Vec::new(),
}
}
}
impl<'a> Schedule<'a> {
pub fn add<M: Monitor, N: Notify>(
&mut self,
fire_message: &str,
recover_message: &str,
wait_time: Duration,
monitor: &'a mut M,
notifier: &'a N
) {
self.entries.push(Arc::new(Mutex::new( Entry {
fire_message: fire_message.to_string(),
recover_message: recover_message.to_string(),
last_checked: None,
wait_time,
monitor,
notifier,
has_fired: false,
})));
}
pub fn run(&mut self) {
rayon::scope(|s| {
for entry in &mut self.entries {
s.spawn(|_| {
let mut entry = entry.lock().unwrap();
handle_entry(&mut entry);
});
}
});
}
}
fn handle_entry(entry: &mut Entry) {
if entry.last_checked.is_none() || entry.last_checked.unwrap().elapsed() >= entry.wait_time {
entry.last_checked = Some(Instant::now());
match entry.monitor.check() {
Action::Update { message } => {
if let Err(e) = entry.notifier.send(&entry.fire_message, message) {
error!("{e}");
}
},
Action::Notify { diagnostic } => {
if !entry.has_fired {
if let Err(e) = entry.notifier.send(&entry.fire_message, diagnostic) {
error!("{e}");
} else {
entry.has_fired = true;
}
}
},
Action::Nothing => {
if entry.has_fired {
if let Err(e) = entry.notifier.send(&entry.recover_message, None) {
error!("{e}");
} else {
entry.has_fired = false;
}
}
},
}
}
}