Skip to main content

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    // relay_pattern::Error
39    PatternError = 4001,
40    // relay_pattern::OutOfGas,
41    PatternOutOfGas = 4002,
42}
43
44impl RelayErrorCode {
45    /// This maps all errors that can possibly happen.
46    pub fn from_error(error: &anyhow::Error) -> RelayErrorCode {
47        for cause in error.chain() {
48            if cause.downcast_ref::<Panic>().is_some() {
49                return RelayErrorCode::Panic;
50            }
51            if cause.downcast_ref::<serde_json::Error>().is_some() {
52                return RelayErrorCode::InvalidJsonError;
53            }
54            if cause.downcast_ref::<GeoIpError>().is_some() {
55                return RelayErrorCode::ProcessingErrorInvalidGeoIp;
56            }
57            if cause.downcast_ref::<relay_pattern::Error>().is_some() {
58                return RelayErrorCode::PatternError;
59            }
60            if cause.downcast_ref::<relay_pattern::OutOfGas>().is_some() {
61                return RelayErrorCode::PatternOutOfGas;
62            }
63            if let Some(err) = cause.downcast_ref::<KeyParseError>() {
64                return match err {
65                    KeyParseError::BadEncoding => RelayErrorCode::KeyParseErrorBadEncoding,
66                    KeyParseError::BadKey => RelayErrorCode::KeyParseErrorBadKey,
67                };
68            }
69            if let Some(err) = cause.downcast_ref::<UnpackError>() {
70                return match err {
71                    UnpackError::BadSignature => RelayErrorCode::UnpackErrorBadSignature,
72                    UnpackError::BadPayload(..) => RelayErrorCode::UnpackErrorBadPayload,
73                    UnpackError::Time(_) => RelayErrorCode::UnpackErrorSignatureExpired,
74                    UnpackError::BadEncoding => RelayErrorCode::UnpackErrorBadEncoding,
75                };
76            }
77            if let Some(err) = cause.downcast_ref::<ProcessingAction>() {
78                return match err {
79                    ProcessingAction::InvalidTransaction(_) => {
80                        RelayErrorCode::ProcessingErrorInvalidTransaction
81                    }
82                    _ => RelayErrorCode::Unknown,
83                };
84            }
85            if let Some(err) = cause.downcast_ref::<InvalidRelease>() {
86                return match err {
87                    InvalidRelease::TooLong => RelayErrorCode::InvalidReleaseErrorTooLong,
88                    InvalidRelease::RestrictedName => {
89                        RelayErrorCode::InvalidReleaseErrorRestrictedName
90                    }
91                    InvalidRelease::BadCharacters => {
92                        RelayErrorCode::InvalidReleaseErrorBadCharacters
93                    }
94                };
95            }
96        }
97        RelayErrorCode::Unknown
98    }
99}
100
101/// Initializes the library
102#[unsafe(no_mangle)]
103pub extern "C" fn relay_init() {
104    relay_ffi::set_panic_hook();
105}
106
107/// Returns the last error code.
108///
109/// If there is no error, 0 is returned.
110#[unsafe(no_mangle)]
111pub extern "C" fn relay_err_get_last_code() -> RelayErrorCode {
112    relay_ffi::with_last_error(RelayErrorCode::from_error).unwrap_or(RelayErrorCode::NoError)
113}
114
115/// Returns the last error message.
116///
117/// If there is no error an empty string is returned.  This allocates new memory
118/// that needs to be freed with `relay_str_free`.
119#[unsafe(no_mangle)]
120pub extern "C" fn relay_err_get_last_message() -> RelayStr {
121    use std::fmt::Write;
122    relay_ffi::with_last_error(|err| {
123        let mut msg = err.to_string();
124        for cause in err.chain().skip(1) {
125            write!(&mut msg, "\n  caused by: {cause}").ok();
126        }
127        RelayStr::from_string(msg)
128    })
129    .unwrap_or_default()
130}
131
132/// Returns the panic information as string.
133#[unsafe(no_mangle)]
134pub extern "C" fn relay_err_get_backtrace() -> RelayStr {
135    let backtrace = relay_ffi::with_last_error(|error| error.backtrace().to_string())
136        .filter(|bt| !bt.is_empty());
137
138    match backtrace {
139        Some(backtrace) => RelayStr::from_string(format!("stacktrace: {backtrace}")),
140        None => RelayStr::default(),
141    }
142}
143
144/// Clears the last error.
145#[unsafe(no_mangle)]
146pub extern "C" fn relay_err_clear() {
147    relay_ffi::take_last_error();
148}