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 = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_NOT_CANDIDATE,
75 BadFoundation = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_FOUNDATION,
77 BadComponentId = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_COMPONENT_ID,
79 BadTransportType = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_TRANSPORT_TYPE,
81 BadPriority = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_PRIORITY,
83 BadAddress = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_ADDRESS,
85 BadCandidateType = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_CANDIDATE_TYPE,
87 BadExtension = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_EXTENSION,
89 Malformed = crate::ffi::RICE_PARSE_CANDIDATE_ERROR_MALFORMED,
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(Clone)]
137pub struct Candidate {
138 ffi: crate::ffi::RiceCandidate,
139}
140
141unsafe impl Send for Candidate {}
142unsafe impl Sync for Candidate {}
143
144impl core::fmt::Debug for Candidate {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 let mut dbg = f.debug_struct("Candidate");
147 dbg.field("component_id", &self.component_id());
148 dbg.field("candidate_type", &self.candidate_type());
149 dbg.field("transport_type", &self.transport());
150 dbg.field("foundation", &self.foundation());
151 dbg.field("priority", &self.priority());
152 dbg.field("address", &self.address());
153 dbg.field("base_address", &self.base_address());
154 dbg.field("related_address", &self.related_address());
155 dbg.field("tcp_type", &self.tcp_type());
156 dbg.finish()
158 }
159}
160
161impl PartialEq<Candidate> for Candidate {
162 fn eq(&self, other: &Candidate) -> bool {
163 unsafe { crate::ffi::rice_candidate_eq(&self.ffi, &other.ffi) }
164 }
165}
166
167impl PartialEq<CandidateOwned> for Candidate {
168 fn eq(&self, other: &CandidateOwned) -> bool {
169 unsafe { crate::ffi::rice_candidate_eq(&self.ffi, other.ffi) }
170 }
171}
172
173impl Eq for Candidate {}
174
175impl CandidateApi for Candidate {}
176impl sealed::CandidateAsC for Candidate {
177 fn as_c(&self) -> *const crate::ffi::RiceCandidate {
178 &self.ffi
179 }
180}
181
182impl Drop for Candidate {
183 fn drop(&mut self) {
184 unsafe { crate::ffi::rice_candidate_clear(&mut self.ffi) }
185 }
186}
187
188impl Candidate {
189 pub fn builder(
209 component_id: usize,
210 ctype: CandidateType,
211 ttype: TransportType,
212 foundation: &str,
213 address: crate::Address,
214 ) -> CandidateBuilder {
215 unsafe {
216 let foundation = CString::new(foundation).unwrap();
217 let mut ret = CandidateBuilder {
218 ffi: crate::ffi::RiceCandidate::zeroed(),
219 };
220 let address = address.into_c_full();
221 let res = crate::ffi::rice_candidate_init(
222 &mut ret.ffi,
223 component_id,
224 ctype.into(),
225 ttype.into(),
226 foundation.as_ptr(),
227 address,
228 );
229 if res != crate::ffi::RICE_ERROR_SUCCESS {
230 let _address = crate::Address::from_c_full(address);
231 panic!("Failed to crate ICE candidate!");
232 }
233 ret
234 }
235 }
236
237 pub(crate) fn as_c(&self) -> *const crate::ffi::RiceCandidate {
238 &self.ffi
239 }
240
241 pub(crate) unsafe fn from_c_none(candidate: *const crate::ffi::RiceCandidate) -> Self {
242 unsafe {
243 let mut ret = Self {
244 ffi: crate::ffi::RiceCandidate::zeroed(),
245 };
246 crate::ffi::rice_candidate_copy_into(candidate, &mut ret.ffi);
247 ret
248 }
249 }
250
251 pub(crate) fn from_c_full(candidate: crate::ffi::RiceCandidate) -> Self {
252 Self { ffi: candidate }
253 }
254
255 pub fn to_owned(&self) -> CandidateOwned {
257 unsafe {
258 CandidateOwned {
259 ffi: crate::ffi::rice_candidate_copy(&self.ffi),
260 }
261 }
262 }
263
264 pub fn to_sdp_string(&self) -> String {
284 unsafe {
285 let res = crate::ffi::rice_candidate_to_sdp_string(&self.ffi);
286 let s = CStr::from_ptr(res);
287 let ret = s.to_str().unwrap().to_owned();
288 crate::ffi::rice_string_free(res);
289 ret
290 }
291 }
292
293 pub fn from_sdp_string(s: &str) -> Result<Candidate, ParseCandidateError> {
295 let cand_str = std::ffi::CString::new(s).unwrap();
296 unsafe {
297 let mut ret = Candidate {
298 ffi: crate::ffi::RiceCandidate::zeroed(),
299 };
300 let res =
301 crate::ffi::rice_candidate_init_from_sdp_string(&mut ret.ffi, cand_str.as_ptr());
302 ParseCandidateError::from_c(res)?;
303 Ok(ret)
304 }
305 }
306}
307
308#[derive(Eq)]
312pub struct CandidateOwned {
313 ffi: *mut crate::ffi::RiceCandidate,
314}
315
316unsafe impl Send for CandidateOwned {}
317unsafe impl Sync for CandidateOwned {}
318
319impl core::fmt::Debug for CandidateOwned {
320 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321 let mut dbg = f.debug_struct("CandidateOwned");
322 dbg.field("component_id", &self.component_id());
323 dbg.field("candidate_type", &self.candidate_type());
324 dbg.field("transport_type", &self.transport());
325 dbg.field("foundation", &self.foundation());
326 dbg.field("priority", &self.priority());
327 dbg.field("address", &self.address());
328 dbg.field("base_address", &self.base_address());
329 dbg.field("related_address", &self.related_address());
330 dbg.field("tcp_type", &self.tcp_type());
331 dbg.finish()
333 }
334}
335
336impl PartialEq<CandidateOwned> for CandidateOwned {
337 fn eq(&self, other: &CandidateOwned) -> bool {
338 unsafe { crate::ffi::rice_candidate_eq(self.ffi, other.ffi) }
339 }
340}
341
342impl PartialEq<Candidate> for CandidateOwned {
343 fn eq(&self, other: &Candidate) -> bool {
344 unsafe { crate::ffi::rice_candidate_eq(self.ffi, &other.ffi) }
345 }
346}
347
348impl Clone for CandidateOwned {
349 fn clone(&self) -> Self {
350 unsafe {
351 Self {
352 ffi: crate::ffi::rice_candidate_copy(self.ffi),
353 }
354 }
355 }
356}
357
358impl Drop for CandidateOwned {
359 fn drop(&mut self) {
360 unsafe { crate::ffi::rice_candidate_free(self.ffi) }
361 }
362}
363
364impl CandidateApi for CandidateOwned {}
365impl sealed::CandidateAsC for CandidateOwned {
366 fn as_c(&self) -> *const crate::ffi::RiceCandidate {
367 self.ffi
368 }
369}
370
371impl CandidateOwned {
372 pub(crate) fn as_c(&self) -> *const crate::ffi::RiceCandidate {
373 self.ffi
374 }
375
376 pub fn to_sdp_string(&self) -> String {
396 unsafe {
397 let res = crate::ffi::rice_candidate_to_sdp_string(self.ffi);
398 let s = CStr::from_ptr(res);
399 let ret = s.to_str().unwrap().to_owned();
400 crate::ffi::rice_string_free(res);
401 ret
402 }
403 }
404}
405
406#[derive(Debug)]
408pub struct CandidateBuilder {
409 ffi: crate::ffi::RiceCandidate,
410}
411
412impl CandidateBuilder {
413 pub fn build(self) -> Candidate {
415 Candidate { ffi: self.ffi }
416 }
417
418 pub fn priority(mut self, priority: u32) -> Self {
420 unsafe {
421 crate::ffi::rice_candidate_set_priority(&mut self.ffi, priority);
422 self
423 }
424 }
425
426 pub fn base_address(mut self, base: crate::Address) -> Self {
428 unsafe {
429 crate::ffi::rice_candidate_set_base_address(&mut self.ffi, base.into_c_full());
430 self
431 }
432 }
433
434 pub fn related_address(mut self, related: crate::Address) -> Self {
436 unsafe {
437 crate::ffi::rice_candidate_set_related_address(&mut self.ffi, related.into_c_full());
438 self
439 }
440 }
441
442 pub fn tcp_type(mut self, typ: TcpType) -> Self {
448 unsafe {
449 if self.ffi.transport_type != TransportType::Tcp.into() && typ != TcpType::None {
450 panic!("Attempt made to set the TcpType of a non-TCP candidate");
451 }
452 crate::ffi::rice_candidate_set_tcp_type(&mut self.ffi, typ.into());
453 self
454 }
455 }
456
457 }
459
460#[derive(Debug, Clone, Copy, PartialEq, Eq)]
462#[repr(u32)]
463pub enum CandidateType {
464 Host = crate::ffi::RICE_CANDIDATE_TYPE_HOST,
466 PeerReflexive = crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
468 ServerReflexive = crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
470 Relayed = crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
472}
473
474impl From<crate::ffi::RiceCandidateType> for CandidateType {
475 fn from(value: crate::ffi::RiceCandidateType) -> Self {
476 match value {
477 crate::ffi::RICE_CANDIDATE_TYPE_HOST => Self::Host,
478 crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE => Self::PeerReflexive,
479 crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE => Self::ServerReflexive,
480 crate::ffi::RICE_CANDIDATE_TYPE_RELAYED => Self::Relayed,
481 val => panic!("Unknown candidate type {val:x?}"),
482 }
483 }
484}
485
486impl From<CandidateType> for crate::ffi::RiceCandidateType {
487 fn from(value: CandidateType) -> Self {
488 match value {
489 CandidateType::Host => crate::ffi::RICE_CANDIDATE_TYPE_HOST,
490 CandidateType::PeerReflexive => crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
491 CandidateType::ServerReflexive => crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
492 CandidateType::Relayed => crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
493 }
494 }
495}
496
497#[derive(Debug, Copy, Clone, PartialEq, Eq)]
499#[repr(u32)]
500pub enum TcpType {
501 None = crate::ffi::RICE_TCP_TYPE_NONE,
503 Active = crate::ffi::RICE_TCP_TYPE_ACTIVE,
505 Passive = crate::ffi::RICE_TCP_TYPE_PASSIVE,
507 So = crate::ffi::RICE_TCP_TYPE_SO,
510}
511
512impl From<crate::ffi::RiceTcpType> for TcpType {
513 fn from(value: crate::ffi::RiceTcpType) -> Self {
514 match value {
515 crate::ffi::RICE_TCP_TYPE_NONE => Self::None,
516 crate::ffi::RICE_TCP_TYPE_ACTIVE => Self::Active,
517 crate::ffi::RICE_TCP_TYPE_PASSIVE => Self::Passive,
518 crate::ffi::RICE_TCP_TYPE_SO => Self::So,
519 val => panic!("Unknown tcp type value {val:x?}"),
520 }
521 }
522}
523
524impl From<TcpType> for crate::ffi::RiceTcpType {
525 fn from(value: TcpType) -> Self {
526 match value {
527 TcpType::None => crate::ffi::RICE_TCP_TYPE_NONE,
528 TcpType::Active => crate::ffi::RICE_TCP_TYPE_ACTIVE,
529 TcpType::Passive => crate::ffi::RICE_TCP_TYPE_PASSIVE,
530 TcpType::So => crate::ffi::RICE_TCP_TYPE_SO,
531 }
532 }
533}
534
535#[derive(Debug, Copy, Clone, PartialEq, Eq)]
537pub enum TransportType {
538 Udp,
540 Tcp,
542}
543
544impl core::fmt::Display for TransportType {
545 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546 write!(f, "{self:?}")
547 }
548}
549
550impl From<crate::ffi::RiceTransportType> for TransportType {
551 fn from(value: crate::ffi::RiceTransportType) -> Self {
552 match value {
553 crate::ffi::RICE_TRANSPORT_TYPE_UDP => Self::Udp,
554 crate::ffi::RICE_TRANSPORT_TYPE_TCP => Self::Tcp,
555 _ => panic!("Unknown transport type value"),
556 }
557 }
558}
559
560impl From<TransportType> for crate::ffi::RiceTransportType {
561 fn from(value: TransportType) -> Self {
562 match value {
563 TransportType::Udp => crate::ffi::RICE_TRANSPORT_TYPE_UDP,
564 TransportType::Tcp => crate::ffi::RICE_TRANSPORT_TYPE_TCP,
565 }
566 }
567}
568
569impl core::str::FromStr for TransportType {
570 type Err = ParseTransportTypeError;
571
572 fn from_str(s: &str) -> Result<Self, Self::Err> {
573 if s.eq_ignore_ascii_case("udp") {
574 Ok(Self::Udp)
575 } else if s.eq_ignore_ascii_case("tcp") {
576 Ok(Self::Tcp)
577 } else {
578 Err(ParseTransportTypeError::UnknownTransport)
579 }
580 }
581}
582
583#[derive(Copy, Clone, Debug, thiserror::Error, PartialEq, Eq)]
585pub enum ParseTransportTypeError {
586 #[error("Unknown transport value was provided")]
588 UnknownTransport,
589}
590
591#[derive(Debug, Clone, PartialEq, Eq)]
593pub struct CandidatePair {
594 pub local: CandidateOwned,
596 pub remote: CandidateOwned,
598}
599
600impl CandidatePair {
601 pub fn new(local: CandidateOwned, remote: CandidateOwned) -> Self {
603 Self { local, remote }
604 }
605}
606
607#[cfg(test)]
608mod tests {
609 use super::*;
610
611 #[test]
612 fn candidate_type() {
613 let _log = crate::tests::test_init_log();
614
615 for (c, r) in [
616 (crate::ffi::RICE_CANDIDATE_TYPE_HOST, CandidateType::Host),
617 (
618 crate::ffi::RICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
619 CandidateType::ServerReflexive,
620 ),
621 (
622 crate::ffi::RICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
623 CandidateType::PeerReflexive,
624 ),
625 (
626 crate::ffi::RICE_CANDIDATE_TYPE_RELAYED,
627 CandidateType::Relayed,
628 ),
629 ] {
630 assert_eq!(CandidateType::from(c), r);
631 assert_eq!(crate::ffi::RiceCandidateType::from(r), c);
632 }
633 }
634
635 #[test]
636 #[should_panic = "Unknown candidate type"]
637 fn candidate_type_out_of_range() {
638 let _log = crate::tests::test_init_log();
639 let _ = CandidateType::from(u32::MAX);
640 }
641
642 #[test]
643 fn tcp_type() {
644 let _log = crate::tests::test_init_log();
645
646 for (c, r) in [
647 (crate::ffi::RICE_TCP_TYPE_NONE, TcpType::None),
648 (crate::ffi::RICE_TCP_TYPE_ACTIVE, TcpType::Active),
649 (crate::ffi::RICE_TCP_TYPE_PASSIVE, TcpType::Passive),
650 (crate::ffi::RICE_TCP_TYPE_SO, TcpType::So),
651 ] {
652 assert_eq!(TcpType::from(c), r);
653 assert_eq!(crate::ffi::RiceTcpType::from(r), c);
654 }
655 }
656
657 #[test]
658 #[should_panic = "Unknown tcp type value"]
659 fn tcp_type_out_of_range() {
660 let _log = crate::tests::test_init_log();
661 let _ = TcpType::from(u32::MAX);
662 }
663
664 #[test]
665 fn transport_type() {
666 let _log = crate::tests::test_init_log();
667
668 for (c, r) in [
669 (crate::ffi::RICE_TRANSPORT_TYPE_UDP, TransportType::Udp),
670 (crate::ffi::RICE_TRANSPORT_TYPE_TCP, TransportType::Tcp),
671 ] {
672 assert_eq!(TransportType::from(c), r);
673 assert_eq!(crate::ffi::RiceTransportType::from(r), c);
674 }
675 }
676
677 #[test]
678 #[should_panic = "Unknown transport type value"]
679 fn transport_type_out_of_range() {
680 let _log = crate::tests::test_init_log();
681 let _ = TransportType::from(u32::MAX);
682 }
683
684 #[test]
685 fn parse_candidate_error() {
686 let _log = crate::tests::test_init_log();
687
688 for (c, r) in [
689 (crate::ffi::RICE_PARSE_CANDIDATE_ERROR_SUCCESS, Ok(())),
690 (
691 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_NOT_CANDIDATE,
692 Err(ParseCandidateError::NotCandidate),
693 ),
694 (
695 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_FOUNDATION,
696 Err(ParseCandidateError::BadFoundation),
697 ),
698 (
699 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_COMPONENT_ID,
700 Err(ParseCandidateError::BadComponentId),
701 ),
702 (
703 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_TRANSPORT_TYPE,
704 Err(ParseCandidateError::BadTransportType),
705 ),
706 (
707 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_PRIORITY,
708 Err(ParseCandidateError::BadPriority),
709 ),
710 (
711 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_ADDRESS,
712 Err(ParseCandidateError::BadAddress),
713 ),
714 (
715 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_CANDIDATE_TYPE,
716 Err(ParseCandidateError::BadCandidateType),
717 ),
718 (
719 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_BAD_EXTENSION,
720 Err(ParseCandidateError::BadExtension),
721 ),
722 (
723 crate::ffi::RICE_PARSE_CANDIDATE_ERROR_MALFORMED,
724 Err(ParseCandidateError::Malformed),
725 ),
726 ] {
727 assert_eq!(ParseCandidateError::from_c(c), r);
728 }
729 }
730
731 #[test]
732 #[should_panic = "Unknown RiceParseCandidateError value"]
733 fn parse_candidate_error_out_of_range() {
734 let _log = crate::tests::test_init_log();
735 let _ = ParseCandidateError::from_c(i32::MAX);
736 }
737
738 #[test]
739 fn transport_type_from_str() {
740 use core::str::FromStr;
741 let _log = crate::tests::test_init_log();
742
743 for (s, r) in [
744 ("udp", Ok(TransportType::Udp)),
745 ("UDP", Ok(TransportType::Udp)),
746 ("tcp", Ok(TransportType::Tcp)),
747 ("TCP", Ok(TransportType::Tcp)),
748 ("random", Err(ParseTransportTypeError::UnknownTransport)),
749 ] {
750 assert_eq!(TransportType::from_str(s), r);
751 }
752 }
753
754 fn base_address() -> crate::Address {
755 "127.0.0.1:1000".parse().unwrap()
756 }
757
758 fn address() -> crate::Address {
759 "127.0.0.2:2000".parse().unwrap()
760 }
761
762 fn related_address() -> crate::Address {
763 "127.0.0.3:3000".parse().unwrap()
764 }
765
766 #[test]
767 fn candidate_build() {
768 let _log = crate::tests::test_init_log();
769
770 let base = base_address();
771 let addr = address();
772 let related = related_address();
773 let cand = Candidate::builder(
774 1,
775 CandidateType::PeerReflexive,
776 TransportType::Tcp,
777 "foundation",
778 addr.clone(),
779 )
780 .base_address(base.clone())
781 .related_address(related.clone())
782 .tcp_type(TcpType::Active)
783 .priority(1234)
784 .build();
785 assert_eq!(cand.component_id(), 1);
786 assert_eq!(cand.candidate_type(), CandidateType::PeerReflexive);
787 assert_eq!(cand.transport(), TransportType::Tcp);
788 assert_eq!(cand.foundation(), "foundation");
789 assert_eq!(cand.address(), addr);
790 assert_eq!(cand.base_address(), base);
791 assert_eq!(cand.related_address(), Some(related));
792 assert_eq!(cand.tcp_type(), TcpType::Active);
793 assert_eq!(cand.priority(), 1234);
794
795 let cand_clone = cand.clone();
796 assert_eq!(cand, cand_clone);
797 }
798
799 #[test]
800 fn candidate_to_owned() {
801 let _log = crate::tests::test_init_log();
802
803 let base = base_address();
804 let addr = address();
805 let related = related_address();
806 let cand = Candidate::builder(
807 1,
808 CandidateType::PeerReflexive,
809 TransportType::Tcp,
810 "foundation",
811 addr.clone(),
812 )
813 .base_address(base.clone())
814 .related_address(related.clone())
815 .tcp_type(TcpType::Active)
816 .priority(1234)
817 .build();
818 let owned = cand.to_owned();
819 assert_eq!(cand, owned);
820 assert_eq!(owned, cand);
821 }
822
823 #[test]
824 fn candidate_string() {
825 let _log = crate::tests::test_init_log();
826
827 let addr = address();
828 let related = related_address();
829 let cand = Candidate::builder(
830 1,
831 CandidateType::PeerReflexive,
832 TransportType::Tcp,
833 "foundation",
834 addr.clone(),
835 )
836 .related_address(related.clone())
837 .tcp_type(TcpType::Active)
838 .priority(1234)
839 .build();
840
841 let s = cand.to_sdp_string();
842 println!("{s}");
843 let parsed = Candidate::from_sdp_string(&s).unwrap();
844 assert_eq!(parsed, cand);
845
846 let owned = cand.to_owned();
847 let s = cand.to_sdp_string();
848 println!("{s}");
849 let parsed = Candidate::from_sdp_string(&s).unwrap();
850 assert_eq!(parsed, owned);
851 }
852}