aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/src/commands/search.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-10 15:29:19 +0900
committernsfisis <nsfisis@gmail.com>2026-05-10 15:29:19 +0900
commit46845eff8d1398f35099a0ef914f77bcaf473287 (patch)
tree12c4850f1d2f438d0ba6c363fdc0e5036cd4601d /crates/mozart/src/commands/search.rs
parent212506c364b2342dd9e5fa789e8cff38835dfe52 (diff)
downloadphp-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.rs30
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}");
}
}