sim-cli 0.7.0

CLI tool for running and comparing Solana simulator backtests
Documentation
mod commands;
mod fetch;
mod output;
mod subscription;

use clap::{Parser, Subcommand};
use commands::{
    compare::{self, CompareArgs},
    ranges::{self, RangesArgs},
    run::{self, RunArgs},
    summarize::{self, SummarizeArgs},
    update,
    usage::{self, UsageArgs},
};
use console::style;
use sim_cli::signals::spawn_os_signal_handler;
use tokio_util::sync::CancellationToken;

/// Simulator CLI
/// Run and compare simulations against historical Solana slots
#[derive(Parser)]
struct Cli {
    /// Simulator endpoint hostname (e.g. staging.simulator.termina.technology)
    #[arg(
        long,
        global = true,
        env = "SIMULATOR_URL",
        default_value = "simulator.termina.technology"
    )]
    url: String,

    /// API key for authentication
    #[arg(long, global = true, env = "SIMULATOR_API_KEY", default_value = "")]
    api_key: String,

    #[command(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    /// Run a backtest simulation over a slot range
    Run(RunArgs),
    /// Compare two simulation output files (baseline vs experiment)
    Compare(CompareArgs),
    /// Extract and print a summary of a simulation output file
    Summarize(SummarizeArgs),
    /// Update the sim binary to the latest published version
    Update,
    /// List available slot ranges on the simulator server
    Ranges(RangesArgs),
    /// Show API key usage metrics for a time window
    Usage(UsageArgs),
}

#[tokio::main]
async fn main() {
    let _ = rustls::crypto::aws_lc_rs::default_provider()
        .install_default()
        .map_err(|_| ())
        .ok();

    let cli = Cli::parse();
    let cancellation = CancellationToken::new();
    // Only `run` observes the token; installing the tokio signal handler for
    // the other commands would swallow Ctrl+C instead of terminating them
    // (tokio replaces the OS SIGINT disposition process-wide).
    if matches!(cli.command, Command::Run(_)) {
        spawn_os_signal_handler(cancellation.clone());
    }

    let result = match cli.command {
        Command::Run(args) => run::run(args, cli.url, cli.api_key, cancellation.clone()).await,
        Command::Compare(args) => compare::compare(args).await,
        Command::Summarize(args) => summarize::summarize(args),
        Command::Update => update::update().await,
        Command::Ranges(args) => ranges::ranges(args, cli.url).await,
        Command::Usage(args) => usage::usage(args, cli.url, cli.api_key).await,
    };

    // Render failures as a single clean line (the error chain joined with `: `)
    // and exit non-zero. Avoids eyre's default `{:?}` dump with its `Location:`
    // and backtrace, which is noise for an operator reading run output.
    if let Err(e) = result {
        eprintln!("{} {:#}", style("").red().bold(), e);
        std::process::exit(1);
    }
}