Rust API

Crates

Crate Purpose
gridwell-ir IR types, parsing, validation
gridwell-core Shared utilities (colors, lengths, font sizes, XML escaping)
gridwell-layout The resolved table every writer renders: visible grid, spans, style cascade
gridwell-ooxml Deterministic zip packaging for the DOCX/XLSX/PPTX writers
gridwell-render The format registry: Writer trait, every format by name, options as JSON
gridwell-writer-html HTML renderer
gridwell-writer-latex LaTeX renderer
gridwell-writer-typst Typst renderer
gridwell-writer-rtf RTF renderer
gridwell-writer-svg SVG renderer
gridwell-writer-ansi Terminal renderer
gridwell-writer-pandoc Pandoc AST renderer
gridwell-writer-quarto Quarto renderer
gridwell-writer-docx Word renderer
gridwell-writer-xlsx Excel renderer
gridwell-writer-pptx PowerPoint renderer

Parsing

use gridwell_ir::Table;

let json = std::fs::read_to_string("table.json")?;
let table = Table::from_json(&json)?;

Validation

let errors = table.validate();
if errors.is_empty() {
    println!("Table is valid");
} else {
    for err in &errors {
        eprintln!("Validation error: {err}");
    }
}

Rendering by format name

The registry validates the table, looks up the format, and checks the options. This is what the CLI and the C, Python and R bindings use.

use gridwell_render::{render, Output, REGISTRY};

let options = serde_json::json!({ "inline_styles": true });
match render(&table, "html", Some(&options))? {
    Output::Text(html) => println!("{html}"),
    Output::Binary(bytes) => std::fs::write("out.bin", bytes)?,
}

// Every format, with its metadata and default options
for w in REGISTRY {
    println!("{} (.{}): {}", w.name(), w.extension(), w.default_options());
}

Saving to a file

save() renders and writes the bytes unchanged. With no format it is inferred from the extension (format_for_path()); options are a JSON string, with file defaults on top (ansi writes plain text unless color is set). It returns the writer used.

use std::path::Path;
use gridwell_render::{format_for_path, save};

save(&table, Path::new("table.rtf"), None, None)?;
save(&table, Path::new("table.json"), Some("quarto"), Some(r#"{"table_id": "t1"}"#))?;
assert_eq!(format_for_path(Path::new("T.DOCX"))?.name(), "docx");

Errors are one gridwell_render::RenderError: UnknownFormat, WrongKind, InvalidTable, InvalidOptions or Writer, and from save()/format_for_path() NoExtension, UnknownExtension, AmbiguousExtension (.json is pandoc and quarto) or Io (with the path).

Using a writer directly

Each writer crate can also be used on its own. Its functions take a &Table and assume it is valid: call table.validated() (or validate()) first.

use gridwell_writer_html::{render_html, HtmlWriter, HtmlWriterConfig};

// Default settings
let html = render_html(&table)?;

// Custom config
let config = HtmlWriterConfig {
    inline_styles: true,
    pretty_print: false,
    class_prefix: "my-table".to_string(),
};
let writer = HtmlWriter::with_config(config);
let html = writer.render(&table)?;

All Text Writers

gridwell_writer_html::render_html(&table)?;
gridwell_writer_latex::render_latex(&table)?;
gridwell_writer_typst::render_typst(&table)?;
gridwell_writer_rtf::render_rtf(&table)?;
gridwell_writer_svg::render_svg(&table)?;
gridwell_writer_ansi::render_ansi(&table)?;
gridwell_writer_pandoc::render_pandoc(&table)?;
gridwell_writer_quarto::render_quarto(&table)?;

Binary Rendering

use gridwell_writer_docx::render_docx;
use gridwell_writer_xlsx::render_xlsx;
use gridwell_writer_pptx::render_pptx;

let docx_bytes = render_docx(&table)?;
std::fs::write("output.docx", &docx_bytes)?;

Error Handling

use gridwell_ir::{Table, ParseError};

match Table::from_json(input) {
    Ok(table) => { /* use table */ },
    Err(ParseError::Json(e)) => eprintln!("Invalid JSON: {e}"),
}