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