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