/// Run conversion using a callback-based visitor.
///
/// Returns a heap-allocated result on success, or null on failure.
/// Check `{{ prefix }}_last_error_code` / `{{ prefix }}_last_error_context` for error details.
/// The returned pointer must be freed with the matching result free function.
///
/// # Safety
///
/// `html` must be a valid, non-null, null-terminated UTF-8 string.
/// `options` must be a valid pointer or null.
/// `visitor` must have been created with `{{ prefix }}_visitor_create`, or be null.
/// Returned pointer must be freed with the matching result free function.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn {{ with_visitor_fn_name }}(
{% for param in params %}
{{ param }},
{% endfor %}
visitor: *mut {{ pascal_prefix }}Visitor,
) -> *mut {{ return_type }} {
clear_last_error();
{{ param_conversions }} struct VisitorRef(*mut {{ pascal_prefix }}Visitor);
impl std::fmt::Debug for VisitorRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VisitorRef").finish_non_exhaustive()
}
}
// SAFETY: VisitorRef is a thin wrapper around a raw pointer to {{ pascal_prefix }}Visitor which
// is itself Send + Sync. The caller guarantees the pointer remains valid during conversion.
unsafe impl Send for VisitorRef {}
// SAFETY: see Send impl above.
unsafe impl Sync for VisitorRef {}
impl {{ trait_path }} for VisitorRef {
{{ visitor_ref_methods }} }
let visitor_handle: Option<std::sync::Arc<std::sync::Mutex<dyn {{ trait_path }} + Send>>> = if visitor.is_null() {
None
} else {
Some(std::sync::Arc::new(std::sync::Mutex::new(VisitorRef(visitor))))
};
{{ call }}
Ok(result) => Box::into_raw(Box::new(result)),
Err(e) => {
set_last_error(2, &e.to_string());
std::ptr::null_mut()
}
}
}