1#![allow(clippy::missing_safety_doc)]
4use libc::c_char;
5use std::ffi::CString;
6use std::io;
7
8#[derive(Eq, PartialEq, Debug)]
9#[repr(C)]
10pub enum SequoiaErrorKind {
13 Unknown,
14 InvalidArgument,
15 IoError,
16}
17
18#[derive(Debug)]
19#[repr(C)]
20pub struct SequoiaError {
21 pub kind: SequoiaErrorKind,
22 pub message: *mut c_char,
23}
24
25impl Drop for SequoiaError {
26 fn drop(&mut self) {
27 unsafe {
28 let _ = CString::from_raw(self.message);
29 }
30 }
31}
32
33#[no_mangle]
34pub unsafe extern "C" fn sequoia_error_free(err_ptr: *mut SequoiaError) {
35 drop(Box::from_raw(err_ptr))
36}
37
38pub unsafe fn set_error_from(err_ptr: *mut *mut SequoiaError, err: anyhow::Error) {
39 if !err_ptr.is_null() {
40 let kind = if err.is::<io::Error>() {
41 SequoiaErrorKind::IoError
42 } else {
43 SequoiaErrorKind::Unknown
44 };
45
46 *err_ptr = Box::into_raw(Box::new(SequoiaError {
47 kind,
48 message: CString::from_vec_unchecked(err.to_string().into()).into_raw(),
49 }));
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn basic_usage() {
59 let error_text = "test error";
60 let result = Result::<(), anyhow::Error>::Err(anyhow::anyhow!(error_text));
61 let mut err_ptr: *mut SequoiaError = std::ptr::null_mut();
62 unsafe { set_error_from(&mut err_ptr, result.unwrap_err()) }
63 assert!(!err_ptr.is_null());
64 unsafe {
65 assert_eq!((*err_ptr).kind, SequoiaErrorKind::Unknown);
66 assert_eq!(
67 std::ffi::CStr::from_ptr((*err_ptr).message).to_str(),
68 Ok(error_text)
69 );
70 }
71 unsafe { sequoia_error_free(err_ptr) }
72 }
73
74 #[test]
75 fn nil_destination() {
76 let result = Result::<(), anyhow::Error>::Err(anyhow::anyhow!("test error"));
77 unsafe { set_error_from(std::ptr::null_mut(), result.unwrap_err()) }
78 }
79
80 #[test]
81 fn io_error() {
82 let error_text = "test error";
83 let result = Result::<(), anyhow::Error>::Err(
84 io::Error::new(io::ErrorKind::Other, error_text).into(),
85 );
86 let mut err_ptr: *mut SequoiaError = std::ptr::null_mut();
87 unsafe { set_error_from(&mut err_ptr, result.unwrap_err()) }
88 assert!(!err_ptr.is_null());
89 unsafe {
90 assert_eq!((*err_ptr).kind, SequoiaErrorKind::IoError);
91 assert_eq!(
92 std::ffi::CStr::from_ptr((*err_ptr).message).to_str(),
93 Ok(error_text)
94 );
95 }
96 unsafe { sequoia_error_free(err_ptr) }
97 }
98}