diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-21 23:38:32 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-21 23:38:32 +0900 |
| commit | 52310761f67220c9c075cd847205825a720035ee (patch) | |
| tree | 0528fc94aea7853e41313e19964d74a958dae9c9 /crates/mozart/src/commands/remove.rs | |
| parent | 92da9e37c68beb180e45e550fba5acd7d28dca27 (diff) | |
| download | php-mozart-52310761f67220c9c075cd847205825a720035ee.tar.gz php-mozart-52310761f67220c9c075cd847205825a720035ee.tar.zst php-mozart-52310761f67220c9c075cd847205825a720035ee.zip | |
feat(console): add structured error handling, verbosity, and suggestions
Implement Phase 7.2 error handling & UX infrastructure:
- Add exit_code module with MozartError, bail()/bail_silent() helpers,
and Composer-compatible exit code constants (0-5, 100)
- Redesign Console struct with Verbosity enum (Quiet/Normal/Verbose/
VeryVerbose/Debug), ANSI auto-detection via IsTerminal, and
verbosity-gated output methods (info/verbose/debug/error)
- Thread Console through all 33 command execute() signatures
- Replace all std::process::exit() calls with structured MozartError
returns handled in main()
- Migrate eprintln\! status messages to console.info() for quiet-mode
suppression
- Add suggest module with Levenshtein distance and "Did you mean?"
formatting for future package name suggestions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src/commands/remove.rs')
| -rw-r--r-- | crates/mozart/src/commands/remove.rs | 83 |
1 files changed, 46 insertions, 37 deletions
diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs index ecfbba8..6969745 100644 --- a/crates/mozart/src/commands/remove.rs +++ b/crates/mozart/src/commands/remove.rs @@ -96,7 +96,11 @@ pub struct RemoveArgs { pub apcu_autoloader_prefix: Option<String>, } -pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { +pub fn execute( + args: &RemoveArgs, + cli: &super::Cli, + console: &crate::console::Console, +) -> anyhow::Result<()> { // Step 1: Validate inputs if args.packages.is_empty() && !args.unused { anyhow::bail!("Not enough arguments (missing: \"packages\")."); @@ -104,20 +108,20 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { // Step 2: Handle deprecated flags if args.update_with_dependencies { - eprintln!( + console.info(&format!( "{}", console::warning( "The -w / --update-with-dependencies flag is deprecated. Use --with-all-dependencies instead." ) - ); + )); } if args.update_with_all_dependencies { - eprintln!( + console.info(&format!( "{}", console::warning( "The -W / --update-with-all-dependencies flag is deprecated. Use --with-all-dependencies instead." ) - ); + )); } // Step 3: Resolve working directory and read composer.json @@ -135,12 +139,12 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { // Step 4: Handle --unused flag (deferred implementation) if args.unused { - eprintln!( + console.info(&format!( "{}", console::warning( "--unused is not yet fully implemented. The resolver will naturally prune unreachable packages." ) - ); + )); // Fall through: if no explicit packages were named, nothing to remove. if args.packages.is_empty() { return Ok(()); @@ -168,12 +172,12 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { raw.require_dev.remove(&name); any_removed = true; } else { - eprintln!( + console.info(&format!( "{}", console::warning(&format!( "{name} is not required in require-dev and has not been removed." )) - ); + )); } } else { // Auto-detect: look in require first, then require-dev @@ -192,12 +196,12 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { raw.require_dev.remove(&name); any_removed = true; } else { - eprintln!( + console.info(&format!( "{}", console::warning(&format!( "{name} is not required in your composer.json and has not been removed." )) - ); + )); } } } @@ -272,35 +276,34 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { }; // Print header messages - eprintln!("Loading composer repositories with package information"); + console.info("Loading composer repositories with package information"); if dev_mode { - eprintln!("Updating dependencies (including require-dev)"); + console.info("Updating dependencies (including require-dev)"); } else { - eprintln!("Updating dependencies"); + console.info("Updating dependencies"); } - eprintln!("Resolving dependencies..."); + console.info("Resolving dependencies..."); // Run resolver - let mut resolved = match resolver::resolve(&request) { - Ok(packages) => packages, - Err(e) => { - eprintln!("{}", console::error(&e.to_string())); - std::process::exit(1); - } - }; + let mut resolved = resolver::resolve(&request).map_err(|e| { + crate::exit_code::bail( + crate::exit_code::DEPENDENCY_RESOLUTION_FAILED, + e.to_string(), + ) + })?; // Read old lock file (if any) for change reporting and partial update let old_lock = if lock_path.exists() { match lockfile::LockFile::read_from_file(&lock_path) { Ok(l) => Some(l), Err(e) => { - eprintln!( + console.info(&format!( "{}", console::warning(&format!( "Could not read existing composer.lock: {}. Treating as a fresh install.", e )) - ); + )); None } } @@ -341,12 +344,12 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { // For --minimal-changes, additionally pin packages beyond the allow list if args.minimal_changes { - eprintln!( + console.info(&format!( "{}", console::info( "Minimal changes mode: preserving locked versions for non-removed packages." ) - ); + )); } resolved = super::update::apply_partial_update(resolved, lock, &allow_list); @@ -385,7 +388,7 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { .filter(|c| matches!(c.kind, super::update::ChangeKind::Remove { .. })) .collect(); - eprintln!( + console.info(&format!( "{}", console::info(&format!( "Package operations: {} install{}, {} update{}, {} removal{}", @@ -396,23 +399,29 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { removals.len(), if removals.len() == 1 { "" } else { "s" }, )) - ); + )); // Print individual change lines for change in &changes { match &change.kind { super::update::ChangeKind::Remove { old_version } => { if args.dry_run { - eprintln!(" - Would remove {} ({})", change.name, old_version); + console.info(&format!( + " - Would remove {} ({})", + change.name, old_version + )); } else { - eprintln!(" - Removing {} ({})", change.name, old_version); + console.info(&format!(" - Removing {} ({})", change.name, old_version)); } } super::update::ChangeKind::Install { new_version } => { if args.dry_run { - eprintln!(" - Would install {} ({})", change.name, new_version); + console.info(&format!( + " - Would install {} ({})", + change.name, new_version + )); } else { - eprintln!(" - Installing {} ({})", change.name, new_version); + console.info(&format!(" - Installing {} ({})", change.name, new_version)); } } super::update::ChangeKind::Update { @@ -420,15 +429,15 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { new_version, } => { if args.dry_run { - eprintln!( + console.info(&format!( " - Would update {} ({} => {})", change.name, old_version, new_version - ); + )); } else { - eprintln!( + console.info(&format!( " - Updating {} ({} => {})", change.name, old_version, new_version - ); + )); } } super::update::ChangeKind::Unchanged => {} @@ -437,7 +446,7 @@ pub fn execute(args: &RemoveArgs, cli: &super::Cli) -> anyhow::Result<()> { // Write lock file (unless --dry-run) if !args.dry_run { - eprintln!("Writing lock file"); + console.info("Writing lock file"); new_lock.write_to_file(&lock_path)?; } |
