diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-10 15:29:19 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-10 15:29:19 +0900 |
| commit | 46845eff8d1398f35099a0ef914f77bcaf473287 (patch) | |
| tree | 12c4850f1d2f438d0ba6c363fdc0e5036cd4601d /crates/mozart/src/commands/search.rs | |
| parent | 212506c364b2342dd9e5fa789e8cff38835dfe52 (diff) | |
| download | php-mozart-46845eff8d1398f35099a0ef914f77bcaf473287.tar.gz php-mozart-46845eff8d1398f35099a0ef914f77bcaf473287.tar.zst php-mozart-46845eff8d1398f35099a0ef914f77bcaf473287.zip | |
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<Mutex<Box<dyn IoInterface>>>` 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.
Diffstat (limited to 'crates/mozart/src/commands/search.rs')
| -rw-r--r-- | crates/mozart/src/commands/search.rs | 30 |
1 files changed, 20 insertions, 10 deletions
diff --git a/crates/mozart/src/commands/search.rs b/crates/mozart/src/commands/search.rs index a5ab04a..7259e6c 100644 --- a/crates/mozart/src/commands/search.rs +++ b/crates/mozart/src/commands/search.rs @@ -1,5 +1,5 @@ use clap::Args; -use mozart_core::console::{Console, hyperlink}; +use mozart_core::console::{IoInterface, hyperlink}; use mozart_core::console_format; use mozart_core::console_writeln; use mozart_core::repository::packagist::SearchResult; @@ -68,12 +68,16 @@ fn is_abandoned(result: &SearchResult) -> bool { } } -pub async fn execute(args: &SearchArgs, cli: &super::Cli, console: &Console) -> anyhow::Result<()> { +pub async fn execute( + args: &SearchArgs, + cli: &super::Cli, + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, +) -> anyhow::Result<()> { // 1. Format check first — matches Composer's `SearchCommand::execute` // L61-66 ordering. let format = args.format.as_deref().unwrap_or("text"); if !matches!(format, "text" | "json") { - console.error(&console_format!( + io.lock().unwrap().error(&console_format!( "<error>Unsupported format \"{format}\". See help for supported formats.</error>" )); return Err(mozart_core::exit_code::bail_silent( @@ -121,8 +125,8 @@ pub async fn execute(args: &SearchArgs, cli: &super::Cli, console: &Console) -> // 7. Render. Empty results emit nothing in text mode (matches Composer) // and `[]` in JSON mode. match format { - "json" => render_json(&results, console)?, - _ => render_text(&results, console), + "json" => render_json(&results, io.clone())?, + _ => render_text(&results, io.clone()), } Ok(()) @@ -133,13 +137,16 @@ pub async fn execute(args: &SearchArgs, cli: &super::Cli, console: &Console) -> /// JSON_UNESCAPED_UNICODE`). `serde_json` does not escape forward slashes /// or non-ASCII Unicode by default, so the encoder configuration alone /// covers the latter two flags. -fn render_json(results: &[SearchResult], console: &Console) -> anyhow::Result<()> { +fn render_json( + results: &[SearchResult], + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, +) -> anyhow::Result<()> { let output: Vec<SearchResultOutput> = results.iter().map(SearchResultOutput::from).collect(); let buf = Vec::new(); let formatter = serde_json::ser::PrettyFormatter::with_indent(b" "); let mut ser = serde_json::Serializer::with_formatter(buf, formatter); output.serialize(&mut ser)?; - console_writeln!(console, "{}", &String::from_utf8(ser.into_inner())?); + console_writeln!(io, "{}", &String::from_utf8(ser.into_inner())?); Ok(()) } @@ -148,7 +155,10 @@ fn render_json(results: &[SearchResult], console: &Console) -> anyhow::Result<() /// else plain `name`, padded to the longest-name column. /// - `<warning>! Abandoned !</warning> ` prefix when abandoned. /// - Description, truncated with `...` to fit the terminal width. -fn render_text(results: &[SearchResult], console: &Console) { +fn render_text( + results: &[SearchResult], + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, +) { if results.is_empty() { return; } @@ -182,14 +192,14 @@ fn render_text(results: &[SearchResult], console: &Console) { let padded_name = if !result.url.is_empty() { format!( "{}{}", - hyperlink(&result.url, &result.name, console.decorated), + hyperlink(&result.url, &result.name, io.lock().unwrap().is_decorated()), " ".repeat(padding_width) ) } else { format!("{}{}", result.name, " ".repeat(padding_width)) }; - console_writeln!(console, "{padded_name}{warning}{desc_display}"); + console_writeln!(io, "{padded_name}{warning}{desc_display}"); } } |
