rustpm 0.2.2

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

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

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

#[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()?;

    let user_agent = format!("rustpm/{} (https://gitlab.com/dfred26/rustpm)", CURRENT_VERSION);
    let out = Command::new("curl")
        .args([
            "-fsSL", "--max-time", "3",
            "-A", &user_agent,
            "https://crates.io/api/v1/crates/rustpm",
        ])
        .output()
        .ok()?;

    if !out.status.success() {
        return None;
    }

    let data: CratesIoResponse = serde_json::from_slice(&out.stdout).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
        );
    }
}