use serde::Deserialize;
use std::time::Duration;
#[derive(Debug, Deserialize)]
struct CrateResponse {
#[serde(rename = "crate")]
crate_info: CrateInfo,
}
#[derive(Debug, Deserialize)]
struct CrateInfo {
newest_version: String,
}
pub async fn check_and_notify() {
let current_version = env!("CARGO_PKG_VERSION");
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(2))
.user_agent(format!("thru/{}", current_version))
.build();
let client = match client {
Ok(c) => c,
Err(_) => return, };
let response = client
.get("https://crates.io/api/v1/crates/thru")
.send()
.await;
let response = match response {
Ok(r) => r,
Err(_) => return, };
let crate_info: CrateResponse = match response.json().await {
Ok(info) => info,
Err(_) => return, };
let latest_version = crate_info.crate_info.newest_version;
if is_newer_version(&latest_version, current_version) {
print_update_notification(&latest_version);
}
}
fn is_newer_version(latest: &str, current: &str) -> bool {
let parse_version = |v: &str| -> Option<(u32, u32, u32)> {
let parts: Vec<&str> = v.split('.').collect();
if parts.len() != 3 {
return None;
}
let major = parts[0].parse::<u32>().ok()?;
let minor = parts[1].parse::<u32>().ok()?;
let patch = parts[2].parse::<u32>().ok()?;
Some((major, minor, patch))
};
let latest_parsed = match parse_version(latest) {
Some(v) => v,
None => return false,
};
let current_parsed = match parse_version(current) {
Some(v) => v,
None => return false,
};
latest_parsed > current_parsed
}
fn print_update_notification(new_version: &str) {
use colored::Colorize;
let current_version = env!("CARGO_PKG_VERSION");
let current_line_content = format!(" Current version: {}", current_version);
let current_padding = 49 - current_line_content.len();
let latest_line_content = format!(" Latest version: {}", new_version);
let latest_padding = 49 - latest_line_content.len();
eprintln!();
eprintln!("{}", "╭─────────────────────────────────────────────────╮".bright_yellow());
eprintln!("{}", "│ A new version of thru is available! │".bright_yellow());
eprintln!("{}{} {}{}{}",
"│".bright_yellow(),
" Current version:".bright_yellow(),
current_version.white(),
" ".repeat(current_padding),
"│".bright_yellow()
);
eprintln!("{}{} {}{}{}",
"│".bright_yellow(),
" Latest version:".bright_yellow(),
new_version.green().bold(),
" ".repeat(latest_padding),
"│".bright_yellow()
);
eprintln!("{}", "│ │".bright_yellow());
eprintln!("{}", "│ Update with: │".bright_yellow());
eprintln!("{}{}{}",
"│".bright_yellow(),
" cargo install thru ".bold(),
"│".bright_yellow()
);
eprintln!("{}", "╰─────────────────────────────────────────────────╯".bright_yellow());
eprintln!();
}
pub fn is_interactive() -> bool {
use std::io::IsTerminal;
std::io::stdout().is_terminal()
}