webrtc_sys/
rtc_error.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    error::Error,
17    fmt::{Display, Formatter},
18};
19
20// cxx doesn't support custom Exception type, so we serialize RtcError inside the cxx::Exception
21// "what" string
22
23#[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        // cxx doesn't support the Option trait
61        pub has_sctp_cause_code: bool,
62        pub sctp_cause_code: u16,
63    }
64}
65
66impl ffi::RtcError {
67    /// # Safety
68    /// The value must be correctly encoded
69    pub unsafe fn from(value: &str) -> Self {
70        // Parse the hex encoded error from c++
71        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..]); // msg isn't encoded
76
77        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}