1mod audio;
9mod error;
10mod group_call;
11mod loopback;
12mod sofia;
13mod user;
14
15pub use audio::AudioEndpoint;
16pub use error::ErrorEndpoint;
17pub use group_call::{GroupCall, GroupCallOrder, ParseGroupCallOrderError};
18pub use loopback::LoopbackEndpoint;
19pub use sofia::{SofiaContact, SofiaEndpoint, SofiaGateway};
20pub use user::UserEndpoint;
21
22use std::fmt;
23use std::str::FromStr;
24
25use super::find_matching_bracket;
26use super::originate::{OriginateError, Variables};
27
28pub trait DialString: fmt::Display {
33 fn variables(&self) -> Option<&Variables>;
35 fn variables_mut(&mut self) -> Option<&mut Variables>;
37 fn set_variables(&mut self, vars: Option<Variables>);
39}
40
41fn write_variables(f: &mut fmt::Formatter<'_>, vars: &Option<Variables>) -> fmt::Result {
46 if let Some(vars) = vars {
47 if !vars.is_empty() {
48 write!(f, "{}", vars)?;
49 }
50 }
51 Ok(())
52}
53
54fn extract_variables(s: &str) -> Result<(Option<Variables>, &str), OriginateError> {
60 let (open, close_ch) = match s
61 .as_bytes()
62 .first()
63 {
64 Some(b'{') => ('{', '}'),
65 Some(b'[') => ('[', ']'),
66 Some(b'<') => ('<', '>'),
67 _ => return Ok((None, s)),
68 };
69 let close = find_matching_bracket(s, open, close_ch)
70 .ok_or_else(|| OriginateError::ParseError(format!("unclosed {} in endpoint", open)))?;
71 let var_str = &s[..=close];
72 let vars: Variables = var_str.parse()?;
73 let vars = if vars.is_empty() { None } else { Some(vars) };
74 Ok((vars, s[close + 1..].trim()))
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
87#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
88#[non_exhaustive]
89pub enum Endpoint {
90 Sofia(SofiaEndpoint),
92 SofiaGateway(SofiaGateway),
94 Loopback(LoopbackEndpoint),
96 User(UserEndpoint),
98 SofiaContact(SofiaContact),
100 GroupCall(GroupCall),
102 Error(ErrorEndpoint),
104 #[cfg_attr(feature = "serde", serde(rename = "portaudio"))]
106 PortAudio(AudioEndpoint),
107 #[cfg_attr(feature = "serde", serde(rename = "pulseaudio"))]
109 PulseAudio(AudioEndpoint),
110 Alsa(AudioEndpoint),
112}
113
114impl From<SofiaEndpoint> for Endpoint {
119 fn from(ep: SofiaEndpoint) -> Self {
120 Self::Sofia(ep)
121 }
122}
123
124impl From<SofiaGateway> for Endpoint {
125 fn from(ep: SofiaGateway) -> Self {
126 Self::SofiaGateway(ep)
127 }
128}
129
130impl From<LoopbackEndpoint> for Endpoint {
131 fn from(ep: LoopbackEndpoint) -> Self {
132 Self::Loopback(ep)
133 }
134}
135
136impl From<UserEndpoint> for Endpoint {
137 fn from(ep: UserEndpoint) -> Self {
138 Self::User(ep)
139 }
140}
141
142impl From<SofiaContact> for Endpoint {
143 fn from(ep: SofiaContact) -> Self {
144 Self::SofiaContact(ep)
145 }
146}
147
148impl From<GroupCall> for Endpoint {
149 fn from(ep: GroupCall) -> Self {
150 Self::GroupCall(ep)
151 }
152}
153
154impl From<ErrorEndpoint> for Endpoint {
155 fn from(ep: ErrorEndpoint) -> Self {
156 Self::Error(ep)
157 }
158}
159
160impl fmt::Display for Endpoint {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 Self::Sofia(ep) => ep.fmt(f),
168 Self::SofiaGateway(ep) => ep.fmt(f),
169 Self::Loopback(ep) => ep.fmt(f),
170 Self::User(ep) => ep.fmt(f),
171 Self::SofiaContact(ep) => ep.fmt(f),
172 Self::GroupCall(ep) => ep.fmt(f),
173 Self::Error(ep) => ep.fmt(f),
174 Self::PortAudio(ep) => ep.fmt_with_prefix(f, "portaudio"),
175 Self::PulseAudio(ep) => ep.fmt_with_prefix(f, "pulseaudio"),
176 Self::Alsa(ep) => ep.fmt_with_prefix(f, "alsa"),
177 }
178 }
179}
180
181impl FromStr for Endpoint {
186 type Err = OriginateError;
187
188 fn from_str(s: &str) -> Result<Self, Self::Err> {
189 let (variables, uri) = extract_variables(s)?;
190 let full = if variables.is_some() {
192 s.to_string()
193 } else {
194 uri.to_string()
195 };
196
197 if uri.starts_with("${sofia_contact(") {
198 Ok(Self::SofiaContact(full.parse()?))
199 } else if uri.starts_with("${group_call(") {
200 Ok(Self::GroupCall(full.parse()?))
201 } else if uri.starts_with("error/") {
202 Ok(Self::Error(full.parse()?))
203 } else if uri.starts_with("loopback/") {
204 Ok(Self::Loopback(full.parse()?))
205 } else if uri.starts_with("sofia/gateway/") {
206 Ok(Self::SofiaGateway(full.parse()?))
207 } else if uri.starts_with("sofia/") {
208 Ok(Self::Sofia(full.parse()?))
209 } else if uri.starts_with("user/") {
210 Ok(Self::User(full.parse()?))
211 } else if uri.starts_with("portaudio") {
212 Ok(Self::PortAudio(AudioEndpoint::parse_with_prefix(
213 &full,
214 "portaudio",
215 )?))
216 } else if uri.starts_with("pulseaudio") {
217 Ok(Self::PulseAudio(AudioEndpoint::parse_with_prefix(
218 &full,
219 "pulseaudio",
220 )?))
221 } else if uri.starts_with("alsa") {
222 Ok(Self::Alsa(AudioEndpoint::parse_with_prefix(&full, "alsa")?))
223 } else {
224 Err(OriginateError::ParseError(format!(
225 "unknown endpoint type: {}",
226 uri
227 )))
228 }
229 }
230}
231
232macro_rules! impl_dial_string_with_variables {
237 ($ty:ty) => {
238 impl DialString for $ty {
239 fn variables(&self) -> Option<&Variables> {
240 self.variables
241 .as_ref()
242 }
243 fn variables_mut(&mut self) -> Option<&mut Variables> {
244 self.variables
245 .as_mut()
246 }
247 fn set_variables(&mut self, vars: Option<Variables>) {
248 self.variables = vars;
249 }
250 }
251 };
252}
253
254impl_dial_string_with_variables!(SofiaEndpoint);
255impl_dial_string_with_variables!(SofiaGateway);
256impl_dial_string_with_variables!(LoopbackEndpoint);
257impl_dial_string_with_variables!(UserEndpoint);
258impl_dial_string_with_variables!(SofiaContact);
259impl_dial_string_with_variables!(GroupCall);
260impl_dial_string_with_variables!(AudioEndpoint);
261
262impl DialString for ErrorEndpoint {
263 fn variables(&self) -> Option<&Variables> {
264 None
265 }
266 fn variables_mut(&mut self) -> Option<&mut Variables> {
267 None
268 }
269 fn set_variables(&mut self, _vars: Option<Variables>) {}
270}
271
272impl DialString for Endpoint {
273 fn variables(&self) -> Option<&Variables> {
274 match self {
275 Self::Sofia(ep) => ep.variables(),
276 Self::SofiaGateway(ep) => ep.variables(),
277 Self::Loopback(ep) => ep.variables(),
278 Self::User(ep) => ep.variables(),
279 Self::SofiaContact(ep) => ep.variables(),
280 Self::GroupCall(ep) => ep.variables(),
281 Self::Error(ep) => ep.variables(),
282 Self::PortAudio(ep) | Self::PulseAudio(ep) | Self::Alsa(ep) => ep.variables(),
283 }
284 }
285 fn variables_mut(&mut self) -> Option<&mut Variables> {
286 match self {
287 Self::Sofia(ep) => ep.variables_mut(),
288 Self::SofiaGateway(ep) => ep.variables_mut(),
289 Self::Loopback(ep) => ep.variables_mut(),
290 Self::User(ep) => ep.variables_mut(),
291 Self::SofiaContact(ep) => ep.variables_mut(),
292 Self::GroupCall(ep) => ep.variables_mut(),
293 Self::Error(ep) => ep.variables_mut(),
294 Self::PortAudio(ep) | Self::PulseAudio(ep) | Self::Alsa(ep) => ep.variables_mut(),
295 }
296 }
297 fn set_variables(&mut self, vars: Option<Variables>) {
298 match self {
299 Self::Sofia(ep) => ep.set_variables(vars),
300 Self::SofiaGateway(ep) => ep.set_variables(vars),
301 Self::Loopback(ep) => ep.set_variables(vars),
302 Self::User(ep) => ep.set_variables(vars),
303 Self::SofiaContact(ep) => ep.set_variables(vars),
304 Self::GroupCall(ep) => ep.set_variables(vars),
305 Self::Error(ep) => ep.set_variables(vars),
306 Self::PortAudio(ep) | Self::PulseAudio(ep) | Self::Alsa(ep) => ep.set_variables(vars),
307 }
308 }
309}
310
311#[cfg(test)]
316mod tests {
317 use super::*;
318 use crate::commands::originate::VariablesType;
319
320 #[test]
323 fn extract_variables_nested_angle_brackets() {
324 let (vars, rest) = extract_variables("<sip_h_Call-Info=<url>>sofia/gw/x").unwrap();
325 assert_eq!(rest, "sofia/gw/x");
326 assert!(vars.is_some());
327 }
328
329 #[test]
330 fn extract_variables_nested_curly_brackets() {
331 let (vars, rest) = extract_variables("{a={b}}sofia/internal/1000").unwrap();
332 assert_eq!(rest, "sofia/internal/1000");
333 assert!(vars.is_some());
334 }
335
336 #[test]
337 fn extract_variables_unclosed_returns_error() {
338 let result = extract_variables("{a=b");
339 assert!(result.is_err());
340 }
341
342 #[test]
345 fn endpoint_from_str_sofia() {
346 let ep: Endpoint = "sofia/internal/1000@domain.com"
347 .parse()
348 .unwrap();
349 assert!(matches!(ep, Endpoint::Sofia(_)));
350 }
351
352 #[test]
353 fn endpoint_from_str_sofia_gateway() {
354 let ep: Endpoint = "sofia/gateway/my_gw/1234"
355 .parse()
356 .unwrap();
357 assert!(matches!(ep, Endpoint::SofiaGateway(_)));
358 }
359
360 #[test]
361 fn endpoint_from_str_loopback() {
362 let ep: Endpoint = "loopback/9199/test"
363 .parse()
364 .unwrap();
365 assert!(matches!(ep, Endpoint::Loopback(_)));
366 }
367
368 #[test]
369 fn endpoint_from_str_user() {
370 let ep: Endpoint = "user/1000@domain.com"
371 .parse()
372 .unwrap();
373 assert!(matches!(ep, Endpoint::User(_)));
374 }
375
376 #[test]
377 fn endpoint_from_str_sofia_contact() {
378 let ep: Endpoint = "${sofia_contact(1000@domain.com)}"
379 .parse()
380 .unwrap();
381 assert!(matches!(ep, Endpoint::SofiaContact(_)));
382 }
383
384 #[test]
385 fn endpoint_from_str_group_call() {
386 let ep: Endpoint = "${group_call(support@domain.com+A)}"
387 .parse()
388 .unwrap();
389 assert!(matches!(ep, Endpoint::GroupCall(_)));
390 }
391
392 #[test]
393 fn endpoint_from_str_error() {
394 let ep: Endpoint = "error/USER_BUSY"
395 .parse()
396 .unwrap();
397 assert!(matches!(ep, Endpoint::Error(_)));
398 assert!("error/user_busy"
399 .parse::<Endpoint>()
400 .is_err());
401 }
402
403 #[test]
404 fn endpoint_from_str_unknown_errors() {
405 let result = "verto/1234".parse::<Endpoint>();
406 assert!(result.is_err());
407 }
408
409 #[test]
410 fn endpoint_from_str_with_variables() {
411 let ep: Endpoint = "{timeout=30}sofia/internal/1000@domain.com"
412 .parse()
413 .unwrap();
414 if let Endpoint::Sofia(inner) = &ep {
415 assert_eq!(inner.profile, "internal");
416 assert!(inner
417 .variables
418 .is_some());
419 } else {
420 panic!("expected Sofia variant");
421 }
422 }
423
424 #[test]
427 fn endpoint_display_delegates_to_inner() {
428 let ep = Endpoint::Sofia(SofiaEndpoint {
429 profile: "internal".into(),
430 destination: "1000@domain.com".into(),
431 variables: None,
432 });
433 assert_eq!(ep.to_string(), "sofia/internal/1000@domain.com");
434 }
435
436 #[test]
439 fn dial_string_variables_returns_some() {
440 let mut vars = Variables::new(VariablesType::Default);
441 vars.insert("k", "v");
442 let ep = SofiaEndpoint {
443 profile: "internal".into(),
444 destination: "1000".into(),
445 variables: Some(vars),
446 };
447 assert!(ep
448 .variables()
449 .is_some());
450 assert_eq!(
451 ep.variables()
452 .unwrap()
453 .get("k"),
454 Some("v")
455 );
456 }
457
458 #[test]
459 fn dial_string_variables_returns_none() {
460 let ep = SofiaEndpoint {
461 profile: "internal".into(),
462 destination: "1000".into(),
463 variables: None,
464 };
465 assert!(ep
466 .variables()
467 .is_none());
468 }
469
470 #[test]
471 fn dial_string_set_variables() {
472 let mut ep = SofiaEndpoint {
473 profile: "internal".into(),
474 destination: "1000".into(),
475 variables: None,
476 };
477 let mut vars = Variables::new(VariablesType::Channel);
478 vars.insert("k", "v");
479 ep.set_variables(Some(vars));
480 assert!(ep
481 .variables()
482 .is_some());
483 }
484
485 #[test]
486 fn dial_string_error_endpoint_no_variables() {
487 let ep = ErrorEndpoint::new(crate::channel::HangupCause::UserBusy);
488 assert!(ep
489 .variables()
490 .is_none());
491 }
492
493 #[test]
494 fn dial_string_on_endpoint_enum() {
495 let mut vars = Variables::new(VariablesType::Default);
496 vars.insert("k", "v");
497 let ep = Endpoint::Sofia(SofiaEndpoint {
498 profile: "internal".into(),
499 destination: "1000".into(),
500 variables: Some(vars),
501 });
502 assert!(ep
503 .variables()
504 .is_some());
505 }
506
507 #[test]
510 fn serde_endpoint_enum_sofia() {
511 let ep = Endpoint::Sofia(SofiaEndpoint {
512 profile: "internal".into(),
513 destination: "1000@domain.com".into(),
514 variables: None,
515 });
516 let json = serde_json::to_string(&ep).unwrap();
517 assert!(json.contains("\"sofia\""));
518 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
519 assert_eq!(parsed, ep);
520 }
521
522 #[test]
523 fn serde_endpoint_enum_sofia_gateway() {
524 let ep = Endpoint::SofiaGateway(SofiaGateway {
525 gateway: "gw1".into(),
526 destination: "1234".into(),
527 profile: None,
528 variables: None,
529 });
530 let json = serde_json::to_string(&ep).unwrap();
531 assert!(json.contains("\"sofia_gateway\""));
532 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
533 assert_eq!(parsed, ep);
534 }
535
536 #[test]
537 fn serde_endpoint_enum_loopback() {
538 let ep = Endpoint::Loopback(LoopbackEndpoint::new("9199").with_context("default"));
539 let json = serde_json::to_string(&ep).unwrap();
540 assert!(json.contains("\"loopback\""));
541 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
542 assert_eq!(parsed, ep);
543 }
544
545 #[test]
546 fn serde_endpoint_enum_user() {
547 let ep = Endpoint::User(UserEndpoint {
548 name: "bob".into(),
549 domain: Some("example.com".into()),
550 variables: None,
551 });
552 let json = serde_json::to_string(&ep).unwrap();
553 assert!(json.contains("\"user\""));
554 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
555 assert_eq!(parsed, ep);
556 }
557
558 #[test]
559 fn serde_endpoint_enum_sofia_contact() {
560 let ep = Endpoint::SofiaContact(SofiaContact {
561 user: "1000".into(),
562 domain: "domain.com".into(),
563 profile: None,
564 variables: None,
565 });
566 let json = serde_json::to_string(&ep).unwrap();
567 assert!(json.contains("\"sofia_contact\""));
568 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
569 assert_eq!(parsed, ep);
570 }
571
572 #[test]
573 fn serde_endpoint_enum_group_call() {
574 let ep = Endpoint::GroupCall(GroupCall {
575 group: "support".into(),
576 domain: "domain.com".into(),
577 order: Some(GroupCallOrder::All),
578 variables: None,
579 });
580 let json = serde_json::to_string(&ep).unwrap();
581 assert!(json.contains("\"group_call\""));
582 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
583 assert_eq!(parsed, ep);
584 }
585
586 #[test]
587 fn serde_endpoint_enum_error() {
588 let ep = Endpoint::Error(ErrorEndpoint::new(crate::channel::HangupCause::UserBusy));
589 let json = serde_json::to_string(&ep).unwrap();
590 assert!(json.contains("\"error\""));
591 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
592 assert_eq!(parsed, ep);
593 }
594
595 #[test]
596 fn serde_endpoint_skips_none_variables() {
597 let ep = SofiaEndpoint {
598 profile: "internal".into(),
599 destination: "1000".into(),
600 variables: None,
601 };
602 let json = serde_json::to_string(&ep).unwrap();
603 assert!(!json.contains("variables"));
604 }
605
606 #[test]
607 fn serde_endpoint_skips_none_profile() {
608 let ep = SofiaGateway {
609 gateway: "gw".into(),
610 destination: "1234".into(),
611 profile: None,
612 variables: None,
613 };
614 let json = serde_json::to_string(&ep).unwrap();
615 assert!(!json.contains("profile"));
616 }
617
618 #[test]
621 fn portaudio_display() {
622 let ep = AudioEndpoint {
623 destination: Some("auto_answer".into()),
624 variables: None,
625 };
626 let endpoint = Endpoint::PortAudio(ep);
627 assert_eq!(endpoint.to_string(), "portaudio/auto_answer");
628 }
629
630 #[test]
631 fn portaudio_bare_display() {
632 let ep = AudioEndpoint {
633 destination: None,
634 variables: None,
635 };
636 let endpoint = Endpoint::PortAudio(ep);
637 assert_eq!(endpoint.to_string(), "portaudio");
638 }
639
640 #[test]
641 fn portaudio_from_str() {
642 let ep: Endpoint = "portaudio/auto_answer"
643 .parse()
644 .unwrap();
645 if let Endpoint::PortAudio(audio) = ep {
646 assert_eq!(
647 audio
648 .destination
649 .as_deref(),
650 Some("auto_answer")
651 );
652 } else {
653 panic!("expected PortAudio");
654 }
655 }
656
657 #[test]
658 fn portaudio_bare_from_str() {
659 let ep: Endpoint = "portaudio"
660 .parse()
661 .unwrap();
662 if let Endpoint::PortAudio(audio) = ep {
663 assert!(audio
664 .destination
665 .is_none());
666 } else {
667 panic!("expected PortAudio");
668 }
669 }
670
671 #[test]
672 fn portaudio_round_trip() {
673 let input = "portaudio/auto_answer";
674 let ep: Endpoint = input
675 .parse()
676 .unwrap();
677 assert_eq!(ep.to_string(), input);
678 }
679
680 #[test]
681 fn portaudio_bare_round_trip() {
682 let input = "portaudio";
683 let ep: Endpoint = input
684 .parse()
685 .unwrap();
686 assert_eq!(ep.to_string(), input);
687 }
688
689 #[test]
690 fn portaudio_with_variables() {
691 let mut vars = Variables::new(VariablesType::Default);
692 vars.insert("codec", "PCMU");
693 let ep = Endpoint::PortAudio(AudioEndpoint {
694 destination: Some("auto_answer".into()),
695 variables: Some(vars),
696 });
697 assert_eq!(ep.to_string(), "{codec=PCMU}portaudio/auto_answer");
698 let parsed: Endpoint = ep
699 .to_string()
700 .parse()
701 .unwrap();
702 assert_eq!(parsed, ep);
703 }
704
705 #[test]
706 fn pulseaudio_display() {
707 let ep = Endpoint::PulseAudio(AudioEndpoint {
708 destination: Some("auto_answer".into()),
709 variables: None,
710 });
711 assert_eq!(ep.to_string(), "pulseaudio/auto_answer");
712 }
713
714 #[test]
715 fn pulseaudio_from_str() {
716 let ep: Endpoint = "pulseaudio/auto_answer"
717 .parse()
718 .unwrap();
719 assert!(matches!(ep, Endpoint::PulseAudio(_)));
720 }
721
722 #[test]
723 fn pulseaudio_round_trip() {
724 let input = "pulseaudio/auto_answer";
725 let ep: Endpoint = input
726 .parse()
727 .unwrap();
728 assert_eq!(ep.to_string(), input);
729 }
730
731 #[test]
732 fn alsa_display() {
733 let ep = Endpoint::Alsa(AudioEndpoint {
734 destination: Some("auto_answer".into()),
735 variables: None,
736 });
737 assert_eq!(ep.to_string(), "alsa/auto_answer");
738 }
739
740 #[test]
741 fn alsa_from_str() {
742 let ep: Endpoint = "alsa/auto_answer"
743 .parse()
744 .unwrap();
745 assert!(matches!(ep, Endpoint::Alsa(_)));
746 }
747
748 #[test]
749 fn alsa_bare_round_trip() {
750 let input = "alsa";
751 let ep: Endpoint = input
752 .parse()
753 .unwrap();
754 assert_eq!(ep.to_string(), input);
755 }
756
757 #[test]
758 fn serde_portaudio() {
759 let ep = Endpoint::PortAudio(AudioEndpoint {
760 destination: Some("auto_answer".into()),
761 variables: None,
762 });
763 let json = serde_json::to_string(&ep).unwrap();
764 assert!(json.contains("\"portaudio\""));
765 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
766 assert_eq!(parsed, ep);
767 }
768
769 #[test]
770 fn serde_pulseaudio() {
771 let ep = Endpoint::PulseAudio(AudioEndpoint {
772 destination: Some("auto_answer".into()),
773 variables: None,
774 });
775 let json = serde_json::to_string(&ep).unwrap();
776 assert!(json.contains("\"pulseaudio\""));
777 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
778 assert_eq!(parsed, ep);
779 }
780
781 #[test]
782 fn serde_alsa() {
783 let ep = Endpoint::Alsa(AudioEndpoint {
784 destination: None,
785 variables: None,
786 });
787 let json = serde_json::to_string(&ep).unwrap();
788 assert!(json.contains("\"alsa\""));
789 let parsed: Endpoint = serde_json::from_str(&json).unwrap();
790 assert_eq!(parsed, ep);
791 }
792
793 #[test]
796 fn from_sofia_endpoint() {
797 let inner = SofiaEndpoint {
798 profile: "internal".into(),
799 destination: "1000@domain.com".into(),
800 variables: None,
801 };
802 let ep: Endpoint = inner
803 .clone()
804 .into();
805 assert_eq!(ep, Endpoint::Sofia(inner));
806 }
807
808 #[test]
809 fn from_sofia_gateway() {
810 let inner = SofiaGateway {
811 gateway: "gw1".into(),
812 destination: "1234".into(),
813 profile: None,
814 variables: None,
815 };
816 let ep: Endpoint = inner
817 .clone()
818 .into();
819 assert_eq!(ep, Endpoint::SofiaGateway(inner));
820 }
821
822 #[test]
823 fn from_loopback_endpoint() {
824 let inner = LoopbackEndpoint::new("9199").with_context("default");
825 let ep: Endpoint = inner
826 .clone()
827 .into();
828 assert_eq!(ep, Endpoint::Loopback(inner));
829 }
830
831 #[test]
832 fn from_user_endpoint() {
833 let inner = UserEndpoint {
834 name: "bob".into(),
835 domain: Some("example.com".into()),
836 variables: None,
837 };
838 let ep: Endpoint = inner
839 .clone()
840 .into();
841 assert_eq!(ep, Endpoint::User(inner));
842 }
843
844 #[test]
845 fn from_sofia_contact() {
846 let inner = SofiaContact {
847 user: "1000".into(),
848 domain: "domain.com".into(),
849 profile: None,
850 variables: None,
851 };
852 let ep: Endpoint = inner
853 .clone()
854 .into();
855 assert_eq!(ep, Endpoint::SofiaContact(inner));
856 }
857
858 #[test]
859 fn from_group_call() {
860 let inner = GroupCall::new("support", "domain.com").with_order(GroupCallOrder::All);
861 let ep: Endpoint = inner
862 .clone()
863 .into();
864 assert_eq!(ep, Endpoint::GroupCall(inner));
865 }
866
867 #[test]
868 fn from_error_endpoint() {
869 let inner = ErrorEndpoint::new(crate::channel::HangupCause::UserBusy);
870 let ep: Endpoint = inner.into();
871 assert_eq!(ep, Endpoint::Error(inner));
872 }
873}