axiolid_capi/
diagnostics.rs

1use super::*;
2
3/// Copy structured metadata for the context's most recent error.
4/// # Safety
5/// Every non-null pointer must be aligned and valid for the documented read or write extent.
6#[no_mangle]
7pub unsafe extern "C" fn axiolid_v0_4_context_last_error_info(
8    context: AxiolidContextHandle,
9    out_info: *mut AxiolidErrorInfo,
10) -> AxiolidStatus {
11    boundary(|| {
12        if out_info.is_null() {
13            return AxiolidStatus::NullPointer;
14        }
15        let Some(context) = context_entry(context.0) else {
16            return AxiolidStatus::InvalidHandle;
17        };
18        let context = lock_unpoisoned(&context);
19        let Some(error) = &context.last_error else {
20            return AxiolidStatus::NoError;
21        };
22        let mut info = AxiolidErrorInfo {
23            status: error.status,
24            operation: error.operation,
25            tolerance: error.tolerance,
26            provider: [0; 64],
27            message_len: error.message.len(),
28            provider_len: 0,
29            reserved: [0; 7],
30        };
31        info.provider_len = copy_text(&error.provider, &mut info.provider);
32        // SAFETY: null was rejected; caller provides one writable error record.
33        unsafe { out_info.write(info) };
34        AxiolidStatus::Ok
35    })
36}
37
38/// Copy the last error message into a caller-owned UTF-8 buffer.
39///
40/// `out_required` receives the byte count including the trailing NUL. Passing a
41/// null buffer with zero capacity is the supported sizing query.
42/// # Safety
43/// Every non-null pointer must be aligned and valid for the documented read or write extent.
44#[no_mangle]
45pub unsafe extern "C" fn axiolid_v0_4_context_last_error_message(
46    context: AxiolidContextHandle,
47    buffer: *mut u8,
48    capacity: usize,
49    out_required: *mut usize,
50) -> AxiolidStatus {
51    boundary(|| {
52        if out_required.is_null() || (capacity != 0 && buffer.is_null()) {
53            return AxiolidStatus::NullPointer;
54        }
55        let Some(context) = context_entry(context.0) else {
56            return AxiolidStatus::InvalidHandle;
57        };
58        let context = lock_unpoisoned(&context);
59        let Some(error) = &context.last_error else {
60            return AxiolidStatus::NoError;
61        };
62        let required = error.message.len().saturating_add(1);
63        // SAFETY: null was rejected; caller provides one writable size value.
64        unsafe { out_required.write(required) };
65        if capacity < required {
66            return AxiolidStatus::BufferTooSmall;
67        }
68        // SAFETY: capacity was checked; caller promises a writable buffer of that size.
69        let destination = unsafe { std::slice::from_raw_parts_mut(buffer, required) };
70        destination[..error.message.len()].copy_from_slice(error.message.as_bytes());
71        destination[required - 1] = 0;
72        AxiolidStatus::Ok
73    })
74}
75
76/// Query live child-object counts for leak checks and shutdown assertions.
77/// # Safety
78/// Every non-null pointer must be aligned and valid for the documented read or write extent.
79#[no_mangle]
80pub unsafe extern "C" fn axiolid_v0_4_context_live_object_counts(
81    context: AxiolidContextHandle,
82    out_meshes: *mut usize,
83    out_results: *mut usize,
84) -> AxiolidStatus {
85    boundary(|| {
86        if out_meshes.is_null() || out_results.is_null() {
87            return AxiolidStatus::NullPointer;
88        }
89        let Some(context) = context_entry(context.0) else {
90            return AxiolidStatus::InvalidHandle;
91        };
92        let context = lock_unpoisoned(&context);
93        // SAFETY: null was rejected; caller provides two writable count values.
94        unsafe {
95            out_meshes.write(context.meshes.live_count());
96            out_results.write(context.results.live_count());
97        }
98        AxiolidStatus::Ok
99    })
100}