rite 0.4.2

Author, execute, and verify cryptographic key ceremonies (the `rite` CLI)
//! `rite report`: generate an HTML post-ceremony report from a transcript.

use clap::Args as ClapArgs;
use std::path::{Path, PathBuf};

use crate::common::{BrandingArgs, ThemeArg, build_branding_or_exit, write_document};
use rite_render::report::build_report_data;
use rite_runtime::read_verified_transcript;

#[derive(ClapArgs, Debug)]
pub struct Args {
    /// Transcript file or run output directory
    ///
    /// Accepts a `transcript.jsonl` file or the output directory produced by
    /// `rite run` (which contains `transcript.jsonl`).
    pub transcript: PathBuf,
    /// Output path (`-` for stdout)
    ///
    /// Defaults to `report.html` next to the transcript.
    #[arg(long, short)]
    pub output: Option<PathBuf>,
    /// Document theme
    #[arg(long, value_enum, default_value_t = ThemeArg::default())]
    pub theme: ThemeArg,
    #[command(flatten)]
    pub branding: BrandingArgs,
}

pub fn run(args: &Args) {
    let jsonl_path = resolve_transcript_path(&args.transcript);

    let loaded = match read_verified_transcript(&jsonl_path) {
        Ok(loaded) => loaded,
        Err(err) => {
            eprintln!(
                "rite report: could not read transcript at {}: {err}",
                jsonl_path.display(),
            );
            std::process::exit(1);
        }
    };

    let data = build_report_data(
        loaded.facts.iter().map(|t| (t.at, &t.fact)),
        loaded.fingerprint.as_str(),
    );
    let branding = build_branding_or_exit(&args.branding);
    let html =
        rite_render::render_report(&data, &branding, args.theme.into()).unwrap_or_else(|e| {
            eprintln!("rite report: failed to render report: {e}");
            std::process::exit(2);
        });

    let default = jsonl_path.with_file_name("report.html");
    write_document(&html, args.output.as_deref(), &default);
}

/// Accept either the JSONL file directly or the parent output directory.
fn resolve_transcript_path(input: &Path) -> PathBuf {
    if input.is_dir() {
        input.join("transcript.jsonl")
    } else {
        input.to_path_buf()
    }
}