Skip to main content

Crate unparser_shared

Crate unparser_shared 

Source
Expand description

Shared plumbing for the un* document extraction family — unpdf, undoc, unhwp.

§What this crate is for

Those three libraries have the same shape — parse a container, build an intermediate representation, render Markdown — and expose it the same way: a Rust API, a C ABI, and C#, Python and WebAssembly bindings over that ABI. The document parts differ entirely and belong in each library. The parts that do not differ live here: thread-local last-error storage, catching panics before they cross extern "C", and the integer space the error classifications live in. VLM/AI-assisted extraction is also here now, as an opt-in feature — see Modules below.

§Scope, and what is kept out

  • No domain content. No document model, no rendering, no format knowledge. If a type would mention a page, a paragraph or a spreadsheet cell, it belongs upstream. Facts about the output syntax are not domain content: where CommonMark lets an emphasis delimiter stand is the same answer for every writer (markdown), and so is how RFC 4180 quotes a field and where a merged cell’s value goes on a CSV grid (csv — its Cell is the writer’s input shape, not a document model).
  • The crate root has no dependencies. Three published cdylibs link it statically, so a dependency here is a dependency in all of them. Everything in csv, ffi, kind, markdown and scaffold is std-only. Optional capabilities each bring their own dependency behind their own feature flag instead of widening this baseline.

It is std-only at the root — ffi::LastErrorSlot holds a CString and lives in a thread_local! — which suits every consumer including wasm32-unknown-unknown (the refine feature does too; ai is native-only — see its module docs).

§Modules

  • csv — a table as CSV (RFC 4180) or tab-separated text: csv::to_csv and csv::to_delimited lay the rows on their grid, a merged cell’s value at its top-left position and the positions it covers empty, and quote the fields.
  • grid — where a table’s cells sit: grid::place gives each cell’s grid column and the table’s width from the cells’ spans, a merged cell recorded once on the cell that owns it.
  • kind — the error-kind values the family already shares, and the bands that keep future ones from colliding. Read its docs before adding a kind anywhere.
  • ffi — ffi::LastErrorSlot, the ffi::catch panic guard, and the macros that export the ABI over them.
  • markdown — CommonMark syntax facts the Markdown writers share: markdown::emphasis_span, the part of a styled run emphasis delimiters can wrap; markdown::image, markdown::link and markdown::link_destination, which write a picture’s description, a link’s text and title, and a link’s target so that they stay inside the construct and read back as given.
  • refine (feature refine) — a lossless, idempotent markdown shape-refinement pass: table shape normalization, ordered-list renumbering, link/image path normalization, frontmatter normalization, and section anchors. Composes with a renderer’s own cleanup stage in either order.
  • ai (feature ai, native only) — VLM-based image understanding and AI-assisted markdown refine, active only when a caller supplies an API key.
  • cli_update (feature cli-update, for the *-cli crates) — the update notice (cached for a day; off by flag, environment variable, CI or a non-terminal stderr) and the update command’s self-replacement from the tool’s GitHub release.
  • scaffold — macros that assemble an entry point out of those primitives. Reach for these before writing the five steps by hand; the sentinel differs by return type and that is where hand-written entry points drift.

§A minimal library

use std::ffi::c_int;
use unparser_shared::ffi::{self, FfiError, LastErrorSlot};
use unparser_shared::kind;

// The slot lives here, not in unparser-shared — that is what keeps it per-library. See
// `ffi`'s module docs.
thread_local! {
    static LAST_ERROR: LastErrorSlot = const { LastErrorSlot::new() };
}

unparser_shared::export_last_error_abi!(LAST_ERROR, mylib_last_error, mylib_last_error_kind);

/// Returns the page count, or -1 on failure.
#[no_mangle]
pub extern "C" fn mylib_page_count(handle: *const u8) -> c_int {
    let result: Result<c_int, FfiError> = ffi::catch(|| {
        if handle.is_null() {
            return Err(ffi::invalid_argument("handle was null"));
        }
        Ok(7)
    });

    match result {
        Ok(count) => {
            LAST_ERROR.with(|slot| slot.clear());
            count
        }
        Err(error) => {
            LAST_ERROR.with(|slot| slot.set_error(&error));
            -1
        }
    }
}

assert_eq!(mylib_page_count(std::ptr::null()), -1);
assert_eq!(mylib_last_error_kind(), kind::INVALID_ARGUMENT);

let data = 0u8;
assert_eq!(mylib_page_count(&data), 7);
assert_eq!(mylib_last_error_kind(), kind::NONE);

Modules§

ai
VLM-based image understanding and AI-assisted markdown refine — active only when a caller supplies an AiConfig. Neither capability touches the network unless called explicitly; there is no ambient behavior change for a caller that never constructs a config.
cli_update
Update notice and self-update for the family’s command-line tools, from GitHub releases.
csv
Tables as delimited text: CSV (RFC 4180) and its tab-separated variant.
ffi
Thread-local last-error storage and the panic guard, for a C ABI that returns sentinels rather than error values.
grid
Where a table’s cells sit on its grid.
inflate
Reading compressed data from an untrusted file without letting it expand without bound.
kind
Error-kind numbering for the un* family: the values that are already shared, and the bands that keep future ones from colliding.
markdown
CommonMark syntax facts every Markdown writer in the family needs.
refine
This module refines the shape of already-rendered markdown without deleting any of its visible text.
scaffold
Macros that assemble a C entry point out of the primitives in crate::ffi.

Macros§

assert_stable_kinds
Assert that an error-kind enum’s discriminants are what they were.
export_bytes_getter
Declare an entry point that hands out an owned byte buffer selected by a C-string key.
export_count_getter
Declare an entry point that hands out a count.
export_free_bytes
Declare the release function for byte buffers this library hands out.
export_free_string
Declare the release function for strings this library hands out.
export_handle
Declare an opaque document handle and the function that frees it.
export_last_error_abi
Declare <name>_last_error and <name>_last_error_kind over a LastErrorSlot.
export_optional_string_getter
Declare an entry point that hands out a string which may legitimately be absent.
export_string_getter
Declare an entry point that hands out an owned string.
with_c_str
Read a nullable C string argument as &str, classifying both ways it can fail.