#[cfg(unix)]
#[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
mod commands;
mod dump;
use clap::Parser;
use tracing::info;
#[derive(Parser)]
#[command(name = "praxis")]
struct Cli {
#[arg(short = 'c', long = "config")]
config: Option<String>,
#[arg(short = 'T', long = "dump", conflicts_with = "validate")]
dump: bool,
#[arg(short = 't', long = "validate")]
validate: bool,
}
#[expect(clippy::print_stderr, reason = "fatal error output")]
fn main() {
let cli = Cli::parse();
let explicit = cli.config.or_else(|| std::env::var("PRAXIS_CONFIG").ok());
if cli.validate {
if let Err(e) = commands::load_and_validate_for_cli(explicit.as_deref()) {
eprintln!("invalid configuration: {e}");
std::process::exit(1);
}
return;
}
if cli.dump {
if let Err(e) = commands::run_dump(explicit.as_deref()) {
eprintln!("dump failed: {e}");
std::process::exit(1);
}
return;
}
let config_path = praxis::resolve_config_path(explicit.as_deref());
let config = praxis::load_config(explicit.as_deref()).unwrap_or_else(|e| praxis::fatal(&e));
praxis::init_tracing(&config).unwrap_or_else(|e| praxis::fatal(&e));
info!("starting server");
praxis::run_server(config, config_path)
}
#[cfg(test)]
#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, reason = "tests")]
mod tests {
use clap::Parser as _;
use super::Cli;
#[test]
fn cli_validate_short_flag() {
let cli = Cli::parse_from(["praxis", "-t"]);
assert!(cli.validate, "-t should set validate to true");
assert!(cli.config.is_none(), "config should be None");
}
#[test]
fn cli_validate_long_flag() {
let cli = Cli::parse_from(["praxis", "--validate"]);
assert!(cli.validate, "--validate should set validate to true");
}
#[test]
fn cli_validate_with_config() {
let cli = Cli::parse_from(["praxis", "-t", "-c", "custom.yaml"]);
assert!(cli.validate, "-t should set validate to true");
assert_eq!(cli.config.as_deref(), Some("custom.yaml"), "-c should set config path");
}
#[test]
fn cli_default_no_validate() {
let cli = Cli::parse_from(["praxis"]);
assert!(!cli.validate, "validate should default to false");
assert!(!cli.dump, "dump should default to false");
}
#[test]
fn cli_dump_short_flag() {
let cli = Cli::parse_from(["praxis", "-T"]);
assert!(cli.dump, "-T should set dump to true");
assert!(!cli.validate, "validate should remain false");
}
#[test]
fn cli_dump_long_flag() {
let cli = Cli::parse_from(["praxis", "--dump"]);
assert!(cli.dump, "--dump should set dump to true");
}
#[test]
fn cli_dump_with_config() {
let cli = Cli::parse_from(["praxis", "-T", "-c", "custom.yaml"]);
assert!(cli.dump, "-T should set dump to true");
assert_eq!(cli.config.as_deref(), Some("custom.yaml"), "-c should set config path");
}
#[test]
fn cli_dump_conflicts_with_validate() {
let result = Cli::try_parse_from(["praxis", "--dump", "--validate"]);
assert!(result.is_err(), "--dump and --validate should conflict");
}
#[test]
fn cli_dump_short_conflicts_with_validate_short() {
let result = Cli::try_parse_from(["praxis", "-T", "-t"]);
assert!(result.is_err(), "-T and -t should conflict");
}
}