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— itsCellis 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,markdownandscaffoldisstd-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_csvandcsv::to_delimitedlay 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::placegives 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, theffi::catchpanic 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::linkandmarkdown::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(featurerefine) — 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 owncleanupstage in either order.ai(featureai, native only) — VLM-based image understanding and AI-assisted markdown refine, active only when a caller supplies an API key.cli_update(featurecli-update, for the*-clicrates) — the update notice (cached for a day; off by flag, environment variable, CI or a non-terminal stderr) and theupdatecommand’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_errorand<name>_last_error_kindover aLastErrorSlot. - 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.