rusdox 0.1.0

Generate DOCX and PDF from YAML at Rust speed.
Documentation
use std::fs;
use std::path::Path;
use std::time::{Duration, Instant};

use rusdox::spec::DocumentSpec;
use rusdox::studio::Studio;

const TOTAL_PAGES: usize = 1000;
const UNIQUE_TEMPLATES: usize = 10;
const SPEC_PATH: &str = "examples/stress/stress_1000_pages.yaml";

fn main() -> Result<(), rusdox::DocxError> {
    let spec_path = Path::new(SPEC_PATH);
    if !spec_path.exists() {
        return Err(rusdox::DocxError::Parse(format!(
            "stress spec not found at {} (run scripts/generate_stress_yaml.sh first)",
            spec_path.display()
        )));
    }

    let studio = Studio::from_default_file_or_default()?;
    let overall_start = Instant::now();

    let parse_start = Instant::now();
    let spec = DocumentSpec::load_from_path(spec_path)?;
    let parse_duration = parse_start.elapsed();

    let compose_start = Instant::now();
    let document = studio.compose(&spec);
    let compose_duration = compose_start.elapsed();

    let output_name = spec.output_name.as_deref().unwrap_or("stress-1000-pages");
    let stats = studio.save_named(&document, output_name)?;
    let total_duration = overall_start.elapsed();
    let yaml_bytes = fs::metadata(spec_path)?.len();

    println!("stress spec: {}", spec_path.display());
    println!("logical pages: {TOTAL_PAGES}");
    println!("unique templates: {UNIQUE_TEMPLATES}");
    println!("yaml parse: {}", format_duration(parse_duration));
    println!("doc compose: {}", format_duration(compose_duration));
    println!("docx write: {}", format_duration(stats.docx_write));
    println!("pdf render: {}", format_duration(stats.pdf_render));
    println!("output total: {}", format_duration(total_duration));
    println!("yaml size: {}", format_bytes(yaml_bytes));
    println!("docx size: {}", format_bytes(stats.docx_bytes));
    println!("pdf size: {}", format_bytes(stats.pdf_bytes));

    Ok(())
}

fn format_duration(duration: Duration) -> String {
    let milliseconds = duration.as_secs_f64() * 1000.0;
    format!("{milliseconds:.2} ms")
}

fn format_bytes(bytes: u64) -> String {
    let kib = 1024.0;
    let mib = kib * 1024.0;
    let gib = mib * 1024.0;
    let value = bytes as f64;

    if value >= gib {
        format!("{:.2} GiB", value / gib)
    } else if value >= mib {
        format!("{:.2} MiB", value / mib)
    } else if value >= kib {
        format!("{:.2} KiB", value / kib)
    } else {
        format!("{bytes} B")
    }
}