relay_cabi/
ffi.rs

1use relay_auth::{KeyParseError, UnpackError};
2use relay_event_normalization::GeoIpError;
3use relay_event_schema::processor::ProcessingAction;
4use relay_ffi::Panic;
5use sentry_release_parser::InvalidRelease;
6
7use crate::core::RelayStr;
8
9/// Represents all possible error codes.
10#[repr(u32)]
11#[allow(missing_docs)]
12pub enum RelayErrorCode {
13    NoError = 0,
14    Panic = 1,
15    Unknown = 2,
16
17    InvalidJsonError = 101, // serde_json::Error
18
19    // relay_auth::KeyParseError
20    KeyParseErrorBadEncoding = 1000,
21    KeyParseErrorBadKey = 1001,
22
23    // relay_auth::UnpackError
24    UnpackErrorBadSignature = 1003,
25    UnpackErrorBadPayload = 1004,
26    UnpackErrorSignatureExpired = 1005,
27    UnpackErrorBadEncoding = 1006,
28
29    // relay_protocol::annotated::ProcessingAction
30    ProcessingErrorInvalidTransaction = 2001,
31    ProcessingErrorInvalidGeoIp = 2002,
32
33    // sentry_release_parser::InvalidRelease
34    InvalidReleaseErrorTooLong = 3001,
35    InvalidReleaseErrorRestrictedName = 3002,
36    InvalidReleaseErrorBadCharacters = 3003,
37}
38
39impl RelayErrorCode {
40    /// This maps all errors that can possibly happen.
41    pub fn from_error(error: &anyhow::Error) -> RelayErrorCode {
42        for cause in error.chain() {
43            if cause.downcast_ref::<Panic>().is_some() {
44                return RelayErrorCode::Panic;
45            }
46            if cause.downcast_ref::<serde_json::Error>().is_some() {
47                return RelayErrorCode::InvalidJsonError;
48            }
49            if cause.downcast_ref::<GeoIpError>().is_some() {
50                return RelayErrorCode::ProcessingErrorInvalidGeoIp;
51            }
52            if let Some(err) = cause.downcast_ref::<KeyParseError>() {
53                return match err {
54                    KeyParseError::BadEncoding => RelayErrorCode::KeyParseErrorBadEncoding,
55                    KeyParseError::BadKey => RelayErrorCode::KeyParseErrorBadKey,
56                };
57            }
58            if let Some(err) = cause.downcast_ref::<UnpackError>() {
59                return match err {
60                    UnpackError::BadSignature => RelayErrorCode::UnpackErrorBadSignature,
61                    UnpackError::BadPayload(..) => RelayErrorCode::UnpackErrorBadPayload,
62                    UnpackError::SignatureExpired => RelayErrorCode::UnpackErrorSignatureExpired,
63                    UnpackError::BadEncoding => RelayErrorCode::UnpackErrorBadEncoding,
64                };
65            }
66            if let Some(err) = cause.downcast_ref::<ProcessingAction>() {
67                return match err {
68                    ProcessingAction::InvalidTransaction(_) => {
69                        RelayErrorCode::ProcessingErrorInvalidTransaction
70                    }
71                    _ => RelayErrorCode::Unknown,
72                };
73            }
74            if let Some(err) = cause.downcast_ref::<InvalidRelease>() {
75                return match err {
76                    InvalidRelease::TooLong => RelayErrorCode::InvalidReleaseErrorTooLong,
77                    InvalidRelease::RestrictedName => {
78                        RelayErrorCode::InvalidReleaseErrorRestrictedName
79                    }
80                    InvalidRelease::BadCharacters => {
81                        RelayErrorCode::InvalidReleaseErrorBadCharacters
82                    }
83                };
84            }
85        }
86        RelayErrorCode::Unknown
87    }
88}
89
90/// Initializes the library
91#[no_mangle]
92pub extern "C" fn relay_init() {
93    relay_ffi::set_panic_hook();
94}
95
96/// Returns the last error code.
97///
98/// If there is no error, 0 is returned.
99#[no_mangle]
100pub extern "C" fn relay_err_get_last_code() -> RelayErrorCode {
101    relay_ffi::with_last_error(RelayErrorCode::from_error).unwrap_or(RelayErrorCode::NoError)
102}
103
104/// Returns the last error message.
105///
106/// If there is no error an empty string is returned.  This allocates new memory
107/// that needs to be freed with `relay_str_free`.
108#[no_mangle]
109pub extern "C" fn relay_err_get_last_message() -> RelayStr {
110    use std::fmt::Write;
111    relay_ffi::with_last_error(|err| {
112        let mut msg = err.to_string();
113        for cause in err.chain().skip(1) {
114            write!(&mut msg, "\n  caused by: {cause}").ok();
115        }
116        RelayStr::from_string(msg)
117    })
118    .unwrap_or_default()
119}
120
121/// Returns the panic information as string.
122#[no_mangle]
123pub extern "C" fn relay_err_get_backtrace() -> RelayStr {
124    let backtrace = relay_ffi::with_last_error(|error| error.backtrace().to_string())
125        .filter(|bt| !bt.is_empty());
126
127    match backtrace {
128        Some(backtrace) => RelayStr::from_string(format!("stacktrace: {backtrace}")),
129        None => RelayStr::default(),
130    }
131}
132
133/// Clears the last error.
134#[no_mangle]
135pub extern "C" fn relay_err_clear() {
136    relay_ffi::take_last_error();
137}