rustpm 0.1.0

A fast, friendly APT frontend with kernel, desktop, and sources management
use semver::Version;
use serde::Deserialize;

const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");

#[derive(Debug)]
pub struct UpdateInfo {
    pub current: Version,
    pub latest: Version,
    pub url: String,
}

// crates.io API response for a crate's latest version
#[derive(Deserialize)]
struct CratesIoResponse {
    #[serde(rename = "crate")]
    krate: CratesIoCrate,
}

#[derive(Deserialize)]
struct CratesIoCrate {
    newest_version: String,
}

pub fn check_for_update(_check_url: &str) -> Option<UpdateInfo> {
    let current = Version::parse(CURRENT_VERSION).ok()?;

    // crates.io API requires a User-Agent header identifying the app
    let resp = ureq::get("https://crates.io/api/v1/crates/rustpm")
        .set("User-Agent", &format!("rustpm/{} (https://gitlab.com/dfred26/rustpm)", CURRENT_VERSION))
        .timeout(std::time::Duration::from_secs(3))
        .call()
        .ok()?;

    let data: CratesIoResponse = resp.into_json().ok()?;
    let latest = Version::parse(&data.krate.newest_version).ok()?;

    if latest > current {
        Some(UpdateInfo {
            current,
            latest,
            url: "https://crates.io/crates/rustpm".to_string(),
        })
    } else {
        None
    }
}

/// Print an update notice if a newer version is available on crates.io.
/// Silently does nothing on any error.
pub fn print_update_notice(check_url: &str) {
    if let Some(info) = check_for_update(check_url) {
        eprintln!(
            "\n  rustpm {} is available (you have {}). Run: cargo install rustpm",
            info.latest, info.current
        );
    }
}