Skip to content

Commit fc03837

Browse files
authored
Merge pull request Lissy93#15 from Lissy93/FEAT/update-check
[Feature] Adds update check
2 parents a393a89 + bb740ad commit fc03837

File tree

4 files changed

+86
-6
lines changed

4 files changed

+86
-6
lines changed

.github/CHANGELOG.md

-1
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
Fixes the units in Average Processing Time (seconds were incorrectly displayed as milliseconds) (#11)

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "adguardian"
3-
version = "1.5.0"
3+
version = "1.6.0"
44
edition = "2021"
55
authors = ["Alicia Sykes"]
66
description = "Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home instance "

src/welcome.rs

+84-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use reqwest::{Client, Error};
77
use colored::*;
88

99
use serde_json::Value;
10+
use serde::Deserialize;
1011
use semver::{Version};
1112

1213
/// Reusable function that just prints success messages to the console
@@ -31,7 +32,7 @@ fn print_ascii_art() {
3132
print_info(art, false);
3233
print_info("\nWelcome to AdGuardian Terminal Edition!", false);
3334
print_info("Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home instance", true);
34-
print_info("For documentation and support, please visit: https://github.com/lissy93/adguardian-term\n", true);
35+
print_info("For documentation and support, please visit: https://github.com/lissy93/adguardian-term", true);
3536
}
3637

3738
/// Print error message, along with (optional) stack trace, then exit
@@ -151,9 +152,83 @@ async fn verify_connection(
151152
}
152153
}
153154

155+
#[derive(Deserialize)]
156+
struct CratesIoResponse {
157+
#[serde(rename = "crate")]
158+
krate: Crate,
159+
}
160+
161+
#[derive(Deserialize)]
162+
struct Crate {
163+
max_version: String,
164+
}
165+
166+
/// Gets the latest version of the crate from crates.io
167+
async fn get_latest_version(crate_name: &str) -> Result<String, Box<dyn std::error::Error>> {
168+
let url = format!("https://crates.io/api/v1/crates/{}", crate_name);
169+
let client = reqwest::Client::new();
170+
let res = client.get(&url)
171+
.header(reqwest::header::USER_AGENT, "version_check (adguardian.as93.net)")
172+
.send()
173+
.await?;
174+
175+
if res.status().is_success() {
176+
let response: CratesIoResponse = res.json().await?;
177+
Ok(response.krate.max_version)
178+
} else {
179+
let status = res.status();
180+
let body = res.text().await?;
181+
Err(format!("Request failed with status {}: body: {}", status, body).into())
182+
}
183+
}
184+
185+
/// Checks for updates to the crate, and prints a message if an update is available
186+
async fn check_for_updates() {
187+
// Get crate name and version from Cargo.toml
188+
let crate_name = env!("CARGO_PKG_NAME");
189+
let crate_version = env!("CARGO_PKG_VERSION");
190+
println!("{}", "\nChecking for updates...".blue());
191+
// Parse the current version, and fetch and parse the latest version
192+
let current_version = Version::parse(crate_version).unwrap_or_else(|_| {
193+
Version::parse("0.0.0").unwrap()
194+
});
195+
let latest_version = Version::parse(
196+
&get_latest_version(crate_name).await.unwrap_or_else(|_| {
197+
"0.0.0".to_string()
198+
})
199+
).unwrap();
200+
201+
// Compare the current and latest versions, and print the appropriate message
202+
if current_version == Version::parse("0.0.0").unwrap() || latest_version == Version::parse("0.0.0").unwrap() {
203+
println!("{}", "Unable to check for updates".yellow());
204+
} else if current_version < latest_version {
205+
println!("{}",
206+
format!(
207+
"A new version of AdGuardian is available.\nUpdate from {} to {} for the best experience",
208+
current_version.to_string().bold(),
209+
latest_version.to_string().bold()
210+
).yellow()
211+
);
212+
} else if current_version == latest_version {
213+
println!(
214+
"{}",
215+
format!("AdGuardian is up-to-date, running version {}", current_version.to_string().bold()).green()
216+
);
217+
} else if current_version > latest_version {
218+
println!(
219+
"{}",
220+
format!("Running a pre-released edition of AdGuardian, version {}", current_version.to_string().bold()).green()
221+
);
222+
} else {
223+
println!("{}", "Unable to check for updates".yellow());
224+
}
225+
}
226+
227+
154228
/// Initiate the welcome script
155229
/// This function will:
156230
/// - Print the AdGuardian ASCII art
231+
/// - Check if there's an update available
157232
/// - Check for the required environmental variables
158233
/// - Prompt the user to enter any missing variables
159234
/// - Verify the connection to the AdGuard instance
@@ -162,7 +237,11 @@ async fn verify_connection(
162237
/// - Then either print a success message, or show instructions to fix and exit
163238
pub async fn welcome() -> Result<(), Box<dyn std::error::Error>> {
164239
print_ascii_art();
165-
println!("{}", "Starting initialization checks...".blue());
240+
241+
// Check for updates
242+
check_for_updates().await;
243+
244+
println!("{}", "\nStarting initialization checks...".blue());
166245

167246
let client = Client::new();
168247

@@ -214,5 +293,7 @@ pub async fn welcome() -> Result<(), Box<dyn std::error::Error>> {
214293
let password = get_env("ADGUARD_PASSWORD")?;
215294

216295
// Verify that we can connect, authenticate, and that version is supported (exit on failure)
217-
verify_connection(&client, ip, port, protocol, username, password).await
296+
verify_connection(&client, ip, port, protocol, username, password).await?;
297+
298+
Ok(())
218299
}

0 commit comments

Comments
 (0)