1use std::ffi::{CStr, CString};
12
13use crate::agent::AgentError;
14
15pub trait CandidateApi: sealed::CandidateAsC {
16 fn component_id(&self) -> usize {
18 unsafe { (*self.as_c()).component_id }
19 }
20 fn candidate_type(&self) -> CandidateType {
22 unsafe { (*self.as_c()).candidate_type.into() }
23 }
24 fn transport(&self) -> TransportType {
26 unsafe { (*self.as_c()).transport_type.into() }
27 }
28
29 fn foundation(&self) -> String {
31 unsafe {
32 CStr::from_ptr((*self.as_c()).foundation)
33 .to_str()
34 .unwrap()
35 .to_owned()
36 }
37 }
38 fn priority(&self) -> u32 {
40 unsafe { (*self.as_c()).priority }
41 }
42 fn address(&self) -> crate::Address {
44 unsafe { crate::Address::from_c_none((*self.as_c()).address) }
45 }
46 fn base_address(&self) -> crate::Address {
48 unsafe { crate::Address::from_c_none((*self.as_c()).base_address) }
49 }
50 fn related_address(&self) -> Option<crate::Address> {
52 unsafe {
53 let related = (*self.as_c()).related_address;
54 if related.is_null() {
55 None
56 } else {
57 Some(crate::Address::from_c_none(related))
58 }
59 }
60 }
61 fn tcp_type(&self) -> TcpType {
63 unsafe { (*self.as_c()).tcp_type.into() }
64 }
65 }
67
68mod sealed {
69 pub trait CandidateAsC {
70 fn as_c(&self) -> *const crate::ffi::RiceCandidate;
71 }
72}
73
74#[derive(Debug)]
76pub struct Candidate {
77 ffi: crate::ffi::RiceCandidate,
78}
79
80unsafe impl Send for Candidate {}
81unsafe impl Sync for Candidate {}
82
83impl PartialEq<Candidate> for Candidate {
84 fn eq(&self, other: &Candidate) -> bool {
85 unsafe { crate::ffi::rice_candidate_eq(&self.ffi, &other.ffi) }
86 }
87}
88
89impl PartialEq<CandidateOwned> for Candidate {
90 fn eq(&self, other: &CandidateOwned) -> bool {
91 unsafe { crate::ffi::rice_candidate_eq(&self.ffi, other.ffi) }
92 }
93}
94
95impl Clone for Candidate {
96 fn clone(&self) -> Self {
97 unsafe {
98 let mut ret = Self {
99 ffi: crate::ffi::RiceCandidate::zeroed(),
100 };
101 crate::ffi::rice_candidate_copy_into(&self.ffi, &mut ret.ffi);
102 ret
103 }
104 }
105}
106
107impl CandidateApi for Candidate {}
108impl sealed::CandidateAsC for Candidate {
109 fn as_c(&self) -> *const crate::ffi::RiceCandidate {
110 &self.ffi
111 }
112}
113
114impl Drop for Candidate {
115 fn drop(&mut self) {
116 unsafe { crate::ffi::rice_candidate_clear(&mut self.ffi) }
117 }
118}
119
120impl Candidate {
121 pub fn builder(
141 component_id: usize,
142 ctype: CandidateType,
143 ttype: TransportType,
144 foundation: &str,
145 address: crate::Address,
146 ) -> CandidateBuilder {
147 unsafe {
148 let foundation = CString::new(foundation).unwrap();
149 let mut ret = CandidateBuilder {
150 ffi: crate::ffi::RiceCandidate::zeroed(),
151 };
152 let address = address.into_c_full();
153 let res = crate::ffi::rice_candidate_init(
154 &mut ret.ffi,
155 component_id,
156 ctype.into(),
157 ttype.into(),
158 foundation.as_ptr(),
159 address,
160 );
161 if res != crate::ffi::RICE_ERROR_SUCCESS {
162 let _address = crate::Address::from_c_full(address);
163 panic!("Failed to crate ICE candidate!");
164 }
165 ret
166 }
167 }
168
169 pub(crate) fn as_c(&self) -> *const crate::ffi::RiceCandidate {
170 &self.ffi
171 }
172
173 pub(crate) unsafe fn from_c_none(candidate: *const crate::ffi::RiceCandidate) -> Self {
174 let mut ret = Self {
175 ffi: crate::ffi::RiceCandidate::zeroed(),
176 };
177 crate::ffi::rice_candidate_copy_into(candidate, &mut ret.ffi);
178 ret
179 }
180
181 pub(crate) fn from_c_full(candidate: crate::ffi::RiceCandidate) -> Self {
182 Self { ffi: candidate }
183 }
184
185 pub fn to_owned(&self) -> CandidateOwned {
186 unsafe {
187 CandidateOwned {
188 ffi: crate::ffi::rice_candidate_copy(&self.ffi),
189 }
190 }
191 }
192
193 pub fn to_sdp_string(&self) -> String {
213 unsafe {
214 let res = crate::ffi::rice_candidate_to_sdp_string(&self.ffi);
215 let s = CStr::from_ptr(res);
216 let ret = s.to_str().unwrap().to_owned();
217 crate::ffi::rice_string_free(res);
218 ret
219 }
220 }
221
222 pub fn from_sdp_string(s: &str) -> Result<Candidate, AgentError> {
225 let cand_str = std::ffi::CString::new(s).unwrap();
226 unsafe {
227 let mut ret = Candidate {
228 ffi: crate::ffi::RiceCandidate::zeroed(),
229 };
230 let res =
231 crate::ffi::rice_candidate_init_from_sdp_string(&mut ret.ffi, cand_str.as_ptr());
232 AgentError::from_c(res)?;
233 Ok(ret)
234 }
235 }
236}
237
238#[derive(Eq)]
239pub struct CandidateOwned {
240 ffi: *mut crate::ffi::RiceCandidate,
241}
242
243unsafe impl Send for CandidateOwned {}
244unsafe impl Sync for CandidateOwned {}
245
246impl core::fmt::Debug for CandidateOwned {
247 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 unsafe {
249 let mut dbg = f.debug_struct("Candidate");
250 let dbg2 = &mut dbg;
251 dbg2.field("ffi", &self.ffi);
252 if !self.ffi.is_null() {
253 dbg2.field("value", &*self.ffi);
254 }
255 dbg.finish()
256 }
257 }
258}
259
260impl PartialEq<CandidateOwned> for CandidateOwned {
261 fn eq(&self, other: &CandidateOwned) -> bool {
262 unsafe { crate::ffi::rice_candidate_eq(self.ffi, other.ffi) }
263 }
264}
265
266impl PartialEq<Candidate> for CandidateOwned {
267 fn eq(&self, other: &Candidate) -> bool {
268 unsafe { crate::ffi::rice_candidate_eq(self.ffi, &other.ffi) }
269 }
270}
271
272impl Clone for CandidateOwned {
273 fn clone(&self) -> Self {
274 unsafe {
275 Self {
276 ffi: crate::ffi::rice_candidate_copy(self.ffi),
277 }
278 }
279 }
280}
281
282impl Drop for CandidateOwned {
283 fn drop(&mut self) {
284 unsafe { crate::ffi::rice_candidate_free(self.ffi) }
285 }
286}
287
288impl CandidateApi for CandidateOwned {}
289impl sealed::CandidateAsC for CandidateOwned {
290 fn as_c(&self) -> *const crate::ffi::RiceCandidate {
291 self.ffi
292 }
293}
294
295impl CandidateOwned {
296 pub(crate) fn as_c(&self) -> *const crate::ffi::RiceCandidate {
297 self.ffi
298 }
299
300 pub fn to_sdp_string(&self) -> String {
320 unsafe {
321 let res = crate::ffi::rice_candidate_to_sdp_string(self.ffi);
322 let s = CStr::from_ptr(res);
323 let ret = s.to_str().unwrap().to_owned();
324 crate::ffi::rice_string_free(res);
325 ret
326 }
327 }
328}
329
330#[derive(Debug)]
332pub struct CandidateBuilder {
333 ffi: crate::ffi::RiceCandidate,
334}
335
336impl CandidateBuilder {
337 pub fn build(self) -> Candidate {
338 Candidate { ffi: self.ffi }
339 }
340
341 pub fn priority(mut self, priority: u32) -> Self {
343 unsafe {
344 crate::ffi::rice_candidate_set_priority(&mut self.ffi, priority);
345 self
346 }
347 }
348
349 pub fn base_address(mut self, base: crate::Address) -> Self {
351 unsafe {
352 crate::ffi::rice_candidate_set_base_address(&mut self.ffi, base.into_c_full());
353 self
354 }
355 }
356
357 pub fn related_address(mut self, related: crate::Address) -> Self {
359 unsafe {
360 crate::ffi::rice_candidate_set_related_address(&mut self.ffi, related.into_c_full());
361 self
362 }
363 }
364
365 pub fn tcp_type(mut self, typ: TcpType) -> Self {
371 unsafe {
372 if self.ffi.transport_type != TransportType::Tcp.into() && typ != TcpType::None {
373 panic!("Attempt made to set the TcpType of a non-TCP candidate");
374 }
375 crate::ffi::rice_candidate_set_tcp_type(&mut self.ffi, typ.into());
376 self
377 }
378 }
379
380 }
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
385#[repr(u32)]
386pub enum CandidateType {
387 Host = crate::ffi::RICE_CANDIDATE_TYPE_HOST,
389 PeerReflexive = crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
391 ServerReflexive = crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
393 Relayed = crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
395}
396
397impl From<crate::ffi::RiceCandidateType> for CandidateType {
398 fn from(value: crate::ffi::RiceCandidateType) -> Self {
399 match value {
400 crate::ffi::RICE_CANDIDATE_TYPE_HOST => Self::Host,
401 crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE => Self::PeerReflexive,
402 crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE => Self::ServerReflexive,
403 crate::ffi::RICE_CANDIDATE_TYPE_RELAYED => Self::Relayed,
404 val => panic!("Unknown candidate type {val:x?}"),
405 }
406 }
407}
408
409impl From<CandidateType> for crate::ffi::RiceCandidateType {
410 fn from(value: CandidateType) -> Self {
411 match value {
412 CandidateType::Host => crate::ffi::RICE_CANDIDATE_TYPE_HOST,
413 CandidateType::PeerReflexive => crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
414 CandidateType::ServerReflexive => crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
415 CandidateType::Relayed => crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
416 }
417 }
418}
419
420#[derive(Debug, Copy, Clone, PartialEq, Eq)]
422#[repr(u32)]
423pub enum TcpType {
424 None = crate::ffi::RICE_TCP_TYPE_NONE,
426 Active = crate::ffi::RICE_TCP_TYPE_ACTIVE,
428 Passive = crate::ffi::RICE_TCP_TYPE_PASSIVE,
430 So = crate::ffi::RICE_TCP_TYPE_SO,
433}
434
435impl From<crate::ffi::RiceTcpType> for TcpType {
436 fn from(value: crate::ffi::RiceTcpType) -> Self {
437 match value {
438 crate::ffi::RICE_TCP_TYPE_NONE => Self::None,
439 crate::ffi::RICE_TCP_TYPE_ACTIVE => Self::Active,
440 crate::ffi::RICE_TCP_TYPE_PASSIVE => Self::Passive,
441 crate::ffi::RICE_TCP_TYPE_SO => Self::So,
442 val => panic!("Unknown RiceTcpType valyue {val:x?}"),
443 }
444 }
445}
446
447impl From<TcpType> for crate::ffi::RiceTcpType {
448 fn from(value: TcpType) -> Self {
449 match value {
450 TcpType::None => crate::ffi::RICE_TCP_TYPE_NONE,
451 TcpType::Active => crate::ffi::RICE_TCP_TYPE_ACTIVE,
452 TcpType::Passive => crate::ffi::RICE_TCP_TYPE_PASSIVE,
453 TcpType::So => crate::ffi::RICE_TCP_TYPE_SO,
454 }
455 }
456}
457
458#[derive(Debug, Copy, Clone, PartialEq, Eq)]
460pub enum TransportType {
461 Udp,
463 Tcp,
465}
466
467impl core::fmt::Display for TransportType {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 write!(f, "{self:?}")
470 }
471}
472
473impl From<crate::ffi::RiceTransportType> for TransportType {
474 fn from(value: crate::ffi::RiceTransportType) -> Self {
475 match value {
476 crate::ffi::RICE_TRANSPORT_TYPE_UDP => Self::Udp,
477 crate::ffi::RICE_TRANSPORT_TYPE_TCP => Self::Tcp,
478 _ => panic!("Unknown RiceTransportType value"),
479 }
480 }
481}
482
483impl From<TransportType> for crate::ffi::RiceTransportType {
484 fn from(value: TransportType) -> Self {
485 match value {
486 TransportType::Udp => crate::ffi::RICE_TRANSPORT_TYPE_UDP,
487 TransportType::Tcp => crate::ffi::RICE_TRANSPORT_TYPE_TCP,
488 }
489 }
490}
491
492#[derive(Debug, Clone, PartialEq, Eq)]
494pub struct CandidatePair {
495 pub local: CandidateOwned,
497 pub remote: CandidateOwned,
499}
500
501impl CandidatePair {
502 pub fn new(local: CandidateOwned, remote: CandidateOwned) -> Self {
504 Self { local, remote }
505 }
506}
507
508#[cfg(test)]
509mod tests {
510 use super::*;
511
512 fn base_address() -> crate::Address {
513 "127.0.0.1:1000".parse().unwrap()
514 }
515
516 fn address() -> crate::Address {
517 "127.0.0.2:2000".parse().unwrap()
518 }
519
520 fn related_address() -> crate::Address {
521 "127.0.0.3:3000".parse().unwrap()
522 }
523
524 #[test]
525 fn candidate_build() {
526 let _log = crate::tests::test_init_log();
527
528 let base = base_address();
529 let addr = address();
530 let related = related_address();
531 let cand = Candidate::builder(
532 1,
533 CandidateType::PeerReflexive,
534 TransportType::Tcp,
535 "foundation",
536 addr.clone(),
537 )
538 .base_address(base.clone())
539 .related_address(related.clone())
540 .tcp_type(TcpType::Active)
541 .build();
542 assert_eq!(cand.component_id(), 1);
543 assert_eq!(cand.candidate_type(), CandidateType::PeerReflexive);
544 assert_eq!(cand.transport(), TransportType::Tcp);
545 assert_eq!(cand.foundation(), "foundation");
546 assert_eq!(cand.address(), addr);
547 assert_eq!(cand.base_address(), base);
548 assert_eq!(cand.related_address(), Some(related));
549 assert_eq!(cand.tcp_type(), TcpType::Active);
550
551 let cand_clone = cand.clone();
552 assert_eq!(cand, cand_clone);
553 }
554}