1use std::ffi::{CStr, CString};
12
13pub trait CandidateApi: sealed::CandidateAsC {
15 fn component_id(&self) -> usize {
17 unsafe { (*self.as_c()).component_id }
18 }
19 fn candidate_type(&self) -> CandidateType {
21 unsafe { (*self.as_c()).candidate_type.into() }
22 }
23 fn transport(&self) -> TransportType {
25 unsafe { (*self.as_c()).transport_type.into() }
26 }
27
28 fn foundation(&self) -> String {
30 unsafe {
31 CStr::from_ptr((*self.as_c()).foundation)
32 .to_str()
33 .unwrap()
34 .to_owned()
35 }
36 }
37 fn priority(&self) -> u32 {
39 unsafe { (*self.as_c()).priority }
40 }
41 fn address(&self) -> crate::Address {
43 unsafe { crate::Address::from_c_none((*self.as_c()).address) }
44 }
45 fn base_address(&self) -> crate::Address {
47 unsafe { crate::Address::from_c_none((*self.as_c()).base_address) }
48 }
49 fn related_address(&self) -> Option<crate::Address> {
51 unsafe {
52 let related = (*self.as_c()).related_address;
53 if related.is_null() {
54 None
55 } else {
56 Some(crate::Address::from_c_none(related))
57 }
58 }
59 }
60 fn tcp_type(&self) -> TcpType {
62 unsafe { (*self.as_c()).tcp_type.into() }
63 }
64 }
66
67#[derive(Debug, Copy, Clone, PartialEq, Eq)]
69#[repr(i32)]
70pub enum ParseCandidateError {
71 NotCandidate = -1,
73 BadFoundation = -2,
75 BadComponentId = -3,
77 BadTransportType = -4,
79 BadPriority = -5,
81 BadAddress = -6,
83 BadCandidateType = -7,
85 BadExtension = -8,
87 Malformed = -9,
89}
90
91impl ParseCandidateError {
92 pub(crate) fn from_c(
93 value: crate::ffi::RiceParseCandidateError,
94 ) -> Result<(), ParseCandidateError> {
95 match value {
96 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_SUCCESS => Ok(()),
97 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_NOT_CANDIDATE => {
98 Err(ParseCandidateError::NotCandidate)
99 }
100 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_FOUNDATION => {
101 Err(ParseCandidateError::BadFoundation)
102 }
103 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_COMPONENT_ID => {
104 Err(ParseCandidateError::BadComponentId)
105 }
106 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_TRANSPORT_TYPE => {
107 Err(ParseCandidateError::BadTransportType)
108 }
109 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_PRIORITY => {
110 Err(ParseCandidateError::BadPriority)
111 }
112 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_ADDRESS => {
113 Err(ParseCandidateError::BadAddress)
114 }
115 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_CANDIDATE_TYPE => {
116 Err(ParseCandidateError::BadCandidateType)
117 }
118 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_EXTENSION => {
119 Err(ParseCandidateError::BadExtension)
120 }
121 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_MALFORMED => Err(ParseCandidateError::Malformed),
122 val => panic!("Unknown RiceParseCandidateError value {val:x?}"),
123 }
124 }
125}
126
127mod sealed {
128 pub trait CandidateAsC {
129 fn as_c(&self) -> *const crate::ffi::RiceCandidate;
130 }
131}
132
133#[derive(Debug)]
135pub struct Candidate {
136 ffi: crate::ffi::RiceCandidate,
137}
138
139unsafe impl Send for Candidate {}
140unsafe impl Sync for Candidate {}
141
142impl PartialEq<Candidate> for Candidate {
143 fn eq(&self, other: &Candidate) -> bool {
144 unsafe { crate::ffi::rice_candidate_eq(&self.ffi, &other.ffi) }
145 }
146}
147
148impl PartialEq<CandidateOwned> for Candidate {
149 fn eq(&self, other: &CandidateOwned) -> bool {
150 unsafe { crate::ffi::rice_candidate_eq(&self.ffi, other.ffi) }
151 }
152}
153
154impl Eq for Candidate {}
155
156impl Clone for Candidate {
157 fn clone(&self) -> Self {
158 unsafe {
159 let mut ret = Self {
160 ffi: crate::ffi::RiceCandidate::zeroed(),
161 };
162 crate::ffi::rice_candidate_copy_into(&self.ffi, &mut ret.ffi);
163 ret
164 }
165 }
166}
167
168impl CandidateApi for Candidate {}
169impl sealed::CandidateAsC for Candidate {
170 fn as_c(&self) -> *const crate::ffi::RiceCandidate {
171 &self.ffi
172 }
173}
174
175impl Drop for Candidate {
176 fn drop(&mut self) {
177 unsafe { crate::ffi::rice_candidate_clear(&mut self.ffi) }
178 }
179}
180
181impl Candidate {
182 pub fn builder(
202 component_id: usize,
203 ctype: CandidateType,
204 ttype: TransportType,
205 foundation: &str,
206 address: crate::Address,
207 ) -> CandidateBuilder {
208 unsafe {
209 let foundation = CString::new(foundation).unwrap();
210 let mut ret = CandidateBuilder {
211 ffi: crate::ffi::RiceCandidate::zeroed(),
212 };
213 let address = address.into_c_full();
214 let res = crate::ffi::rice_candidate_init(
215 &mut ret.ffi,
216 component_id,
217 ctype.into(),
218 ttype.into(),
219 foundation.as_ptr(),
220 address,
221 );
222 if res != crate::ffi::RICE_ERROR_SUCCESS {
223 let _address = crate::Address::from_c_full(address);
224 panic!("Failed to crate ICE candidate!");
225 }
226 ret
227 }
228 }
229
230 pub(crate) fn as_c(&self) -> *const crate::ffi::RiceCandidate {
231 &self.ffi
232 }
233
234 pub(crate) unsafe fn from_c_none(candidate: *const crate::ffi::RiceCandidate) -> Self {
235 unsafe {
236 let mut ret = Self {
237 ffi: crate::ffi::RiceCandidate::zeroed(),
238 };
239 crate::ffi::rice_candidate_copy_into(candidate, &mut ret.ffi);
240 ret
241 }
242 }
243
244 pub(crate) fn from_c_full(candidate: crate::ffi::RiceCandidate) -> Self {
245 Self { ffi: candidate }
246 }
247
248 pub fn to_owned(&self) -> CandidateOwned {
250 unsafe {
251 CandidateOwned {
252 ffi: crate::ffi::rice_candidate_copy(&self.ffi),
253 }
254 }
255 }
256
257 pub fn to_sdp_string(&self) -> String {
277 unsafe {
278 let res = crate::ffi::rice_candidate_to_sdp_string(&self.ffi);
279 let s = CStr::from_ptr(res);
280 let ret = s.to_str().unwrap().to_owned();
281 crate::ffi::rice_string_free(res);
282 ret
283 }
284 }
285
286 pub fn from_sdp_string(s: &str) -> Result<Candidate, ParseCandidateError> {
288 let cand_str = std::ffi::CString::new(s).unwrap();
289 unsafe {
290 let mut ret = Candidate {
291 ffi: crate::ffi::RiceCandidate::zeroed(),
292 };
293 let res =
294 crate::ffi::rice_candidate_init_from_sdp_string(&mut ret.ffi, cand_str.as_ptr());
295 ParseCandidateError::from_c(res)?;
296 Ok(ret)
297 }
298 }
299}
300
301#[derive(Eq)]
305pub struct CandidateOwned {
306 ffi: *mut crate::ffi::RiceCandidate,
307}
308
309unsafe impl Send for CandidateOwned {}
310unsafe impl Sync for CandidateOwned {}
311
312impl core::fmt::Debug for CandidateOwned {
313 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 unsafe {
315 let mut dbg = f.debug_struct("Candidate");
316 let dbg2 = &mut dbg;
317 dbg2.field("ffi", &self.ffi);
318 if !self.ffi.is_null() {
319 dbg2.field("value", &*self.ffi);
320 }
321 dbg.finish()
322 }
323 }
324}
325
326impl PartialEq<CandidateOwned> for CandidateOwned {
327 fn eq(&self, other: &CandidateOwned) -> bool {
328 unsafe { crate::ffi::rice_candidate_eq(self.ffi, other.ffi) }
329 }
330}
331
332impl PartialEq<Candidate> for CandidateOwned {
333 fn eq(&self, other: &Candidate) -> bool {
334 unsafe { crate::ffi::rice_candidate_eq(self.ffi, &other.ffi) }
335 }
336}
337
338impl Clone for CandidateOwned {
339 fn clone(&self) -> Self {
340 unsafe {
341 Self {
342 ffi: crate::ffi::rice_candidate_copy(self.ffi),
343 }
344 }
345 }
346}
347
348impl Drop for CandidateOwned {
349 fn drop(&mut self) {
350 unsafe { crate::ffi::rice_candidate_free(self.ffi) }
351 }
352}
353
354impl CandidateApi for CandidateOwned {}
355impl sealed::CandidateAsC for CandidateOwned {
356 fn as_c(&self) -> *const crate::ffi::RiceCandidate {
357 self.ffi
358 }
359}
360
361impl CandidateOwned {
362 pub(crate) fn as_c(&self) -> *const crate::ffi::RiceCandidate {
363 self.ffi
364 }
365
366 pub fn to_sdp_string(&self) -> String {
386 unsafe {
387 let res = crate::ffi::rice_candidate_to_sdp_string(self.ffi);
388 let s = CStr::from_ptr(res);
389 let ret = s.to_str().unwrap().to_owned();
390 crate::ffi::rice_string_free(res);
391 ret
392 }
393 }
394}
395
396#[derive(Debug)]
398pub struct CandidateBuilder {
399 ffi: crate::ffi::RiceCandidate,
400}
401
402impl CandidateBuilder {
403 pub fn build(self) -> Candidate {
405 Candidate { ffi: self.ffi }
406 }
407
408 pub fn priority(mut self, priority: u32) -> Self {
410 unsafe {
411 crate::ffi::rice_candidate_set_priority(&mut self.ffi, priority);
412 self
413 }
414 }
415
416 pub fn base_address(mut self, base: crate::Address) -> Self {
418 unsafe {
419 crate::ffi::rice_candidate_set_base_address(&mut self.ffi, base.into_c_full());
420 self
421 }
422 }
423
424 pub fn related_address(mut self, related: crate::Address) -> Self {
426 unsafe {
427 crate::ffi::rice_candidate_set_related_address(&mut self.ffi, related.into_c_full());
428 self
429 }
430 }
431
432 pub fn tcp_type(mut self, typ: TcpType) -> Self {
438 unsafe {
439 if self.ffi.transport_type != TransportType::Tcp.into() && typ != TcpType::None {
440 panic!("Attempt made to set the TcpType of a non-TCP candidate");
441 }
442 crate::ffi::rice_candidate_set_tcp_type(&mut self.ffi, typ.into());
443 self
444 }
445 }
446
447 }
449
450#[derive(Debug, Clone, Copy, PartialEq, Eq)]
452#[repr(u32)]
453pub enum CandidateType {
454 Host = crate::ffi::RICE_CANDIDATE_TYPE_HOST,
456 PeerReflexive = crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
458 ServerReflexive = crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
460 Relayed = crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
462}
463
464impl From<crate::ffi::RiceCandidateType> for CandidateType {
465 fn from(value: crate::ffi::RiceCandidateType) -> Self {
466 match value {
467 crate::ffi::RICE_CANDIDATE_TYPE_HOST => Self::Host,
468 crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE => Self::PeerReflexive,
469 crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE => Self::ServerReflexive,
470 crate::ffi::RICE_CANDIDATE_TYPE_RELAYED => Self::Relayed,
471 val => panic!("Unknown candidate type {val:x?}"),
472 }
473 }
474}
475
476impl From<CandidateType> for crate::ffi::RiceCandidateType {
477 fn from(value: CandidateType) -> Self {
478 match value {
479 CandidateType::Host => crate::ffi::RICE_CANDIDATE_TYPE_HOST,
480 CandidateType::PeerReflexive => crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
481 CandidateType::ServerReflexive => crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
482 CandidateType::Relayed => crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
483 }
484 }
485}
486
487#[derive(Debug, Copy, Clone, PartialEq, Eq)]
489#[repr(u32)]
490pub enum TcpType {
491 None = crate::ffi::RICE_TCP_TYPE_NONE,
493 Active = crate::ffi::RICE_TCP_TYPE_ACTIVE,
495 Passive = crate::ffi::RICE_TCP_TYPE_PASSIVE,
497 So = crate::ffi::RICE_TCP_TYPE_SO,
500}
501
502impl From<crate::ffi::RiceTcpType> for TcpType {
503 fn from(value: crate::ffi::RiceTcpType) -> Self {
504 match value {
505 crate::ffi::RICE_TCP_TYPE_NONE => Self::None,
506 crate::ffi::RICE_TCP_TYPE_ACTIVE => Self::Active,
507 crate::ffi::RICE_TCP_TYPE_PASSIVE => Self::Passive,
508 crate::ffi::RICE_TCP_TYPE_SO => Self::So,
509 val => panic!("Unknown RiceTcpType valyue {val:x?}"),
510 }
511 }
512}
513
514impl From<TcpType> for crate::ffi::RiceTcpType {
515 fn from(value: TcpType) -> Self {
516 match value {
517 TcpType::None => crate::ffi::RICE_TCP_TYPE_NONE,
518 TcpType::Active => crate::ffi::RICE_TCP_TYPE_ACTIVE,
519 TcpType::Passive => crate::ffi::RICE_TCP_TYPE_PASSIVE,
520 TcpType::So => crate::ffi::RICE_TCP_TYPE_SO,
521 }
522 }
523}
524
525#[derive(Debug, Copy, Clone, PartialEq, Eq)]
527pub enum TransportType {
528 Udp,
530 Tcp,
532}
533
534impl core::fmt::Display for TransportType {
535 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
536 write!(f, "{self:?}")
537 }
538}
539
540impl From<crate::ffi::RiceTransportType> for TransportType {
541 fn from(value: crate::ffi::RiceTransportType) -> Self {
542 match value {
543 crate::ffi::RICE_TRANSPORT_TYPE_UDP => Self::Udp,
544 crate::ffi::RICE_TRANSPORT_TYPE_TCP => Self::Tcp,
545 _ => panic!("Unknown RiceTransportType value"),
546 }
547 }
548}
549
550impl From<TransportType> for crate::ffi::RiceTransportType {
551 fn from(value: TransportType) -> Self {
552 match value {
553 TransportType::Udp => crate::ffi::RICE_TRANSPORT_TYPE_UDP,
554 TransportType::Tcp => crate::ffi::RICE_TRANSPORT_TYPE_TCP,
555 }
556 }
557}
558
559impl core::str::FromStr for TransportType {
560 type Err = ParseTransportTypeError;
561
562 fn from_str(s: &str) -> Result<Self, Self::Err> {
563 if s.eq_ignore_ascii_case("udp") {
564 Ok(Self::Udp)
565 } else if s.eq_ignore_ascii_case("tcp") {
566 Ok(Self::Tcp)
567 } else {
568 Err(ParseTransportTypeError::UnknownTransport)
569 }
570 }
571}
572
573#[derive(Debug, thiserror::Error)]
575pub enum ParseTransportTypeError {
576 #[error("Unknown transport value was provided")]
578 UnknownTransport,
579}
580
581#[derive(Debug, Clone, PartialEq, Eq)]
583pub struct CandidatePair {
584 pub local: CandidateOwned,
586 pub remote: CandidateOwned,
588}
589
590impl CandidatePair {
591 pub fn new(local: CandidateOwned, remote: CandidateOwned) -> Self {
593 Self { local, remote }
594 }
595}
596
597#[cfg(test)]
598mod tests {
599 use super::*;
600
601 fn base_address() -> crate::Address {
602 "127.0.0.1:1000".parse().unwrap()
603 }
604
605 fn address() -> crate::Address {
606 "127.0.0.2:2000".parse().unwrap()
607 }
608
609 fn related_address() -> crate::Address {
610 "127.0.0.3:3000".parse().unwrap()
611 }
612
613 #[test]
614 fn candidate_build() {
615 let _log = crate::tests::test_init_log();
616
617 let base = base_address();
618 let addr = address();
619 let related = related_address();
620 let cand = Candidate::builder(
621 1,
622 CandidateType::PeerReflexive,
623 TransportType::Tcp,
624 "foundation",
625 addr.clone(),
626 )
627 .base_address(base.clone())
628 .related_address(related.clone())
629 .tcp_type(TcpType::Active)
630 .build();
631 assert_eq!(cand.component_id(), 1);
632 assert_eq!(cand.candidate_type(), CandidateType::PeerReflexive);
633 assert_eq!(cand.transport(), TransportType::Tcp);
634 assert_eq!(cand.foundation(), "foundation");
635 assert_eq!(cand.address(), addr);
636 assert_eq!(cand.base_address(), base);
637 assert_eq!(cand.related_address(), Some(related));
638 assert_eq!(cand.tcp_type(), TcpType::Active);
639
640 let cand_clone = cand.clone();
641 assert_eq!(cand, cand_clone);
642 }
643}