use serde::Deserialize;
use std::env;
const GITHUB_API_URL: &str = "https://api.github.com/repos/pie-314/trx/releases/latest";
const USER_AGENT: &str = "trx-updater";
#[derive(Deserialize, Debug)]
struct Release {
tag_name: String,
assets: Vec<Asset>,
}
#[derive(Deserialize, Debug)]
struct Asset {
name: String,
browser_download_url: String,
}
pub fn check_for_updates() -> Option<(String, String)> {
let client = reqwest::blocking::Client::builder()
.user_agent(USER_AGENT)
.timeout(std::time::Duration::from_secs(2))
.build()
.ok()?;
let response = client.get(GITHUB_API_URL).send().ok()?;
let release: Release = response.json().ok()?;
let current_version = env!("CARGO_PKG_VERSION");
let latest_version = release.tag_name.trim_start_matches('v');
if is_newer(latest_version, current_version) {
let target_asset_name = match (env::consts::OS, env::consts::ARCH) {
("linux", "x86_64") => "trx-linux-x86_64.tar.gz",
("macos", "x86_64") => "trx-macos-x86_64.tar.gz",
("macos", "aarch64") => "trx-macos-aarch64.tar.gz",
_ => return None,
};
let asset = release
.assets
.iter()
.find(|a| a.name == target_asset_name)?;
Some((
latest_version.to_string(),
asset.browser_download_url.clone(),
))
} else {
None
}
}
fn is_newer(latest: &str, current: &str) -> bool {
let latest_parts: Vec<u32> = latest.split('.').filter_map(|s| s.parse().ok()).collect();
let current_parts: Vec<u32> = current.split('.').filter_map(|s| s.parse().ok()).collect();
for (l, c) in latest_parts.iter().zip(current_parts.iter()) {
if l > c {
return true;
}
if l < c {
return false;
}
}
latest_parts.len() > current_parts.len()
}
pub fn update_self(url: &str) -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::blocking::Client::builder()
.user_agent(USER_AGENT)
.build()?;
let response = client.get(url).send()?;
let bytes = response.bytes()?;
let tar_gz = flate2::read::GzDecoder::new(&bytes[..]);
let mut archive = tar::Archive::new(tar_gz);
let temp_dir = tempfile::tempdir()?;
let mut exe_path = None;
for entry in archive.entries()? {
let mut entry = entry?;
let path = entry.path()?.to_path_buf();
let is_binary = path.file_name().and_then(|s| s.to_str()) == Some("trx") ||
path.file_name().and_then(|s| s.to_str()) == Some("trx.exe");
if is_binary {
let target_path = temp_dir.path().join(path.file_name().unwrap());
entry.unpack(&target_path)?;
exe_path = Some(target_path);
break;
}
}
let exe_path = exe_path.ok_or("Could not find binary in release archive")?;
self_replace::self_replace(&exe_path)?;
Ok(())
}