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