diff --git a/Cargo.lock b/Cargo.lock index c3450d7..bbcd400 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -792,7 +792,7 @@ dependencies = [ [[package]] name = "bootable-core" -version = "0.1.3" +version = "0.1.4" dependencies = [ "bzip2", "flate2", @@ -817,7 +817,7 @@ dependencies = [ [[package]] name = "bootable-desktop" -version = "0.1.3" +version = "0.1.4" dependencies = [ "anyhow", "bootable-core", @@ -831,7 +831,7 @@ dependencies = [ [[package]] name = "bootable-helper" -version = "0.1.3" +version = "0.1.4" dependencies = [ "bootable-core", "serde_json", @@ -839,7 +839,7 @@ dependencies = [ [[package]] name = "bootable-tui" -version = "0.1.3" +version = "0.1.4" dependencies = [ "anyhow", "bootable-core", diff --git a/Cargo.toml b/Cargo.toml index bae8092..c045744 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ default-members = [ resolver = "2" [workspace.package] -version = "0.1.3" +version = "0.1.4" edition = "2024" license = "Apache-2.0" rust-version = "1.88" diff --git a/apps/bootable-tui/src/main.rs b/apps/bootable-tui/src/main.rs index 829f023..78e13a9 100644 --- a/apps/bootable-tui/src/main.rs +++ b/apps/bootable-tui/src/main.rs @@ -77,6 +77,9 @@ enum Commands { index: usize, #[arg(long, value_name = "ISO_FILE")] output: Option, + /// Emit newline-delimited JSON progress for graphical clients. + #[arg(long)] + json_progress: bool, }, /// List official Raspberry Pi Imager images. PiImages { @@ -174,7 +177,8 @@ fn main() -> Result<()> { slug, index, output, - }) => download_release(&engine, &slug, index, output), + json_progress, + }) => download_release(&engine, &slug, index, output, json_progress), Some(Commands::PiImages { device, limit, @@ -290,19 +294,37 @@ fn download_release( slug: &str, index: usize, output: Option, + json_progress: bool, ) -> Result<()> { - let details = engine.distribution_details(slug)?; - let releases = resolve_releases(engine, &details)?; - let release = releases - .get(index) - .with_context(|| format!("release index {index} is out of range"))?; - let destination = output.unwrap_or_else(|| PathBuf::from(&release.name)); - let mut reporter = ProgressReporter::default(); - let report = engine.download_iso(release, &destination, |progress| reporter.print(progress))?; - println!("Ready to write: {}", report.path.display()); - println!("Kind: {}", report.kind); - println!("Size: {}", format_bytes(report.size)); - Ok(()) + let mut reporter = ProgressReporter::new(json_progress); + let result = (|| { + let details = engine.distribution_details(slug)?; + let releases = resolve_releases(engine, &details)?; + let release = releases + .get(index) + .with_context(|| format!("release index {index} is out of range"))?; + let destination = output.unwrap_or_else(|| PathBuf::from(&release.name)); + engine + .download_iso(release, &destination, |progress| reporter.print(progress)) + .map_err(anyhow::Error::from) + })(); + + match result { + Ok(report) => { + if json_progress { + reporter.finished(); + } else { + println!("Ready to write: {}", report.path.display()); + println!("Kind: {}", report.kind); + println!("Size: {}", format_bytes(report.size)); + } + Ok(()) + } + Err(error) => { + reporter.failed(&error.to_string()); + Err(error) + } + } } fn resolve_releases(engine: &Bootable, details: &DistributionDetails) -> Result> { @@ -5079,6 +5101,28 @@ mod layout_tests { )); } + #[test] + fn download_json_progress_is_an_explicit_client_mode() { + let cli = Cli::try_parse_from([ + "bootable", + "download", + "cachyos", + "--index", + "1", + "--output", + "image.iso", + "--json-progress", + ]) + .expect("valid catalog client invocation"); + assert!(matches!( + cli.command, + Some(Commands::Download { + json_progress: true, + .. + }) + )); + } + #[test] fn progress_events_are_stable_newline_json_payloads() { let event = progress_event_json(&Progress { diff --git a/packaging/Packager.toml b/packaging/Packager.toml index 13e2816..d186dd2 100644 --- a/packaging/Packager.toml +++ b/packaging/Packager.toml @@ -1,7 +1,7 @@ name = "bootable" product-name = "Bootable" identifier = "app.bootable.Bootable" -version = "0.1.3" +version = "0.1.4" description = "Create verified boot media from trusted images" long-description = "A safety-first boot media writer with matching desktop and terminal interfaces." homepage = "https://github.com/debpalash/bootable"