use std::time::Duration;
const GITHUB_API_URL: &str = "https://api.github.com/repos/affromero/gitpane/releases/latest";
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub(crate) fn check_latest() -> Option<String> {
let agent = ureq::Agent::new_with_config(
ureq::config::Config::builder()
.timeout_connect(Some(Duration::from_secs(5)))
.timeout_recv_body(Some(Duration::from_secs(10)))
.build(),
);
let body = agent
.get(GITHUB_API_URL)
.header("Accept", "application/vnd.github.v3+json")
.header("User-Agent", concat!("gitpane/", env!("CARGO_PKG_VERSION")))
.call()
.ok()?
.body_mut()
.read_to_string()
.ok()?;
let body: serde_json::Value = serde_json::from_str(&body).ok()?;
let tag = body.get("tag_name")?.as_str()?;
let remote = parse_semver(tag)?;
let local = parse_semver(CURRENT_VERSION)?;
if remote > local {
Some(tag.trim_start_matches('v').to_string())
} else {
None
}
}
fn parse_semver(s: &str) -> Option<(u32, u32, u32)> {
let s = s.trim_start_matches('v');
let mut parts = s.splitn(3, '.');
let major = parts.next()?.parse().ok()?;
let minor = parts.next()?.parse().ok()?;
let patch = parts.next()?.parse().ok()?;
Some((major, minor, patch))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detects_newer_version() {
let remote = parse_semver("v1.0.0").unwrap();
let local = parse_semver("0.2.0").unwrap();
assert!(remote > local);
}
#[test]
fn test_same_version_returns_none() {
let remote = parse_semver("0.2.0").unwrap();
let local = parse_semver("0.2.0").unwrap();
assert!(remote <= local);
}
#[test]
fn test_older_remote_returns_none() {
let remote = parse_semver("0.1.0").unwrap();
let local = parse_semver("0.2.0").unwrap();
assert!(remote <= local);
}
#[test]
fn test_handles_v_prefix() {
assert_eq!(parse_semver("v1.2.3"), Some((1, 2, 3)));
assert_eq!(parse_semver("1.2.3"), Some((1, 2, 3)));
}
#[test]
fn test_handles_malformed_tag() {
assert_eq!(parse_semver("not-a-version"), None);
assert_eq!(parse_semver("1.2"), None);
assert_eq!(parse_semver(""), None);
}
}