use clap::Parser; use mozart::commands; use mozart_core::MOZART_VERSION; use mozart_core::exit_code; use tracing_subscriber::{EnvFilter, fmt, prelude::*}; fn init_tracing(profile: bool, verbose: u8, quiet: bool) { // MOZART_LOG environment variable takes highest priority. if let Ok(env_filter) = EnvFilter::try_from_env("MOZART_LOG") { tracing_subscriber::registry() .with(fmt::layer().with_writer(std::io::stderr)) .with(env_filter) .init(); return; } if profile { let filter = match verbose { 0 => "mozart=info", 1 | 2 => "mozart=debug", _ => "mozart=trace", }; tracing_subscriber::registry() .with( fmt::layer() .with_writer(std::io::stderr) .with_timer(fmt::time::uptime()) .with_span_events(fmt::format::FmtSpan::CLOSE), ) .with(EnvFilter::new(filter)) .init(); } else if verbose >= 3 && !quiet { tracing_subscriber::registry() .with(fmt::layer().with_writer(std::io::stderr).with_target(false)) .with(EnvFilter::new("mozart=debug")) .init(); } // Otherwise: no subscriber installed → tracing macros are effectively zero-cost no-ops. } #[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!( "Mozart version {} {}", MOZART_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!( "PHP version {} ({})", 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; }; if cli.no_cache { println!("Disabling cache usage"); // SAFETY: single-threaded at this point; no other threads have started yet. #[cfg(windows)] unsafe { std::env::set_var("COMPOSER_CACHE_DIR", "nul"); } #[cfg(not(windows))] unsafe { std::env::set_var("COMPOSER_CACHE_DIR", "/dev/null"); } } init_tracing(cli.profile, cli.verbose, cli.quiet); match commands::execute(&cli).await { Ok(()) => {} Err(e) => { // Check if this is a structured MozartError with a specific exit code. if let Some(mozart_err) = e.downcast_ref::() { // Only print a message when there is one (bail_silent produces empty message). if !mozart_err.message.is_empty() { eprintln!( "{}", mozart_core::console_format!("{}", mozart_err.message) ); } std::process::exit(mozart_err.exit_code); } // Generic anyhow error — print and exit with GENERAL_ERROR. eprintln!("{}", mozart_core::console_format!("{e:#}")); std::process::exit(exit_code::GENERAL_ERROR); } } }