1use std::{
16 error::Error,
17 fmt::{Display, Formatter},
18};
19
20#[cxx::bridge(namespace = "livekit_ffi")]
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 fn parse(value: &str) -> Option<Self> {
86 if value.len() < 22 {
87 return None;
88 }
89 let error_type = u32::from_str_radix(&value[0..8], 16).ok()?;
90 let error_detail = u32::from_str_radix(&value[8..16], 16).ok()?;
91 let has_scp_cause_code = u8::from_str_radix(&value[16..18], 16).ok()?;
92 let sctp_cause_code = u16::from_str_radix(&value[18..22], 16).ok()?;
93 let message = String::from(&value[22..]);
94
95 Some(Self {
96 error_type: rtc_error_type_from_u32(error_type),
97 error_detail: rtc_error_detail_type_from_u32(error_detail),
98 sctp_cause_code,
99 has_sctp_cause_code: has_scp_cause_code == 1,
100 message,
101 })
102 }
103
104 pub unsafe fn from(value: &str) -> Self {
111 Self::parse(value).expect("malformed serialized RtcError")
112 }
113
114 pub fn ok(&self) -> bool {
115 self.error_type == ffi::RtcErrorType::None
116 }
117}
118
119fn rtc_error_type_from_u32(value: u32) -> ffi::RtcErrorType {
120 match value {
121 0 => ffi::RtcErrorType::None,
122 1 => ffi::RtcErrorType::UnsupportedOperation,
123 2 => ffi::RtcErrorType::UnsupportedParameter,
124 3 => ffi::RtcErrorType::InvalidParameter,
125 4 => ffi::RtcErrorType::InvalidRange,
126 5 => ffi::RtcErrorType::SyntaxError,
127 6 => ffi::RtcErrorType::InvalidState,
128 7 => ffi::RtcErrorType::InvalidModification,
129 8 => ffi::RtcErrorType::NetworkError,
130 9 => ffi::RtcErrorType::ResourceExhausted,
131 10 => ffi::RtcErrorType::InternalError,
132 11 => ffi::RtcErrorType::OperationErrorWithData,
133 _ => ffi::RtcErrorType::None,
134 }
135}
136
137fn rtc_error_detail_type_from_u32(value: u32) -> ffi::RtcErrorDetailType {
138 match value {
139 0 => ffi::RtcErrorDetailType::None,
140 1 => ffi::RtcErrorDetailType::DataChannelFailure,
141 2 => ffi::RtcErrorDetailType::DtlsFailure,
142 3 => ffi::RtcErrorDetailType::FingerprintFailure,
143 4 => ffi::RtcErrorDetailType::SctpFailure,
144 5 => ffi::RtcErrorDetailType::SdpSyntaxError,
145 6 => ffi::RtcErrorDetailType::HardwareEncoderNotAvailable,
146 7 => ffi::RtcErrorDetailType::HardwareEncoderError,
147 _ => ffi::RtcErrorDetailType::None,
148 }
149}
150
151impl Error for ffi::RtcError {}
152
153impl Display for ffi::RtcError {
154 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
155 write!(f, "RtcError occurred {:?}: {}", self.error_type, self.message)
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use crate::rtc_error::ffi::{RtcError, RtcErrorDetailType, RtcErrorType};
162
163 #[cxx::bridge(namespace = "livekit_ffi")]
164 pub mod ffi {
165 unsafe extern "C++" {
166 include!("livekit/rtc_error.h");
167
168 fn serialize_deserialize() -> String;
169 }
170 }
171
172 #[test]
175 fn serialize_deserialize() {
176 let str = ffi::serialize_deserialize();
177 let error = unsafe { RtcError::from(&str) };
178
179 assert_eq!(error.error_type, RtcErrorType::InternalError);
180 assert_eq!(error.error_detail, RtcErrorDetailType::DataChannelFailure);
181 assert!(error.has_sctp_cause_code);
182 assert_eq!(error.sctp_cause_code, 24);
183 assert_eq!(error.message, "this is not a test, I repeat, this is not a test");
184 }
185}