Skip to main content

Crate uncore

Crate uncore 

Source
Expand description

Shared C-ABI plumbing and error-kind conventions 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 are the plumbing: thread-local last-error storage, catching panics before they cross extern "C", and the integer space the error classifications live in.

That plumbing was written three times. This crate is where it lives once.

§Scope, and what is kept out

Two rules keep this crate from becoming a dumping ground:

  • 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.
  • No dependencies. Three published cdylibs link this statically, so a dependency here is a dependency in all of them. Everything here is std.

It is std-only — ffi::LastErrorSlot holds a CString and lives in a thread_local! — which suits every consumer including wasm32-unknown-unknown.

§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.
  • ffiffi::LastErrorSlot, the ffi::catch panic guard, and the macros that export the ABI over them.
  • 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_char, c_int};
use uncore::ffi::{self, FfiError, LastErrorSlot};
use uncore::kind;

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

uncore::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§

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.
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.