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