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.
  • 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 ffi, kind 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

  • 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.
  • 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.
  • 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.
ffi
Thread-local last-error storage and the panic guard, for a C ABI that returns sentinels rather than error values.
kind
Error-kind numbering for the un* family: the values that are already shared, and the bands that keep future ones from colliding.
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.