iris-chat 0.1.24

Iris Chat command line client and shared encrypted chat core
Documentation
use std::time::{Duration, Instant};

/// Core-owned cadence for app update checks.
///
/// Platform shells still perform the actual fetch/download/install because
/// those paths are native, but they should ask this policy before starting an
/// automatic check so startup and timed polling do not drift per platform.
#[derive(Debug, Clone)]
pub struct UpdateAutoCheckPolicy {
    interval: Duration,
    startup_check_done: bool,
    last_check_started_at: Option<Instant>,
}

impl UpdateAutoCheckPolicy {
    #[must_use]
    pub fn new(interval: Duration) -> Self {
        Self {
            interval,
            startup_check_done: false,
            last_check_started_at: None,
        }
    }

    #[must_use]
    pub fn should_start_check(&mut self, enabled: bool, now: Instant) -> bool {
        if !enabled {
            return false;
        }
        if !self.startup_check_done {
            self.startup_check_done = true;
            self.last_check_started_at = Some(now);
            return true;
        }
        if self
            .last_check_started_at
            .is_some_and(|last| now.duration_since(last) >= self.interval)
        {
            self.last_check_started_at = Some(now);
            return true;
        }
        false
    }

    pub fn note_manual_check_started(&mut self, now: Instant) {
        self.last_check_started_at = Some(now);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn startup_check_fires_once_when_enabled() {
        let start = Instant::now();
        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));

        assert!(policy.should_start_check(true, start));
        assert!(!policy.should_start_check(true, start + Duration::from_secs(59)));
    }

    #[test]
    fn interval_check_fires_after_cadence() {
        let start = Instant::now();
        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));

        assert!(policy.should_start_check(true, start));
        assert!(!policy.should_start_check(true, start + Duration::from_secs(59)));
        assert!(policy.should_start_check(true, start + Duration::from_secs(60)));
    }

    #[test]
    fn disabled_auto_check_never_fires() {
        let start = Instant::now();
        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));

        assert!(!policy.should_start_check(false, start));
    }

    #[test]
    fn manual_check_resets_interval() {
        let start = Instant::now();
        let mut policy = UpdateAutoCheckPolicy::new(Duration::from_secs(60));

        assert!(policy.should_start_check(true, start));
        policy.note_manual_check_started(start + Duration::from_secs(50));
        assert!(!policy.should_start_check(true, start + Duration::from_secs(100)));
        assert!(policy.should_start_check(true, start + Duration::from_secs(110)));
    }
}