Skip to main content

export_string_getter

Macro export_string_getter 

Source
macro_rules! export_string_getter {
    (
        $(#[$meta:meta])*
        $slot:path,
        $name:ident($handle:ident: $handle_ty:ty $(, $arg:ident: $arg_ty:ty)* $(,)?),
        $body:block
    ) => { ... };
}
Expand description

Declare an entry point that hands out an owned string.

$body runs inside catch and evaluates to Result<String, FfiError>. Everything around it is the same in every such entry point: clear the slot, reject a null handle, move the string into a CString, report invalid_output if it holds an interior NUL, record a failure and return null.

The handle parameter’s name is taken from the call site so that $body can project through it — { Ok((*doc).inner.title()) }. The handle is *const, since producing a string does not modify the document.

The null-handle message is "document is null", fixed here so the family reports it identically — the same reason invalid_output’s wording is fixed.

unparser_shared::export_string_getter!(
    /// The report's line at `index`, or a failure when it is out of range.
    ///
    /// # Safety
    ///
    /// - `doc` must be a valid handle.
    /// - The returned string must be freed with `demo2_free_string`.
    LAST_ERROR,
    demo2_line(doc: Demo2Document, index: c_int),
    {
        match unsafe { (&(*doc).inner.lines).get(index as usize) } {
            Some(line) => Ok(line.clone()),
            None => Err((unparser_shared::kind::OTHER, format!("no line {index}"))),
        }
    }
);

let doc = Box::into_raw(Box::new(Demo2Document {
    inner: Report { lines: vec!["first".to_string()] },
}));

let line = unsafe { demo2_line(doc, 0) };
assert_eq!(unsafe { CStr::from_ptr(line) }.to_str().unwrap(), "first");
unsafe { demo2_free_string(line) };

assert!(unsafe { demo2_line(doc, 9) }.is_null());
assert_eq!(demo2_last_error_kind(), unparser_shared::kind::OTHER);

unsafe { demo2_free_document(doc) };