use std::io::IsTerminal;
use std::process::ExitCode;
use std::sync::Arc;
use clap::Parser;
use acme_proxy::cli::schema::{SchemaPlan, plan_schema};
use acme_proxy::cli::{Cli, Command, LogLevel, LoggingPlan, dispatch, generate, plan_logging};
use acme_proxy_core::config::Config;
use acme_proxy_server::logging::init_command_logging;
use acme_proxy_server::logging::init_logging;
use acme_proxy_store::db::Database;
#[tokio::main]
async fn main() -> ExitCode {
let cli = Cli::parse();
let palette = acme_proxy::cli::style::resolve(
cli.color,
std::io::stderr().is_terminal(),
std::env::var("NO_COLOR").ok().as_deref(),
);
if let Some(command @ (Command::Completions { .. } | Command::Man)) = &cli.command {
if let Err(error) = generate::write(command, &mut std::io::stdout().lock()) {
eprintln!("{}", palette.bad(&error.to_string()));
return ExitCode::from(error.exit_code());
}
return ExitCode::SUCCESS;
}
let config = Arc::new(Config::load().unwrap_or_else(|error| {
eprintln!("{}", palette.bad(&format!("configuration error: {error}")));
std::process::exit(1);
}));
let installed = plan_logging(
cli.command.as_ref(),
cli.log_level,
std::env::var("RUST_LOG").ok().as_deref(),
);
let logging = match installed {
LoggingPlan::Silent => Ok(()),
LoggingPlan::Server => {
init_logging(&config.logging, cli.log_level.map(LogLevel::directive))
}
LoggingPlan::Command => init_command_logging(cli.log_level.map(LogLevel::directive)),
};
logging.unwrap_or_else(|error| {
eprintln!("{}", palette.bad(&error));
std::process::exit(1);
});
let database = Arc::new(
Database::open(&config.database.url)
.await
.unwrap_or_else(|error| {
eprintln!(
"{}",
palette.bad(&format!(
"database error: cannot open {}: {error}",
config.database.url
))
);
std::process::exit(1);
}),
);
if plan_schema(cli.command.as_ref()) == SchemaPlan::Require {
match database.pending_migrations().await {
Ok(pending) if pending.is_empty() => {}
Ok(pending) => {
eprintln!(
"{}",
palette.bad(&format!(
"database error: the schema is {} migration(s) behind; \
run `acme-proxy migrate` first",
pending.len()
))
);
std::process::exit(1);
}
Err(error) => {
eprintln!(
"{}",
palette.bad(&format!(
"database error: cannot read the schema version: {error}"
))
);
std::process::exit(1);
}
}
}
let stdin = std::io::stdin();
let mut reader = stdin.lock();
if let Err(error) = dispatch(
cli.command,
cli.yes,
cli.color,
&mut reader,
&config,
database,
)
.await
{
eprintln!("{}", palette.bad(&error.to_string()));
return ExitCode::from(error.exit_code());
}
ExitCode::SUCCESS
}