use std::cmp::Ordering;
use anyhow::{Context, Result, anyhow};
use async_trait::async_trait;
use clap::Args;
use reqwest;
use semver::Version;
use crate::{
commands::{Runnable, RunnableInvokeRules},
context::AppContext,
log_cute, log_info,
util::logging::{BOLD, RESET},
};
#[derive(Args, Debug)]
pub struct CheckUpdateCmd;
#[async_trait]
impl Runnable for CheckUpdateCmd {
fn get_invoke_rules(&self) -> RunnableInvokeRules {
RunnableInvokeRules {
do_config_autosync: false,
require_sudo: false,
respect_lock: false,
}
}
async fn run(&self, _: &AppContext) -> Result<()> {
let current_version = env!("CARGO_PKG_VERSION");
log_info!("Current version: {current_version}",);
let url = "https://api.github.com/repos/machlit/cutler/releases/latest";
let client = reqwest::Client::builder()
.user_agent("cutler-update-check")
.build()
.with_context(|| "Failed to build request client".to_string())?;
let resp = client
.get(url)
.header("Accept", "application/vnd.github.v3+json")
.send()
.await
.with_context(|| format!("Failed to fetch latest GitHub release: {url}"))?;
let body = resp.text().await?;
let json: serde_json::Value = serde_json::from_str(&body)
.map_err(|e| anyhow!("Failed to parse GitHub API response: {e}"))?;
let latest_version = json
.get("tag_name")
.and_then(|v| v.as_str())
.or_else(|| json.get("name").and_then(|v| v.as_str()))
.map(|s| s.trim_start_matches('v').to_string())
.ok_or_else(|| anyhow!("Could not find latest version tag in GitHub API response"))?;
log_info!("Latest version: {latest_version}");
let current = Version::parse(current_version)
.with_context(|| "Could not parse current version".to_string())?;
let latest = Version::parse(&latest_version)
.with_context(|| "Could not parse latest version".to_string())?;
match current.cmp(&latest) {
Ordering::Less => {
println!(
r"
{BOLD}Update available:{RESET} {current_version} → {latest_version}
To update, run one of the following:
i. brew update && brew upgrade cutler # if installed via homebrew
ii. cargo install cutler --force # if installed via cargo
iii. mise up cutler # if installed via mise
iv. cutler self-update # for manual installs
Or download the latest release from:
https://github.com/machlit/cutler/releases"
);
}
Ordering::Equal => {
log_cute!("You are using the latest version.");
}
Ordering::Greater => {
log_cute!(
"You are on a development version ({current_version}) ahead of latest release ({latest_version})."
);
}
}
Ok(())
}
}