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
39impl RelayErrorCode {
40 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#[no_mangle]
92pub extern "C" fn relay_init() {
93 relay_ffi::set_panic_hook();
94}
95
96#[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#[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#[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#[no_mangle]
135pub extern "C" fn relay_err_clear() {
136 relay_ffi::take_last_error();
137}