1use std::ffi::CString;
2use std::os::raw::{c_char, c_int};
3use std::ptr;
4
5static UNKNOWN_ERROR: &str = "Unable to create error string\0";
6
7use std::cell::RefCell;
8thread_local! {
9 pub static LAST_ERROR: RefCell<Option<(i32, CString)>> = RefCell::new(None);
10}
11
12#[derive(thiserror::Error, Debug)]
13pub enum Error {
14 #[error(transparent)]
15 VC(#[from] ssi::vc::Error),
16 #[error(transparent)]
17 Zcap(#[from] ssi::zcap::Error),
18 #[error(transparent)]
19 JWK(#[from] ssi::jwk::Error),
20 #[error(transparent)]
21 Null(#[from] std::ffi::NulError),
22 #[error(transparent)]
23 Utf8(#[from] std::str::Utf8Error),
24 #[error(transparent)]
25 Borrow(#[from] std::cell::BorrowError),
26 #[error(transparent)]
27 IO(#[from] std::io::Error),
28 #[error("Unable to generate DID")]
29 UnableToGenerateDID,
30 #[error("Unknown DID method")]
31 UnknownDIDMethod,
32 #[error("Unable to get verification method")]
33 UnableToGetVerificationMethod,
34 #[error("Unknown proof format: {0}")]
35 UnknownProofFormat(String),
36
37 #[doc(hidden)]
38 #[error("")]
39 __Nonexhaustive,
40}
41
42impl Error {
43 pub fn stash(self) {
44 LAST_ERROR.with(|stash| {
45 stash.replace(Some((
46 self.get_code(),
47 CString::new(self.to_string()).unwrap(),
48 )))
49 });
50 }
51
52 fn get_code(&self) -> c_int {
53 match self {
55 Error::VC(_) => 1,
56 Error::Null(_) => 2,
57 Error::Utf8(_) => 3,
58 Error::JWK(_) => 4,
59 Error::Zcap(_) => 5,
60 _ => -1,
61 }
62 }
63}
64
65#[no_mangle]
66pub extern "C" fn didkit_error_message() -> *const c_char {
70 LAST_ERROR.with(|error| match error.try_borrow() {
71 Ok(maybe_err_ref) => match &*maybe_err_ref {
72 Some(err) => err.1.as_ptr() as *const c_char,
73 None => ptr::null(),
74 },
75 Err(_) => UNKNOWN_ERROR.as_ptr() as *const c_char,
76 })
77}
78
79#[no_mangle]
80pub extern "C" fn didkit_error_code() -> c_int {
83 LAST_ERROR.with(|error| match error.try_borrow() {
84 Ok(maybe_err_ref) => match &*maybe_err_ref {
85 Some(err) => err.0,
86 None => 0,
87 },
88 Err(err) => Error::from(err).get_code(),
89 })
90}
91
92impl From<serde_json::Error> for Error {
93 fn from(err: serde_json::Error) -> Error {
94 Error::VC(ssi::vc::Error::from(err))
95 }
96}
97
98impl From<ssi::ldp::Error> for Error {
99 fn from(e: ssi::ldp::Error) -> Error {
100 ssi::vc::Error::from(e).into()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn errors() {
110 use crate::c::didkit_vc_issue_presentation;
111 use std::ffi::CStr;
112 let presentation = "{}\0".as_ptr() as *const c_char;
113 let options = "{}\0".as_ptr() as *const c_char;
114 let key = "{}\0".as_ptr() as *const c_char;
115 let vp = didkit_vc_issue_presentation(presentation, options, key);
116 assert_eq!(vp, ptr::null());
117 let msg = unsafe { CStr::from_ptr(didkit_error_message()) }
118 .to_str()
119 .unwrap();
120 let code = didkit_error_code();
121 assert_ne!(code, 0);
122 println!("code: {:?} msg: {:?}", code, msg);
123 }
124}