use std::time::Duration;
use anyhow::{anyhow, Context, Result};
use serde::Deserialize;
use super::platform::latest_url;
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LatestPointer {
#[serde(default)]
pub schema_version: u32,
pub version: String,
}
pub fn fetch_latest(timeout: Duration) -> Result<LatestPointer> {
let url = latest_url();
let client = reqwest::blocking::Client::builder()
.timeout(timeout)
.user_agent(format!("a4/{}", env!("CARGO_PKG_VERSION")))
.build()
.context("Failed to build HTTP client")?;
let response = client
.get(&url)
.send()
.with_context(|| format!("Failed to fetch {url}"))?;
if !response.status().is_success() {
return Err(anyhow!("{url} returned HTTP {}", response.status()));
}
let pointer: LatestPointer = response
.json()
.with_context(|| format!("{url} is not a valid latest.json"))?;
semver::Version::parse(&pointer.version)
.with_context(|| format!("{url} carries a non-semver version {:?}", pointer.version))?;
Ok(pointer)
}
pub fn is_newer(latest: &str, current: &str) -> Option<bool> {
let latest = semver::Version::parse(latest).ok()?;
let current = semver::Version::parse(current).ok()?;
Some(latest > current)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn newer_detection_handles_prerelease_and_garbage() {
assert_eq!(is_newer("0.14.0", "0.13.0"), Some(true));
assert_eq!(is_newer("0.13.0", "0.13.0"), Some(false));
assert_eq!(is_newer("0.13.0", "0.14.0"), Some(false));
assert_eq!(is_newer("0.13.0", "0.13.0-rc.1"), Some(true));
assert_eq!(is_newer("latest", "0.13.0"), None);
}
}