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