📄 src/typst_renderer/mod.rs
mod receipt_world;

use std::fmt::Display;

use png::EncodingError;
use typst::{diag::SourceDiagnostic, ecow::EcoVec, layout::PagedDocument};

use self::receipt_world::ReceiptWorld;
use super::receipt_printer::ReceiptPrinter;

pub fn render_typst_receipt(content: &str) -> Result<Vec<u8>, TypstRenderError> {
    let world = ReceiptWorld::new(content);
    let document: PagedDocument = typst::compile(&world)
        .output
        .map_err(TypstRenderError::Compilation)?;

    let page = match &document.pages[..] {
        [] => return Err(TypstRenderError::NoOutput),
        [page] => page,
        _ => return Err(TypstRenderError::MultiPageOutput),
    };

    let pixmap = typst_render::render(page, ReceiptPrinter::IMAGE_PIXELS_PER_PT);
    let png = pixmap.encode_png().map_err(TypstRenderError::Rendering)?;

    Ok(png)
}

#[derive(Debug)]
pub enum TypstRenderError {
    Compilation(EcoVec<SourceDiagnostic>),
    NoOutput,
    MultiPageOutput,
    Rendering(EncodingError),
}

impl Display for TypstRenderError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TypstRenderError::Compilation(diagnostics) => {
                for diagnostic in diagnostics {
                    writeln!(f, "Compile: {}: {:?}", diagnostic.message, diagnostic.trace)?;
                }
                Ok(())
            }
            TypstRenderError::NoOutput => write!(f, "No output"),
            TypstRenderError::MultiPageOutput => write!(f, "Multi-page output"),
            TypstRenderError::Rendering(encoding_error) => encoding_error.fmt(f),
        }
    }
}