use clap::Parser;
use std::process::ExitCode;
mod cli;
mod cmd;
mod command_router;
mod config;
mod diagnostic;
mod load;
mod lock;
mod model;
mod parse;
mod render;
mod resource_plan;
mod scan;
mod schema;
mod signature;
mod terminal_md;
mod theme;
mod ui;
mod validate;
mod verification;
mod write;
#[cfg(feature = "tui")]
mod tui;
pub(crate) use cli::*;
use config::Config;
use diagnostic::{Diagnostic, DiagnosticCode, DiagnosticLevel};
fn main() -> ExitCode {
let cli = Cli::parse();
let result = run(&cli);
match result {
Ok(diags) => {
let has_errors = diags.iter().any(|d| d.level == DiagnosticLevel::Error);
let has_warnings = diags.iter().any(|d| d.level == DiagnosticLevel::Warning);
for diag in &diags {
ui::diagnostic(diag);
}
if has_errors {
ExitCode::FAILURE
} else if has_warnings {
if matches!(
cli.command,
Commands::Check {
deny_warnings: true,
..
} | Commands::Check {
has_active: true,
..
}
) {
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
} else {
ExitCode::SUCCESS
}
}
Err(e) => {
if let Some(diag) = e.downcast_ref::<Diagnostic>() {
ui::diagnostic(diag);
} else {
ui::error(&e);
}
ExitCode::FAILURE
}
}
}
fn run(cli: &Cli) -> anyhow::Result<Vec<Diagnostic>> {
let config = Config::load(cli.config.as_deref())?;
let op = write::WriteOp::from_dry_run(cli.dry_run);
let plan = command_router::CommandPlan::from_parsed(&cli.command, cli.dry_run)?;
let lock_disposition = plan.lock_disposition();
let _guard = if matches!(
lock_disposition,
command_router::LockDisposition::GovRootExclusive
) {
if matches!(
plan.op,
command_router::Op::Builtin(command_router::BuiltinOp::Init { .. })
) {
let gov_root = config.gov_root.as_path();
if !op.is_preview() && !gov_root.exists() {
std::fs::create_dir_all(gov_root).map_err(|e| {
Diagnostic::new(
DiagnosticCode::E0901IoError,
format!("Failed to create gov root: {}", e),
gov_root.display().to_string(),
)
})?;
}
}
if op.is_preview()
&& matches!(
plan.op,
command_router::Op::Builtin(command_router::BuiltinOp::Init { .. })
)
{
None
} else {
Some(lock::acquire_gov_lock(&config)?)
}
} else {
None
};
plan.execute(&config, op)
}