Skip to main content

pr_bro/version_check/
mod.rs

1pub mod cache;
2pub mod checker;
3
4use std::time::{SystemTime, UNIX_EPOCH};
5
6/// Status of version check result
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum VersionStatus {
9    /// A newer version is available
10    UpdateAvailable { current: String, latest: String },
11    /// Current version is up to date
12    UpToDate,
13    /// Version check failed or was skipped
14    Unknown,
15}
16
17/// Check for available updates
18///
19/// This function checks the GitHub Releases API for newer versions.
20/// Results are cached for 24 hours. Dismissed versions are not shown.
21/// All errors fail silently and return Unknown.
22pub async fn check_version(token: &str, current_version: &str) -> VersionStatus {
23    let cache_path = crate::github::cache::get_cache_path();
24
25    // Try to load from cache first
26    if let Some(cached) = cache::read_cached_version(&cache_path) {
27        if cache::is_cache_fresh(&cached) {
28            // Check if this version was dismissed
29            if let Some(dismissed) = cache::read_dismissed_version(&cache_path) {
30                if dismissed == cached.latest_version {
31                    return VersionStatus::UpToDate; // Suppress dismissed version
32                }
33            }
34
35            // Check if cached version is newer
36            if checker::is_newer(current_version, &cached.latest_version) {
37                return VersionStatus::UpdateAvailable {
38                    current: current_version.to_string(),
39                    latest: cached.latest_version,
40                };
41            } else {
42                return VersionStatus::UpToDate;
43            }
44        }
45    }
46
47    // No fresh cache, fetch from API
48    let latest_version = match checker::fetch_latest_version(token).await {
49        Ok(version) => version,
50        Err(_) => return VersionStatus::Unknown, // Fail silently
51    };
52
53    // Write to cache
54    let now = SystemTime::now()
55        .duration_since(UNIX_EPOCH)
56        .unwrap()
57        .as_secs();
58
59    let cached_info = cache::CachedVersionInfo {
60        latest_version: latest_version.clone(),
61        checked_at: now,
62    };
63
64    // Ignore cache write errors
65    let _ = cache::write_cached_version(&cache_path, &cached_info);
66
67    // Check if dismissed
68    if let Some(dismissed) = cache::read_dismissed_version(&cache_path) {
69        if dismissed == latest_version {
70            return VersionStatus::UpToDate; // Suppress dismissed version
71        }
72    }
73
74    // Compare versions
75    if checker::is_newer(current_version, &latest_version) {
76        VersionStatus::UpdateAvailable {
77            current: current_version.to_string(),
78            latest: latest_version,
79        }
80    } else {
81        VersionStatus::UpToDate
82    }
83}
84
85/// Dismiss a specific version so it won't be shown in the update banner
86pub fn dismiss_version(version: &str) {
87    let cache_path = crate::github::cache::get_cache_path();
88    // Ignore errors - this is best-effort
89    let _ = cache::write_dismissed_version(&cache_path, version);
90}
91
92/// Load cached status without making an API call
93///
94/// Useful for showing update banner immediately on startup if cache is fresh.
95/// Returns Unknown if no fresh cache exists.
96pub fn load_cached_status(current_version: &str) -> VersionStatus {
97    let cache_path = crate::github::cache::get_cache_path();
98
99    let cached = match cache::read_cached_version(&cache_path) {
100        Some(c) if cache::is_cache_fresh(&c) => c,
101        _ => return VersionStatus::Unknown,
102    };
103
104    // Check if dismissed
105    if let Some(dismissed) = cache::read_dismissed_version(&cache_path) {
106        if dismissed == cached.latest_version {
107            return VersionStatus::UpToDate;
108        }
109    }
110
111    // Compare versions
112    if checker::is_newer(current_version, &cached.latest_version) {
113        VersionStatus::UpdateAvailable {
114            current: current_version.to_string(),
115            latest: cached.latest_version,
116        }
117    } else {
118        VersionStatus::UpToDate
119    }
120}