From 46845eff8d1398f35099a0ef914f77bcaf473287 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 10 May 2026 15:29:19 +0900 Subject: refactor(io): introduce IoInterface trait mirroring Composer IOInterface Add an `IoInterface` trait in mozart-core::console that mirrors `\Composer\IO\IOInterface`, implement it for `Console`, and switch commands, the auditor, and the suggested-packages reporter to accept the abstracted IO (typically `Arc>>` at the command boundary, `&dyn IoInterface` deeper down) instead of `&Console`. The console_writeln\!/write\! macros now go through `IoInterface::verbosity()` via the lock so any implementor works. --- crates/mozart-core/src/repository/advisory.rs | 127 +++++++++++--------------- 1 file changed, 55 insertions(+), 72 deletions(-) (limited to 'crates/mozart-core/src/repository') diff --git a/crates/mozart-core/src/repository/advisory.rs b/crates/mozart-core/src/repository/advisory.rs index 02a6e1a..08f59a1 100644 --- a/crates/mozart-core/src/repository/advisory.rs +++ b/crates/mozart-core/src/repository/advisory.rs @@ -1,7 +1,7 @@ use super::packagist::SecurityAdvisory; use super::repository::RepositorySet; use crate::advisory::{AbandonedHandling, AuditFormat}; -use crate::console::Console; +use crate::console::IoInterface; use crate::{console_writeln, console_writeln_error}; use indexmap::IndexMap; use std::collections::BTreeMap; @@ -88,7 +88,7 @@ impl Auditor { /// Returns a bitmask: 0=ok, 1=vulnerable, 2=abandoned, 3=both. pub async fn audit( &self, - console: &Console, + io: std::sync::Arc>>, repo_set: &RepositorySet, packages: &[PackageInfo], options: &AuditOptions<'_>, @@ -132,7 +132,7 @@ impl Auditor { &ignored_advisories, &unreachable_repos, &abandoned_packages, - console, + &io, ); return Ok(bitmask); } @@ -152,8 +152,8 @@ impl Auditor { let msg = format!( "Found {ignored_total} ignored security vulnerability advisor{plurality} affecting {ignored_pkg_count} package{pkg_plurality}{punctuation}" ); - console_writeln_error!(console, "{msg}"); - self.output_advisories_ignored(console, &ignored_advisories, format); + console_writeln_error!(io, "{msg}"); + self.output_advisories_ignored(&io, &ignored_advisories, format); } if active_pkg_count > 0 { @@ -168,38 +168,35 @@ impl Auditor { "Found {active_total} security vulnerability advisor{plurality} affecting {active_pkg_count} package{pkg_plurality}{punctuation}" ); if options.warning_only { - console_writeln_error!(console, "{msg}"); + console_writeln_error!(io, "{msg}"); } else { - console_writeln_error!(console, "{msg}"); + console_writeln_error!(io, "{msg}"); } - self.output_advisories(console, &advisories, format); + self.output_advisories(&io, &advisories, format); } if format == AuditFormat::Summary { - console_writeln_error!( - console, - "Run \"mozart audit\" for a full list of advisories." - ); + console_writeln_error!(io, "Run \"mozart audit\" for a full list of advisories."); } } else { console_writeln_error!( - console, + io, "No security vulnerability advisories found.", ); } if !unreachable_repos.is_empty() { console_writeln_error!( - console, + io, "The following repositories were unreachable:", ); for repo in &unreachable_repos { - console_writeln_error!(console, " - {repo}"); + console_writeln_error!(io, " - {repo}"); } } if !abandoned_packages.is_empty() && format != AuditFormat::Summary { - self.output_abandoned_packages(console, &abandoned_packages, format); + self.output_abandoned_packages(&io, &abandoned_packages, format); } Ok(bitmask) @@ -364,13 +361,13 @@ impl Auditor { fn output_advisories( &self, - console: &Console, + io: &std::sync::Arc>>, advisories: &BTreeMap>, format: AuditFormat, ) { match format { - AuditFormat::Table => self.output_advisories_table(console, advisories), - AuditFormat::Plain => self.output_advisories_plain(console, advisories), + AuditFormat::Table => self.output_advisories_table(io, advisories), + AuditFormat::Plain => self.output_advisories_plain(io, advisories), AuditFormat::Summary => {} AuditFormat::Json => unreachable!(), } @@ -378,13 +375,13 @@ impl Auditor { fn output_advisories_ignored( &self, - console: &Console, + io: &std::sync::Arc>>, advisories: &BTreeMap>, format: AuditFormat, ) { match format { - AuditFormat::Table => self.output_ignored_advisories_table(console, advisories), - AuditFormat::Plain => self.output_ignored_advisories_plain(console, advisories), + AuditFormat::Table => self.output_ignored_advisories_table(io, advisories), + AuditFormat::Plain => self.output_ignored_advisories_plain(io, advisories), AuditFormat::Summary => {} AuditFormat::Json => unreachable!(), } @@ -392,30 +389,25 @@ impl Auditor { fn output_advisories_table( &self, - console: &Console, + io: &std::sync::Arc>>, advisories: &BTreeMap>, ) { for pkg_advisories in advisories.values() { for matched in pkg_advisories { - self.render_advisory_table( - console, - &matched.advisory, - &matched.installed_version, - None, - ); + self.render_advisory_table(io, &matched.advisory, &matched.installed_version, None); } } } fn output_ignored_advisories_table( &self, - console: &Console, + io: &std::sync::Arc>>, advisories: &BTreeMap>, ) { for pkg_advisories in advisories.values() { for ignored in pkg_advisories { self.render_advisory_table( - console, + io, &ignored.advisory, &ignored.installed_version, ignored.ignore_reason.as_deref(), @@ -426,7 +418,7 @@ impl Auditor { fn render_advisory_table( &self, - console: &Console, + io: &std::sync::Arc>>, adv: &SecurityAdvisory, installed_version: &str, ignore_reason: Option<&str>, @@ -459,10 +451,10 @@ impl Auditor { vw = value_width ); - console_writeln_error!(console, "{}", separator); + console_writeln_error!(io, "{}", separator); for (label, value) in &rows { console_writeln_error!( - console, + io, "| {:>>, advisories: &BTreeMap>, ) { let mut first = true; for pkg_advisories in advisories.values() { for matched in pkg_advisories { if !first { - console_writeln_error!(console, "--------"); + console_writeln_error!(io, "--------"); } - self.render_advisory_plain( - console, - &matched.advisory, - &matched.installed_version, - None, - ); + self.render_advisory_plain(io, &matched.advisory, &matched.installed_version, None); first = false; } } @@ -498,17 +485,17 @@ impl Auditor { fn output_ignored_advisories_plain( &self, - console: &Console, + io: &std::sync::Arc>>, advisories: &BTreeMap>, ) { let mut first = true; for pkg_advisories in advisories.values() { for ignored in pkg_advisories { if !first { - console_writeln_error!(console, "--------"); + console_writeln_error!(io, "--------"); } self.render_advisory_plain( - console, + io, &ignored.advisory, &ignored.installed_version, ignored.ignore_reason.as_deref(), @@ -520,39 +507,35 @@ impl Auditor { fn render_advisory_plain( &self, - console: &Console, + io: &std::sync::Arc>>, adv: &SecurityAdvisory, installed_version: &str, ignore_reason: Option<&str>, ) { - console_writeln_error!(console, "Package: {}", adv.package_name); - console_writeln_error!(console, "Version: {installed_version}"); - console_writeln_error!( - console, - "Severity: {}", - adv.severity.as_deref().unwrap_or(""), - ); - console_writeln_error!(console, "Advisory ID: {}", adv.advisory_id); - console_writeln_error!(console, "CVE: {}", adv.cve.as_deref().unwrap_or("NO CVE")); - console_writeln_error!(console, "Title: {}", adv.title); - console_writeln_error!(console, "URL: {}", adv.link.as_deref().unwrap_or("")); - console_writeln_error!(console, "Affected versions: {}", adv.affected_versions); - console_writeln_error!(console, "Reported at: {}", adv.reported_at); + console_writeln_error!(io, "Package: {}", adv.package_name); + console_writeln_error!(io, "Version: {installed_version}"); + console_writeln_error!(io, "Severity: {}", adv.severity.as_deref().unwrap_or(""),); + console_writeln_error!(io, "Advisory ID: {}", adv.advisory_id); + console_writeln_error!(io, "CVE: {}", adv.cve.as_deref().unwrap_or("NO CVE")); + console_writeln_error!(io, "Title: {}", adv.title); + console_writeln_error!(io, "URL: {}", adv.link.as_deref().unwrap_or("")); + console_writeln_error!(io, "Affected versions: {}", adv.affected_versions); + console_writeln_error!(io, "Reported at: {}", adv.reported_at); if let Some(reason) = ignore_reason { - console_writeln_error!(console, "Ignore reason: {reason}"); + console_writeln_error!(io, "Ignore reason: {reason}"); } } fn output_abandoned_packages( &self, - console: &Console, + io: &std::sync::Arc>>, packages: &[AbandonedPackage], format: AuditFormat, ) { let count = packages.len(); let plurality = if count == 1 { "" } else { "s" }; console_writeln_error!( - console, + io, "Found {count} abandoned package{plurality}:", ); @@ -560,14 +543,14 @@ impl Auditor { for pkg in packages { match &pkg.replacement { Some(repl) => console_writeln_error!( - console, + io, "{} ({}) is abandoned. Use {} instead.", pkg.name, pkg.version, repl, ), None => console_writeln_error!( - console, + io, "{} ({}) is abandoned. No replacement was suggested.", pkg.name, pkg.version, @@ -598,7 +581,7 @@ impl Auditor { .max("Suggested Replacement".len()); console_writeln_error!( - console, + io, "| {:>, unreachable_repos: &[String], abandoned_packages: &[AbandonedPackage], - console: &Console, + io: &std::sync::Arc>>, ) { let mut advisories_map: serde_json::Map = serde_json::Map::new(); for (pkg_name, matched_list) in advisories { @@ -720,7 +703,7 @@ impl Auditor { } let json_str = serde_json::to_string_pretty(&output).unwrap_or_else(|_| "{}".to_string()); - console_writeln!(console, "{}", &json_str); + console_writeln!(io, "{}", &json_str); } } -- cgit v1.3.1