aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/src/commands/self_update.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-22 11:07:42 +0900
committernsfisis <nsfisis@gmail.com>2026-02-22 11:15:29 +0900
commit9f0d210021c54f63c9984446862b6ec68834bc63 (patch)
treed1522b8047c60bc7ee7a9d832178dd24e1b07636 /crates/mozart/src/commands/self_update.rs
parent2c243a3cb814939bbe40fda1608781825ab0d77d (diff)
downloadphp-mozart-9f0d210021c54f63c9984446862b6ec68834bc63.tar.gz
php-mozart-9f0d210021c54f63c9984446862b6ec68834bc63.tar.zst
php-mozart-9f0d210021c54f63c9984446862b6ec68834bc63.zip
refactor(async): migrate from blocking HTTP to async/await with tokio
Replace reqwest::blocking with async reqwest across the entire codebase. All command execute functions, registry API calls (packagist, downloader, resolver, lockfile), and the main entry point now use async/await with the tokio runtime. The pubgrub resolver runs on spawn_blocking since its DependencyProvider trait is synchronous, using Handle::block_on for async I/O within that context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src/commands/self_update.rs')
-rw-r--r--crates/mozart/src/commands/self_update.rs47
1 files changed, 26 insertions, 21 deletions
diff --git a/crates/mozart/src/commands/self_update.rs b/crates/mozart/src/commands/self_update.rs
index 03d2643..9f6a7fe 100644
--- a/crates/mozart/src/commands/self_update.rs
+++ b/crates/mozart/src/commands/self_update.rs
@@ -1,5 +1,5 @@
use clap::Args;
-use std::io::{Read, Write};
+use std::io::Write;
use std::path::{Path, PathBuf};
// ─── CLI args ─────────────────────────────────────────────────────────────────
@@ -50,7 +50,7 @@ const BACKUP_EXTENSION: &str = ".old";
// ─── Public entry point ───────────────────────────────────────────────────────
-pub fn execute(
+pub async fn execute(
args: &SelfUpdateArgs,
_cli: &super::Cli,
_console: &mozart_core::console::Console,
@@ -69,7 +69,7 @@ pub fn execute(
if args.rollback {
rollback(&current_exe, &data_dir)
} else {
- update(args, &current_exe, &data_dir)
+ update(args, &current_exe, &data_dir).await
}
}
@@ -128,10 +128,10 @@ fn platform_asset_name() -> anyhow::Result<String> {
// ─── GitHub fetching ──────────────────────────────────────────────────────────
-fn fetch_releases(include_prerelease: bool) -> anyhow::Result<Vec<GitHubRelease>> {
+async fn fetch_releases(include_prerelease: bool) -> anyhow::Result<Vec<GitHubRelease>> {
let url = format!("{GITHUB_API_BASE}/{GITHUB_REPO}/releases");
- let client = reqwest::blocking::Client::builder()
+ let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.user_agent(concat!("mozart/", env!("CARGO_PKG_VERSION")))
.build()
@@ -140,6 +140,7 @@ fn fetch_releases(include_prerelease: bool) -> anyhow::Result<Vec<GitHubRelease>
let response = client
.get(&url)
.send()
+ .await
.map_err(|e| anyhow::anyhow!("Could not fetch releases from GitHub: {e}"))?;
if !response.status().is_success() {
@@ -151,6 +152,7 @@ fn fetch_releases(include_prerelease: bool) -> anyhow::Result<Vec<GitHubRelease>
let mut releases: Vec<GitHubRelease> = response
.json()
+ .await
.map_err(|e| anyhow::anyhow!("Could not parse GitHub releases response: {e}"))?;
if !include_prerelease {
@@ -203,16 +205,21 @@ fn find_asset<'a>(release: &'a GitHubRelease, asset_name: &str) -> anyhow::Resul
// ─── Download ─────────────────────────────────────────────────────────────────
-fn download_asset(asset: &GitHubAsset, dest: &Path, show_progress: bool) -> anyhow::Result<()> {
- let client = reqwest::blocking::Client::builder()
+async fn download_asset(
+ asset: &GitHubAsset,
+ dest: &Path,
+ show_progress: bool,
+) -> anyhow::Result<()> {
+ let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(300))
.user_agent(concat!("mozart/", env!("CARGO_PKG_VERSION")))
.build()
.map_err(|e| anyhow::anyhow!("Could not build HTTP client: {e}"))?;
- let mut response = client
+ let response = client
.get(&asset.browser_download_url)
.send()
+ .await
.map_err(|e| anyhow::anyhow!("Could not download asset: {e}"))?;
if !response.status().is_success() {
@@ -228,18 +235,16 @@ fn download_asset(asset: &GitHubAsset, dest: &Path, show_progress: bool) -> anyh
let total_bytes = asset.size;
let mut downloaded: u64 = 0;
- let mut buf = [0u8; 8192];
+ let mut stream = response;
- loop {
- let n = response
- .read(&mut buf)
- .map_err(|e| anyhow::anyhow!("Error reading download stream: {e}"))?;
- if n == 0 {
- break;
- }
- file.write_all(&buf[..n])
+ while let Some(chunk) = stream
+ .chunk()
+ .await
+ .map_err(|e| anyhow::anyhow!("Error reading download stream: {e}"))?
+ {
+ file.write_all(&chunk)
.map_err(|e| anyhow::anyhow!("Error writing to destination file: {e}"))?;
- downloaded += n as u64;
+ downloaded += chunk.len() as u64;
if show_progress && total_bytes > 0 {
let pct = (downloaded * 100) / total_bytes;
@@ -257,13 +262,13 @@ fn download_asset(asset: &GitHubAsset, dest: &Path, show_progress: bool) -> anyh
// ─── Core update flow ─────────────────────────────────────────────────────────
-fn update(args: &SelfUpdateArgs, current_exe: &Path, data_dir: &Path) -> anyhow::Result<()> {
+async fn update(args: &SelfUpdateArgs, current_exe: &Path, data_dir: &Path) -> anyhow::Result<()> {
let current_version = get_current_version();
println!("Updating Mozart...");
// Fetch releases
- let releases = fetch_releases(args.preview)?;
+ let releases = fetch_releases(args.preview).await?;
// Find target release
let target_release = find_target_release(&releases, args.version.as_deref())?;
@@ -298,7 +303,7 @@ fn update(args: &SelfUpdateArgs, current_exe: &Path, data_dir: &Path) -> anyhow:
.map_err(|e| anyhow::anyhow!("Could not create temporary file: {e}"))?;
let tmp_path = tmp.path().to_path_buf();
- download_asset(asset, &tmp_path, !args.no_progress)?;
+ download_asset(asset, &tmp_path, !args.no_progress).await?;
// Set executable permission on Unix
#[cfg(unix)]