aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-05 12:50:43 +0900
committernsfisis <nsfisis@gmail.com>2026-05-05 12:55:09 +0900
commit78a627f9b839e902faec6e5f7fee4ec19fc0e4b8 (patch)
tree9438a91dc70e87ce78091377f571316ecf1a2a7b
parenta55ad1cc44c12836eca5652d231902968e04eea1 (diff)
downloadphp-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>
-rw-r--r--crates/mozart/src/commands/dependency.rs154
-rw-r--r--crates/mozart/src/commands/depends.rs67
-rw-r--r--crates/mozart/src/commands/prohibits.rs109
3 files changed, 177 insertions, 153 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
diff --git a/crates/mozart/src/commands/depends.rs b/crates/mozart/src/commands/depends.rs
index 514ce11..9324b82 100644
--- a/crates/mozart/src/commands/depends.rs
+++ b/crates/mozart/src/commands/depends.rs
@@ -1,5 +1,4 @@
use clap::Args;
-use std::path::PathBuf;
#[derive(Args)]
pub struct DependsArgs {
@@ -24,58 +23,16 @@ pub async fn execute(
cli: &super::Cli,
console: &mozart_core::console::Console,
) -> anyhow::Result<()> {
- let working_dir = match &cli.working_dir {
- Some(dir) => PathBuf::from(dir),
- None => std::env::current_dir()?,
- };
-
- let packages = super::dependency::load_packages(&working_dir, args.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 = args.package.to_lowercase();
-
- // Verify the target package is known
- let target_known = packages.iter().any(|p| p.name.to_lowercase() == target);
- if !target_known && mozart_core::platform::is_platform_package(&target) {
- anyhow::bail!(
- "Could not find platform package \"{}\". Is PHP available?",
- args.package
- );
- }
- if !target_known {
- anyhow::bail!(
- "Could not find package \"{}\" in your project",
- args.package
- );
- }
-
- let recursive = args.tree || args.recursive;
- let needles = vec![target];
- let results = super::dependency::get_dependents(&packages, &needles, None, false, recursive)?;
-
- if results.is_empty() {
- console.info(&format!(
- "There is no installed package depending on \"{}\"",
- args.package
- ));
- return Err(mozart_core::exit_code::bail_silent(
- mozart_core::exit_code::GENERAL_ERROR,
- ));
- }
-
- if args.tree {
- super::dependency::print_tree(&results, 0, console);
- } else {
- super::dependency::print_table(&results, console);
- }
-
- Ok(())
+ super::dependency::do_execute(
+ cli,
+ console,
+ super::dependency::DoExecuteArgs {
+ package: &args.package,
+ version: None,
+ recursive: args.recursive,
+ tree: args.tree,
+ locked: args.locked,
+ inverted: false,
+ },
+ )
}
diff --git a/crates/mozart/src/commands/prohibits.rs b/crates/mozart/src/commands/prohibits.rs
index 8eb4166..4bb00e4 100644
--- a/crates/mozart/src/commands/prohibits.rs
+++ b/crates/mozart/src/commands/prohibits.rs
@@ -1,6 +1,4 @@
use clap::Args;
-use mozart_core::console_format;
-use std::path::PathBuf;
#[derive(Args)]
pub struct ProhibitsArgs {
@@ -28,99 +26,16 @@ pub async fn execute(
cli: &super::Cli,
console: &mozart_core::console::Console,
) -> anyhow::Result<()> {
- let working_dir = match &cli.working_dir {
- Some(dir) => PathBuf::from(dir),
- None => std::env::current_dir()?,
- };
-
- let packages = super::dependency::load_packages(&working_dir, args.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 = args.package.to_lowercase();
-
- // Fix #2: Verify the target package is known
- let target_known = packages.iter().any(|p| p.name.to_lowercase() == target);
- if !target_known {
- anyhow::bail!(
- "Could not find package \"{}\" in your project",
- args.package
- );
- }
-
- // Parse the version constraint the user is asking about
- let version_constraint = mozart_semver::VersionConstraint::parse(&args.version)
- .map_err(|e| anyhow::anyhow!("Invalid version constraint '{}': {}", args.version, e))?;
-
- let recursive = args.tree || args.recursive;
- let needles = vec![target];
-
- let results = super::dependency::get_dependents(
- &packages,
- &needles,
- Some(&version_constraint),
- true, // inverted = prohibits mode
- recursive,
- )?;
-
- if results.is_empty() {
- console.write_stdout(
- &console_format!(
- "<info>{} {} can be installed.</info>",
- args.package,
- args.version
- ),
- mozart_core::console::Verbosity::Normal,
- );
- return Ok(());
- }
-
- if args.tree {
- super::dependency::print_tree(&results, 0, console);
- } else {
- super::dependency::print_table(&results, console);
- }
-
- // Fix #5: Print resolution hint message
- // Determine the appropriate composer command based on whether the needle
- // is in root's require or require-dev.
- let needle_lower = args.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, args.package, args.version
- ));
-
- // Fix #3: Return exit code 1 when prohibitors are found
- Err(mozart_core::exit_code::bail_silent(
- mozart_core::exit_code::GENERAL_ERROR,
- ))
+ super::dependency::do_execute(
+ cli,
+ console,
+ super::dependency::DoExecuteArgs {
+ package: &args.package,
+ version: Some(&args.version),
+ recursive: args.recursive,
+ tree: args.tree,
+ locked: args.locked,
+ inverted: true,
+ },
+ )
}