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/src/commands/create_project.rs | 68 +++++++++++++++------------- 1 file changed, 37 insertions(+), 31 deletions(-) (limited to 'crates/mozart/src/commands/create_project.rs') diff --git a/crates/mozart/src/commands/create_project.rs b/crates/mozart/src/commands/create_project.rs index 2b2fbe1..276bd3a 100644 --- a/crates/mozart/src/commands/create_project.rs +++ b/crates/mozart/src/commands/create_project.rs @@ -1,6 +1,6 @@ use clap::Args; use indexmap::IndexMap; -use mozart_core::console::Console; +use mozart_core::console::IoInterface; use mozart_core::console_format; use mozart_core::package::{self, Stability}; use mozart_core::repository::downloader; @@ -146,12 +146,15 @@ fn dir_from_package_name(package_name: &str) -> &str { } /// Remove VCS metadata directories from the target directory. -fn remove_vcs_metadata(target_dir: &Path, console: &Console) -> anyhow::Result<()> { +fn remove_vcs_metadata( + target_dir: &Path, + io: std::sync::Arc>>, +) -> anyhow::Result<()> { for vcs_dir in VCS_DIRS { let path = target_dir.join(vcs_dir); if path.exists() { std::fs::remove_dir_all(&path)?; - console.info(&console_format!( + io.lock().unwrap().info(&console_format!( "Removed VCS metadata directory: {vcs_dir}" )); } @@ -280,29 +283,29 @@ fn version_satisfies_constraint(packagist_version: &str, constraint: &str) -> bo pub async fn execute( args: &CreateProjectArgs, cli: &super::Cli, - console: &Console, + io: std::sync::Arc>>, ) -> anyhow::Result<()> { // --- Deprecated / aliased flags --- if args.dev { - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "You are using the deprecated option \"dev\". Dev packages are installed by default now." )); } if args.no_custom_installers { - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "You are using the deprecated option \"no-custom-installers\". Use \"no-plugins\" instead." )); } // --- --ask interactive prompt for the project directory --- - let directory_arg: Option = if console.interactive && args.ask { + let directory_arg: Option = if io.lock().unwrap().is_interactive() && args.ask { let package = args .package .as_deref() .ok_or_else(|| anyhow::anyhow!("Not enough arguments (missing: \"package\")."))?; let lower = package.to_lowercase(); let basename = dir_from_package_name(&lower).to_string(); - let answer = console.ask( + let answer = io.lock().unwrap().ask( &console_format!("New project directory [{basename}]: "), &basename, ); @@ -334,7 +337,7 @@ pub async fn execute( let secure_http = !args.no_secure_http; install_project( - console, + &io, cli, args, args.package.as_deref(), @@ -359,7 +362,7 @@ pub async fn execute( #[allow(clippy::too_many_arguments)] async fn install_project( - console: &Console, + io: &std::sync::Arc>>, cli: &super::Cli, args: &CreateProjectArgs, package_name: Option<&str>, @@ -382,7 +385,7 @@ async fn install_project( // Mozart does not yet support custom repositories on the create-project // command — warn and ignore (deferred; tracked under priority 2). if repositories.is_some() || add_repository { - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "Custom repository options (--repository, --repository-url, --add-repository) \ are not yet supported and will be ignored." )); @@ -392,7 +395,7 @@ async fn install_project( let root_result = if let Some(name) = package_name { Some( install_root_package( - console, + io, cli, args, name, @@ -433,18 +436,17 @@ async fn install_project( let mut vcs_removed = false; if !args.keep_vcs { let should_remove = if installed_from_vcs { - args.remove_vcs - || !console.interactive - || console.confirm(&console_format!( - "Do you want to remove the existing VCS (.git, .svn..) history? [y,n]? " - )) + let remove_vcs_confirmed = io.lock().unwrap().confirm(&console_format!( + "Do you want to remove the existing VCS (.git, .svn..) history? [y,n]? " + )); + args.remove_vcs || !io.lock().unwrap().is_interactive() || remove_vcs_confirmed } else { // Default for dist installs: scrub VCS metadata that may have been // shipped inside the archive (matches Mozart's pre-split behaviour). true }; if should_remove { - remove_vcs_metadata(&target_dir, console)?; + remove_vcs_metadata(&target_dir, io.clone())?; vcs_removed = true; } } @@ -452,7 +454,7 @@ async fn install_project( // --- Read composer.json from the new project --- let composer_path = target_dir.join("composer.json"); if !composer_path.exists() { - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "No composer.json found in {}. Skipping dependency installation.", target_dir.display() )); @@ -468,7 +470,7 @@ async fn install_project( } if no_install { - console.info(&console_format!( + io.lock().unwrap().info(&console_format!( "Skipping dependency installation (--no-install)." )); return Ok(()); @@ -542,7 +544,7 @@ async fn install_project( block_insecure: false, }; - console.info("Resolving dependencies..."); + io.lock().unwrap().info("Resolving dependencies..."); let resolved = resolver::resolve(&request).await.map_err(|e| { mozart_core::exit_code::bail( @@ -573,7 +575,7 @@ async fn install_project( .filter(|c| matches!(c.kind, super::update::ChangeKind::Install { .. })) .collect(); - console.info(&console_format!( + io.lock().unwrap().info(&console_format!( "Package operations: {} install{}, 0 updates, 0 removals", installs.len(), if installs.len() == 1 { "" } else { "s" } @@ -581,18 +583,20 @@ async fn install_project( for change in &changes { if let super::update::ChangeKind::Install { new_version } = &change.kind { - console.info(&format!(" - Installing {} ({})", change.name, new_version)); + io.lock() + .unwrap() + .info(&format!(" - Installing {} ({})", change.name, new_version)); } } - console.info("Writing lock file"); + io.lock().unwrap().info("Writing lock file"); let lock_path = target_dir.join("composer.lock"); new_lock.write_to_file(&lock_path)?; let vendor_dir = target_dir.join("vendor"); if prefer_source { - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "Source installs are not yet supported. Falling back to dist." )); } @@ -633,7 +637,7 @@ async fn install_project( download_only: false, prefer_source: args.prefer_source, }, - console, + io.clone(), &mut executor, ) .await?; @@ -643,7 +647,7 @@ async fn install_project( #[allow(clippy::too_many_arguments)] async fn install_root_package( - console: &Console, + io: &std::sync::Arc>>, cli: &super::Cli, _args: &CreateProjectArgs, package_name: &str, @@ -704,7 +708,7 @@ async fn install_root_package( } let short = shortest_path(&working_dir, &target_dir); - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "Creating a \"{package_name}\" project at \"{short}\"" )); @@ -760,11 +764,13 @@ async fn install_root_package( let concrete_version = best.version.clone(); // --- Print "Installing" line + plugin notice --- - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "Installing {name} ({concrete_version})" )); if disable_plugins { - console.write_error(&console_format!("Plugins have been disabled.")); + io.lock() + .unwrap() + .write_error(&console_format!("Plugins have been disabled.")); } // --- Create the target directory and download + extract the dist archive --- @@ -800,7 +806,7 @@ async fn install_root_package( // Mozart only supports dist downloads today, so this is always false. let installed_from_vcs = false; - console.write_error(&console_format!( + io.lock().unwrap().write_error(&console_format!( "Created project in {}", target_dir.display() )); -- cgit v1.3.1