1use std::ffi::CStr;
2
3use crate::c_api::{mts_status_t, MTS_SUCCESS, MTS_CALLBACK_ERROR};
4
5pub use metatensor_sys::Error;
6
7fn get_last_error(status: Option<mts_status_t>) -> Error {
8 let mut message = std::ptr::null();
9 let mut origin = std::ptr::null();
10 let mut user_data = std::ptr::null_mut();
11 let last_error_status = unsafe {
12 crate::c_api::mts_last_error(
13 &mut message, &mut origin, &mut user_data
14 )
15 };
16
17 if last_error_status != MTS_SUCCESS {
18 return Error {
19 code: status,
20 message: "INTERNAL ERROR: failed to get the last error".into(),
21 };
22 }
23
24 let message = if message.is_null() {
25 "<no message provided>"
26 } else {
27 unsafe { CStr::from_ptr(message).to_str().unwrap_or("<invalid UTF-8 in error message>") }
28 };
29
30 let origin = if origin.is_null() {
31 "<no origin provided>"
32 } else {
33 unsafe { CStr::from_ptr(origin).to_str().unwrap_or("<invalid UTF-8 in error origin>") }
34 };
35
36 if !user_data.is_null() && origin == "Rust Error" {
37 let rust_error = unsafe {
38 user_data.cast::<Error>().as_ref().expect("should not be null")
39 };
40 return rust_error.clone();
41 }
42
43 return Error {
44 code: status,
45 message: message.to_owned(),
46 };
47}
48
49unsafe extern "C" fn error_deleter(data: *mut std::ffi::c_void) {
50 let _ = unsafe { Box::from_raw(data.cast::<Error>()) };
51}
52
53pub(crate) fn store_last_error(error: Error) -> mts_status_t {
56 let c_message = std::ffi::CString::new(error.message.clone()).expect("found NULL byte in error message");
57 let c_origin = std::ffi::CString::new("Rust Error").expect("found NULL byte in error origin");
58 let status = unsafe {
59 crate::c_api::mts_set_last_error(
60 c_message.as_ptr(),
61 c_origin.as_ptr(),
62 Box::into_raw(Box::new(error)).cast(),
63 Some(error_deleter),
64 )
65 };
66
67 check_status(status).expect("failed to set last error");
68
69 return MTS_CALLBACK_ERROR;
70}
71
72pub fn check_status(status: mts_status_t) -> Result<(), Error> {
74 if status == MTS_SUCCESS {
75 return Ok(())
76 } else {
77 return Err(get_last_error(Some(status)));
78 }
79}
80
81pub fn check_ptr<T>(ptr: *const T) -> Result<(), Error> {
83 if ptr.is_null() {
84 return Err(get_last_error(None));
85 }
86
87 return Ok(())
88}
89
90
91pub(crate) fn catch_unwind<F>(function: F) -> mts_status_t where F: FnOnce() -> Result<(), Error> + std::panic::UnwindSafe {
94 match std::panic::catch_unwind(function) {
95 Ok(Ok(())) => MTS_SUCCESS,
96 Ok(Err(e)) => {
97 return store_last_error(e);
98 },
99 Err(e) => {
100 return store_last_error(e.into());
101 }
102 }
103}