use clap::Parser as _;
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 as _;
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);
}
}
}