From 9f0d210021c54f63c9984446862b6ec68834bc63 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 22 Feb 2026 11:07:42 +0900 Subject: 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 --- crates/mozart/src/commands/self_update.rs | 49 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'crates/mozart/src/commands/self_update.rs') 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(¤t_exe, &data_dir) } else { - update(args, ¤t_exe, &data_dir) + update(args, ¤t_exe, &data_dir).await } } @@ -128,10 +128,10 @@ fn platform_asset_name() -> anyhow::Result { // ─── GitHub fetching ────────────────────────────────────────────────────────── -fn fetch_releases(include_prerelease: bool) -> anyhow::Result> { +async fn fetch_releases(include_prerelease: bool) -> anyhow::Result> { 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 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 let mut releases: Vec = 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]; - - 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]) + let mut stream = response; + + 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)] -- cgit v1.3.1