Skip to main content

iris_chat_core/
update_policy.rs

1use std::time::{Duration, Instant};
2
3/// Core-owned cadence for app update checks.
4///
5/// Platform shells still perform the actual fetch/download/install because
6/// those paths are native, but they should ask this policy before starting an
7/// automatic check so startup and timed polling do not drift per platform.
8#[derive(Debug, Clone)]
9pub struct UpdateAutoCheckPolicy {
10    interval: Duration,
11    startup_check_done: bool,
12    last_check_started_at: Option<Instant>,
13}
14
15impl UpdateAutoCheckPolicy {
16    #[must_use]
17    pub fn new(interval: Duration) -> Self {
18        Self {
19            interval,
20            startup_check_done: false,
21            last_check_started_at: None,
22        }
23    }
24
25    #[must_use]
26    pub fn should_start_check(&mut self, enabled: bool, now: Instant) -> bool {
27        if !enabled {
28            return false;
29        }
30        if !self.startup_check_done {
31            self.startup_check_done = true;
32            self.last_check_started_at = Some(now);
33            return true;
34        }
35        if self
36            .last_check_started_at
37            .is_some_and(|last| now.duration_since(last) >= self.interval)
38        {
39            self.last_check_started_at = Some(now);
40            return true;
41        }
42        false
43    }
44
45    pub fn note_manual_check_started(&mut self, now: Instant) {
46        self.last_check_started_at = Some(now);
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn startup_check_fires_once_when_enabled() {
56        let start = Instant::now();
57        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));
58
59        assert!(policy.should_start_check(true, start));
60        assert!(!policy.should_start_check(true, start + Duration::from_secs(59)));
61    }
62
63    #[test]
64    fn interval_check_fires_after_cadence() {
65        let start = Instant::now();
66        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));
67
68        assert!(policy.should_start_check(true, start));
69        assert!(!policy.should_start_check(true, start + Duration::from_secs(59)));
70        assert!(policy.should_start_check(true, start + Duration::from_secs(60)));
71    }
72
73    #[test]
74    fn disabled_auto_check_never_fires() {
75        let start = Instant::now();
76        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));
77
78        assert!(!policy.should_start_check(false, start));
79    }
80
81    #[test]
82    fn manual_check_resets_interval() {
83        let start = Instant::now();
84        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));
85
86        assert!(policy.should_start_check(true, start));
87        policy.note_manual_check_started(start + Duration::from_secs(50));
88        assert!(!policy.should_start_check(true, start + Duration::from_secs(100)));
89        assert!(policy.should_start_check(true, start + Duration::from_secs(110)));
90    }
91}