diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-05 12:50:43 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-05 12:55:09 +0900 |
| commit | 78a627f9b839e902faec6e5f7fee4ec19fc0e4b8 (patch) | |
| tree | 9438a91dc70e87ce78091377f571316ecf1a2a7b /crates/mozart/src/commands/dependency.rs | |
| parent | a55ad1cc44c12836eca5652d231902968e04eea1 (diff) | |
| download | php-mozart-78a627f9b839e902faec6e5f7fee4ec19fc0e4b8.tar.gz php-mozart-78a627f9b839e902faec6e5f7fee4ec19fc0e4b8.tar.zst php-mozart-78a627f9b839e902faec6e5f7fee4ec19fc0e4b8.zip | |
refactor(depends): share execution path with prohibits via do_execute
Mirror composer's BaseDependencyCommand::doExecute by collapsing the
duplicated working-dir/load/lookup/print pipeline in depends.rs and
prohibits.rs into a single dependency::do_execute helper driven by an
`inverted` flag. The clap arg structs stay in their per-command modules
and just forward to the shared entry point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src/commands/dependency.rs')
| -rw-r--r-- | crates/mozart/src/commands/dependency.rs | 154 |
1 files changed, 153 insertions, 1 deletions
diff --git a/crates/mozart/src/commands/dependency.rs b/crates/mozart/src/commands/dependency.rs index d044432..19e9430 100644 --- a/crates/mozart/src/commands/dependency.rs +++ b/crates/mozart/src/commands/dependency.rs @@ -6,9 +6,161 @@ use indexmap::IndexSet; use std::collections::BTreeMap; -use std::path::Path; +use std::path::{Path, PathBuf}; use anyhow::Result; +use mozart_core::console_format; + +// ───────────────────────────────────────────────────────────────────────────── +// Shared command entry point +// ───────────────────────────────────────────────────────────────────────────── + +/// Inputs for [`do_execute`], collected from the `depends` / `prohibits` CLI args. +pub struct DoExecuteArgs<'a> { + pub package: &'a str, + /// Version constraint string (only set for `prohibits`). + pub version: Option<&'a str>, + pub recursive: bool, + pub tree: bool, + pub locked: bool, + /// `true` for `prohibits` (why-not), `false` for `depends` (why). + pub inverted: bool, +} + +/// Shared implementation for `depends` (why) and `prohibits` (why-not). +/// +/// Mirrors `BaseDependencyCommand::doExecute` in Composer: a single function +/// driven by `inverted` to switch between "who depends on X?" and +/// "who prevents X version V from being installed?". +pub fn do_execute( + cli: &super::Cli, + console: &mozart_core::console::Console, + args: DoExecuteArgs<'_>, +) -> Result<()> { + let DoExecuteArgs { + package, + version, + recursive, + tree, + locked, + inverted, + } = args; + + let working_dir = match &cli.working_dir { + Some(dir) => PathBuf::from(dir), + None => std::env::current_dir()?, + }; + + let packages = load_packages(&working_dir, locked)?; + + if packages.is_empty() { + console.write_error( + "No dependencies installed. Try running mozart install or update, or use --locked.", + ); + return Err(mozart_core::exit_code::bail_silent( + mozart_core::exit_code::GENERAL_ERROR, + )); + } + + let target = package.to_lowercase(); + + let target_known = packages.iter().any(|p| p.name.to_lowercase() == target); + if !target_known { + if !inverted && mozart_core::platform::is_platform_package(&target) { + anyhow::bail!( + "Could not find platform package \"{}\". Is PHP available?", + package + ); + } + anyhow::bail!("Could not find package \"{}\" in your project", package); + } + + let constraint = match version { + Some(v) => Some( + mozart_semver::VersionConstraint::parse(v) + .map_err(|e| anyhow::anyhow!("Invalid version constraint '{}': {}", v, e))?, + ), + None => None, + }; + + let recursive = tree || recursive; + let needles = vec![target]; + + let results = get_dependents( + &packages, + &needles, + constraint.as_ref(), + inverted, + recursive, + )?; + + if results.is_empty() { + if inverted { + console.write_stdout( + &console_format!( + "<info>{} {} can be installed.</info>", + package, + version.unwrap_or("") + ), + mozart_core::console::Verbosity::Normal, + ); + return Ok(()); + } + console.info(&format!( + "There is no installed package depending on \"{}\"", + package + )); + return Err(mozart_core::exit_code::bail_silent( + mozart_core::exit_code::GENERAL_ERROR, + )); + } + + if tree { + print_tree(&results, 0, console); + } else { + print_table(&results, console); + } + + if !inverted { + return Ok(()); + } + + // Resolution hint: pick the right composer command based on whether the + // package sits in root's `require`, `require-dev`, or neither. + let needle_lower = package.to_lowercase(); + let composer_command = packages + .iter() + .find(|p| p.is_root) + .map(|root| { + if root + .require + .keys() + .any(|k| k.to_lowercase() == needle_lower) + { + "require" + } else if root + .require_dev + .keys() + .any(|k| k.to_lowercase() == needle_lower) + { + "require --dev" + } else { + "update" + } + }) + .unwrap_or("update"); + + console.info(&format!( + "Not finding what you were looking for? Try calling `composer {} \"{}:{}\" --dry-run` to get another view on the problem.", + composer_command, + package, + version.unwrap_or("") + )); + + Err(mozart_core::exit_code::bail_silent( + mozart_core::exit_code::GENERAL_ERROR, + )) +} // ───────────────────────────────────────────────────────────────────────────── // Core types |
