aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--crates/mozart-core/src/platform.rs17
-rw-r--r--crates/mozart/src/commands.rs13
-rw-r--r--crates/mozart/src/commands/bump.rs5
-rw-r--r--crates/mozart/src/commands/global.rs4
-rw-r--r--crates/mozart/src/main.rs35
5 files changed, 66 insertions, 8 deletions
diff --git a/crates/mozart-core/src/platform.rs b/crates/mozart-core/src/platform.rs
index 317a75f..819d8c9 100644
--- a/crates/mozart-core/src/platform.rs
+++ b/crates/mozart-core/src/platform.rs
@@ -265,6 +265,23 @@ pub fn parse_platform_info(output: &str) -> Vec<PlatformPackage> {
}
}
+/// Detect PHP version and binary path for `--version` display.
+///
+/// Returns `(PHP_VERSION, PHP_BINARY)` by running a single PHP invocation.
+/// Returns `None` if PHP is not available.
+pub fn detect_php_version_and_binary() -> Option<(String, String)> {
+ let output = std::process::Command::new("php")
+ .args(["-r", r#"echo PHP_VERSION, "\n", PHP_BINARY;"#])
+ .output()
+ .ok()?;
+ if !output.status.success() {
+ return None;
+ }
+ let stdout = String::from_utf8(output.stdout).ok()?;
+ let mut lines = stdout.lines();
+ Some((lines.next()?.to_owned(), lines.next()?.to_owned()))
+}
+
/// Try to detect the installed PHP version by running `php --version`.
pub fn detect_php_version() -> Option<String> {
let output = std::process::Command::new("php")
diff --git a/crates/mozart/src/commands.rs b/crates/mozart/src/commands.rs
index 968763f..e633526 100644
--- a/crates/mozart/src/commands.rs
+++ b/crates/mozart/src/commands.rs
@@ -35,10 +35,14 @@ pub mod update;
pub mod validate;
#[derive(clap::Parser)]
-#[command(name = "mozart", version, about = "A PHP dependency manager")]
+#[command(name = "mozart", about = "A PHP dependency manager")]
pub struct Cli {
#[command(subcommand)]
- pub command: Commands,
+ pub command: Option<Commands>,
+
+ /// Display version information
+ #[arg(short = 'V', long = "version")]
+ pub version: bool,
/// Increase the verbosity of messages: 1 for normal, 2 for more verbose, 3 for debug
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
@@ -238,7 +242,7 @@ impl Commands {
}
}
-#[tracing::instrument(skip(cli), fields(command = cli.command.name()))]
+#[tracing::instrument(skip(cli), fields(command = cli.command.as_ref().map(|c| c.name()).unwrap_or("none")))]
pub async fn execute(cli: &Cli) -> anyhow::Result<()> {
let console = mozart_core::console::Console::new(
cli.verbose,
@@ -247,7 +251,8 @@ pub async fn execute(cli: &Cli) -> anyhow::Result<()> {
cli.no_ansi,
cli.no_interaction,
);
- match &cli.command {
+ let command = cli.command.as_ref().expect("command must be set");
+ match command {
Commands::About(args) => about::execute(args, cli, &console).await,
Commands::Archive(args) => archive::execute(args, cli, &console).await,
Commands::Audit(args) => audit::execute(args, cli, &console).await,
diff --git a/crates/mozart/src/commands/bump.rs b/crates/mozart/src/commands/bump.rs
index b322dbe..e1b5f63 100644
--- a/crates/mozart/src/commands/bump.rs
+++ b/crates/mozart/src/commands/bump.rs
@@ -311,12 +311,13 @@ mod tests {
fn make_cli(working_dir: &std::path::Path) -> super::super::Cli {
super::super::Cli {
- command: super::super::Commands::Bump(BumpArgs {
+ command: Some(super::super::Commands::Bump(BumpArgs {
packages: vec![],
dev_only: false,
no_dev_only: false,
dry_run: false,
- }),
+ })),
+ version: false,
verbose: 0,
profile: false,
no_plugins: false,
diff --git a/crates/mozart/src/commands/global.rs b/crates/mozart/src/commands/global.rs
index 97d56d2..dfa4fc2 100644
--- a/crates/mozart/src/commands/global.rs
+++ b/crates/mozart/src/commands/global.rs
@@ -221,7 +221,7 @@ mod tests {
fn test_global_args_has_correct_command() {
// Verify GlobalArgs parses correctly through the CLI
let cli = Cli::try_parse_from(["mozart", "global", "require", "vendor/package"]).unwrap();
- if let Commands::Global(args) = cli.command {
+ if let Some(Commands::Global(args)) = cli.command {
assert_eq!(args.command_name, "require");
assert_eq!(args.args, vec!["vendor/package"]);
} else {
@@ -234,7 +234,7 @@ mod tests {
// Verify hyphen values in trailing args are accepted
let cli = Cli::try_parse_from(["mozart", "global", "require", "vendor/pkg", "--no-update"])
.unwrap();
- if let Commands::Global(args) = cli.command {
+ if let Some(Commands::Global(args)) = cli.command {
assert_eq!(args.command_name, "require");
assert!(args.args.contains(&"--no-update".to_string()));
} else {
diff --git a/crates/mozart/src/main.rs b/crates/mozart/src/main.rs
index ed7c4ad..8201d87 100644
--- a/crates/mozart/src/main.rs
+++ b/crates/mozart/src/main.rs
@@ -40,6 +40,41 @@ fn init_tracing(profile: bool, verbose: u8, quiet: bool) {
#[tokio::main]
async fn main() {
let cli = commands::Cli::parse();
+
+ if cli.version {
+ let build_date = option_env!("MOZART_BUILD_DATE").unwrap_or("source");
+ // Line 1 (stdout): getLongVersion() equivalent
+ println!(
+ "{}",
+ mozart_core::console_format!(
+ "<info>Mozart</info> version <comment>{}</comment> {}",
+ env!("CARGO_PKG_VERSION"),
+ build_date
+ )
+ );
+ // Line 2 (stderr): PHP version + binary path (matches Composer's output)
+ let (php_version, php_binary) = mozart_core::platform::detect_php_version_and_binary()
+ .unwrap_or_else(|| ("not found".into(), "php".into()));
+ eprintln!(
+ "{}",
+ mozart_core::console_format!(
+ "<info>PHP</info> version <comment>{}</comment> ({})",
+ php_version,
+ php_binary
+ )
+ );
+ // Line 3 (stderr): diagnose hint
+ eprintln!("Run the \"diagnose\" command to get more detailed diagnostics output.");
+ return;
+ }
+
+ let Some(ref _cmd) = cli.command else {
+ use clap::CommandFactory;
+ commands::Cli::command().print_help().ok();
+ println!();
+ return;
+ };
+
init_tracing(cli.profile, cli.verbose, cli.quiet);
match commands::execute(&cli).await {
Ok(()) => {}