1use std::{
16 error::Error,
17 fmt::{Display, Formatter},
18};
19
20#[cxx::bridge(namespace = "livekit")]
24pub mod ffi {
25 #[derive(Debug)]
26 #[repr(i32)]
27 pub enum RtcErrorType {
28 None,
29 UnsupportedOperation,
30 UnsupportedParameter,
31 InvalidParameter,
32 InvalidRange,
33 SyntaxError,
34 InvalidState,
35 InvalidModification,
36 NetworkError,
37 ResourceExhausted,
38 InternalError,
39 OperationErrorWithData,
40 }
41
42 #[derive(Debug)]
43 #[repr(i32)]
44 pub enum RtcErrorDetailType {
45 None,
46 DataChannelFailure,
47 DtlsFailure,
48 FingerprintFailure,
49 SctpFailure,
50 SdpSyntaxError,
51 HardwareEncoderNotAvailable,
52 HardwareEncoderError,
53 }
54
55 #[derive(Debug)]
56 pub struct RtcError {
57 pub error_type: RtcErrorType,
58 pub message: String,
59 pub error_detail: RtcErrorDetailType,
60 pub has_sctp_cause_code: bool,
62 pub sctp_cause_code: u16,
63 }
64}
65
66impl ffi::RtcError {
67 pub unsafe fn from(value: &str) -> Self {
70 let error_type = u32::from_str_radix(&value[0..8], 16).unwrap();
72 let error_detail = u32::from_str_radix(&value[8..16], 16).unwrap();
73 let has_scp_cause_code = u8::from_str_radix(&value[16..18], 16).unwrap();
74 let sctp_cause_code = u16::from_str_radix(&value[18..22], 16).unwrap();
75 let message = String::from(&value[22..]); Self {
78 error_type: std::mem::transmute(error_type),
79 error_detail: std::mem::transmute(error_detail),
80 sctp_cause_code,
81 has_sctp_cause_code: has_scp_cause_code == 1,
82 message,
83 }
84 }
85
86 pub fn ok(&self) -> bool {
87 self.error_type == ffi::RtcErrorType::None
88 }
89}
90
91impl Error for ffi::RtcError {}
92
93impl Display for ffi::RtcError {
94 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
95 write!(f, "RtcError occurred {:?}: {}", self.error_type, self.message)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::rtc_error::ffi::{RtcError, RtcErrorDetailType, RtcErrorType};
102
103 #[cxx::bridge(namespace = "livekit")]
104 pub mod ffi {
105 unsafe extern "C++" {
106 include!("livekit/rtc_error.h");
107
108 fn serialize_deserialize() -> String;
109 fn throw_error() -> Result<()>;
110 }
111 }
112
113 #[test]
114 fn serialize_deserialize() {
115 let str = ffi::serialize_deserialize();
116 let error = unsafe { RtcError::from(&str) };
117
118 assert_eq!(error.error_type, RtcErrorType::InternalError);
119 assert_eq!(error.error_detail, RtcErrorDetailType::DataChannelFailure);
120 assert!(error.has_sctp_cause_code);
121 assert_eq!(error.sctp_cause_code, 24);
122 assert_eq!(error.message, "this is not a test, I repeat, this is not a test");
123 }
124
125 #[test]
126 fn throw_error() {
127 let exc: cxx::Exception = ffi::throw_error().err().unwrap();
128 let error = unsafe { RtcError::from(exc.what()) };
129
130 assert_eq!(error.error_type, RtcErrorType::InvalidModification);
131 assert_eq!(error.error_detail, RtcErrorDetailType::None);
132 assert!(!error.has_sctp_cause_code);
133 assert_eq!(error.sctp_cause_code, 0);
134 assert_eq!(error.message, "exception is thrown!");
135 }
136}