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#[repr(u32)]
11#[allow(missing_docs)]
12pub enum RelayErrorCode {
13 NoError = 0,
14 Panic = 1,
15 Unknown = 2,
16
17 InvalidJsonError = 101, KeyParseErrorBadEncoding = 1000,
21 KeyParseErrorBadKey = 1001,
22
23 UnpackErrorBadSignature = 1003,
25 UnpackErrorBadPayload = 1004,
26 UnpackErrorSignatureExpired = 1005,
27 UnpackErrorBadEncoding = 1006,
28
29 ProcessingErrorInvalidTransaction = 2001,
31 ProcessingErrorInvalidGeoIp = 2002,
32
33 InvalidReleaseErrorTooLong = 3001,
35 InvalidReleaseErrorRestrictedName = 3002,
36 InvalidReleaseErrorBadCharacters = 3003,
37
38 PatternError = 4001,
40 PatternOutOfGas = 4002,
42}
43
44impl RelayErrorCode {
45 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#[unsafe(no_mangle)]
103pub extern "C" fn relay_init() {
104 relay_ffi::set_panic_hook();
105}
106
107#[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#[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#[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#[unsafe(no_mangle)]
146pub extern "C" fn relay_err_clear() {
147 relay_ffi::take_last_error();
148}