improv_core_model/
schedule.rs1use crate::{MeasureId, RefreshPolicy};
9use std::collections::HashMap;
10
11pub fn is_due(policy: RefreshPolicy, now_secs: u64, last_run: Option<u64>) -> bool {
19 match policy {
20 RefreshPolicy::Manual => false,
21 RefreshPolicy::OnLoad => last_run.is_none(),
22 RefreshPolicy::Interval { secs } => match last_run {
23 None => true,
24 Some(last) => now_secs.saturating_sub(last) >= secs,
25 },
26 }
27}
28
29pub fn due_measures(
33 policies: &HashMap<MeasureId, RefreshPolicy>,
34 now_secs: u64,
35 last_run: &HashMap<MeasureId, u64>,
36) -> Vec<MeasureId> {
37 let mut due: Vec<MeasureId> = policies
38 .iter()
39 .filter(|(id, &p)| is_due(p, now_secs, last_run.get(id).copied()))
40 .map(|(id, _)| *id)
41 .collect();
42 due.sort_by_key(|m| m.0);
43 due
44}
45
46#[cfg(test)]
47#[cfg_attr(coverage_nightly, coverage(off))]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn manual_is_never_due() {
53 assert!(!is_due(RefreshPolicy::Manual, 100, None));
54 assert!(!is_due(RefreshPolicy::Manual, 100, Some(0)));
55 }
56
57 #[test]
58 fn on_load_is_due_only_once() {
59 assert!(is_due(RefreshPolicy::OnLoad, 0, None));
60 assert!(!is_due(RefreshPolicy::OnLoad, 999, Some(0)));
61 }
62
63 #[test]
64 fn interval_respects_elapsed() {
65 let p = RefreshPolicy::Interval { secs: 30 };
66 assert!(is_due(p, 0, None)); assert!(!is_due(p, 20, Some(0))); assert!(is_due(p, 30, Some(0))); assert!(is_due(p, 61, Some(30))); }
71
72 #[test]
73 fn due_measures_filters_and_sorts() {
74 let mut policies = HashMap::new();
75 policies.insert(MeasureId(3), RefreshPolicy::OnLoad);
76 policies.insert(MeasureId(1), RefreshPolicy::Manual);
77 policies.insert(MeasureId(2), RefreshPolicy::Interval { secs: 10 });
78 let mut last = HashMap::new();
79 last.insert(MeasureId(2), 100u64); assert_eq!(due_measures(&policies, 105, &last), vec![MeasureId(3)]);
83 assert_eq!(
85 due_measures(&policies, 115, &last),
86 vec![MeasureId(2), MeasureId(3)]
87 );
88 }
89}