diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-23 01:23:40 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-23 01:23:40 +0900 |
| commit | c7a5859e1770dbc7cbc7cb1785c34b880162dfac (patch) | |
| tree | 58081330c415776c9020771f12227889abcffa12 /crates/mozart/src/commands/global.rs | |
| parent | 7372dc668476cde4bcb789e586cb9a65af1afca0 (diff) | |
| download | php-mozart-c7a5859e1770dbc7cbc7cb1785c34b880162dfac.tar.gz php-mozart-c7a5859e1770dbc7cbc7cb1785c34b880162dfac.tar.zst php-mozart-c7a5859e1770dbc7cbc7cb1785c34b880162dfac.zip | |
fix(global): add ~/.composer fallback, XDG auto-detection, and no-arg handling
Rewrite composer_home() with Composer-compatible resolution: detect XDG
environment (any XDG_* var or /etc/xdg), prefer existing directories,
and fall back to ~/.composer for legacy systems. Consolidate duplicate
composer_home_dir() from global.rs into shared config_helpers. Accept
no subcommand gracefully with a helpful error instead of a parse error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src/commands/global.rs')
| -rw-r--r-- | crates/mozart/src/commands/global.rs | 99 |
1 files changed, 25 insertions, 74 deletions
diff --git a/crates/mozart/src/commands/global.rs b/crates/mozart/src/commands/global.rs index dfa4fc2..12b3a4e 100644 --- a/crates/mozart/src/commands/global.rs +++ b/crates/mozart/src/commands/global.rs @@ -1,10 +1,9 @@ use clap::Args; -use std::path::PathBuf; #[derive(Args)] pub struct GlobalArgs { /// The command name to run - pub command_name: String, + pub command_name: Option<String>, /// Arguments to pass to the command #[arg(trailing_var_arg = true, allow_hyphen_values = true)] @@ -21,7 +20,16 @@ pub async fn execute( use clap::Parser as _; use std::fs; - let home = composer_home_dir()?; + let command_name = match &args.command_name { + Some(name) => name.clone(), + None => { + anyhow::bail!( + "The global command requires a subcommand, e.g. `mozart global require package/name`" + ); + } + }; + + let home = super::config_helpers::composer_home(); fs::create_dir_all(&home)?; @@ -36,7 +44,7 @@ pub async fn execute( argv.extend(append_global_options(cli)); argv.push("--working-dir".to_string()); argv.push(home.to_string_lossy().into_owned()); - argv.push(args.command_name.clone()); + argv.push(command_name); argv.extend(args.args.iter().cloned()); let new_cli = super::Cli::try_parse_from(&argv)?; @@ -45,26 +53,6 @@ pub async fn execute( // ─── Helpers ───────────────────────────────────────────────────────────────── -fn composer_home_dir() -> anyhow::Result<PathBuf> { - if let Ok(val) = std::env::var("COMPOSER_HOME") - && !val.is_empty() - { - return Ok(PathBuf::from(val)); - } - - if let Ok(xdg) = std::env::var("XDG_CONFIG_HOME") - && !xdg.is_empty() - { - return Ok(PathBuf::from(xdg).join("composer")); - } - - let home = std::env::var("HOME") - .map(PathBuf::from) - .map_err(|_| anyhow::anyhow!("Cannot determine home directory: $HOME is not set"))?; - - Ok(home.join(".config").join("composer")) -} - fn append_global_options(cli: &super::Cli) -> Vec<String> { let mut opts: Vec<String> = Vec::new(); @@ -119,54 +107,6 @@ mod tests { Cli::try_parse_from(["mozart", "about"]).unwrap() } - // ── composer_home_dir tests ─────────────────────────────────────────────── - - /// Guards env-var mutations so the three composer_home_dir tests - /// cannot race each other when `cargo test` runs them in parallel. - static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(()); - - #[test] - fn test_composer_home_dir_from_env() { - let _lock = ENV_MUTEX.lock().unwrap(); - // SAFETY: test-only; protected by ENV_MUTEX - unsafe { - std::env::set_var("COMPOSER_HOME", "/tmp/test-composer-home"); - } - let result = composer_home_dir().unwrap(); - unsafe { - std::env::remove_var("COMPOSER_HOME"); - } - assert_eq!(result, PathBuf::from("/tmp/test-composer-home")); - } - - #[test] - fn test_composer_home_dir_xdg() { - let _lock = ENV_MUTEX.lock().unwrap(); - // SAFETY: test-only; protected by ENV_MUTEX - unsafe { - std::env::remove_var("COMPOSER_HOME"); - std::env::set_var("XDG_CONFIG_HOME", "/tmp/test-xdg-config"); - } - let result = composer_home_dir().unwrap(); - unsafe { - std::env::remove_var("XDG_CONFIG_HOME"); - } - assert_eq!(result, PathBuf::from("/tmp/test-xdg-config/composer")); - } - - #[test] - fn test_composer_home_dir_default() { - let _lock = ENV_MUTEX.lock().unwrap(); - // SAFETY: test-only; protected by ENV_MUTEX - unsafe { - std::env::remove_var("COMPOSER_HOME"); - std::env::remove_var("XDG_CONFIG_HOME"); - } - let result = composer_home_dir().unwrap(); - let home = std::env::var("HOME").map(PathBuf::from).unwrap(); - assert_eq!(result, home.join(".config").join("composer")); - } - // ── append_global_options tests ─────────────────────────────────────────── #[test] @@ -222,7 +162,7 @@ mod tests { // Verify GlobalArgs parses correctly through the CLI let cli = Cli::try_parse_from(["mozart", "global", "require", "vendor/package"]).unwrap(); if let Some(Commands::Global(args)) = cli.command { - assert_eq!(args.command_name, "require"); + assert_eq!(args.command_name, Some("require".to_string())); assert_eq!(args.args, vec!["vendor/package"]); } else { panic!("Expected Global command"); @@ -235,10 +175,21 @@ mod tests { let cli = Cli::try_parse_from(["mozart", "global", "require", "vendor/pkg", "--no-update"]) .unwrap(); if let Some(Commands::Global(args)) = cli.command { - assert_eq!(args.command_name, "require"); + assert_eq!(args.command_name, Some("require".to_string())); assert!(args.args.contains(&"--no-update".to_string())); } else { panic!("Expected Global command"); } } + + #[test] + fn test_global_args_no_subcommand() { + // Verify that no subcommand parses to None + let cli = Cli::try_parse_from(["mozart", "global"]).unwrap(); + if let Some(Commands::Global(args)) = cli.command { + assert_eq!(args.command_name, None); + } else { + panic!("Expected Global command"); + } + } } |
