tricord 0.1.5

Run a command and report its process tree's CPU, memory, and I/O usage
Documentation
//! Entry point for the `tricorder` binary.

use std::{path::PathBuf, process::ExitCode};

use clap::Parser;
use tricord::{
    SchemaMode,
    cli::Args,
    run::{RunOptions, run_command},
};

fn main() -> ExitCode {
    let args = Args::parse();

    env_logger::Builder::new().filter_level(args.log_level()).format_timestamp_secs().init();

    let Some((command, command_args)) = args.command.split_first() else {
        eprintln!("tricorder: error: no command given (separate with `--`)");
        return ExitCode::from(2);
    };
    let command = command.clone();
    let command_args = command_args.to_vec();

    let options = RunOptions {
        interval: args.interval_duration(),
        output_path: args.out.clone().into_boxed_path(),
        format: args.format.into(),
        force_summary: args.summary,
        trace_path: args.trace.clone().map(PathBuf::into_boxed_path),
        markdown_path: args.export_markdown.clone().map(PathBuf::into_boxed_path),
        schema_mode: if args.snakemake { SchemaMode::SnakemakeStrict } else { SchemaMode::Full },
    };

    match run_command(&command, &command_args, &options) {
        Ok(outcome) => clamp_exit_code(outcome.exit_code()),
        Err(err) => {
            eprintln!("tricorder: error: {err}");
            ExitCode::from(127)
        }
    }
}

/// `ExitCode` only carries values 0..=255; clamp negative or out-of-range
/// values to a sensible byte. `ExitCode::from` accepts `u8` directly so the
/// rare case of a child exit code outside that range doesn't panic.
fn clamp_exit_code(code: i32) -> ExitCode {
    match u8::try_from(code) {
        Ok(byte) => ExitCode::from(byte),
        Err(_) => ExitCode::from(1),
    }
}