1macro_rules! impl_dial_string_with_variables {
14 ($ty:ty) => {
15 impl $crate::commands::endpoint::DialString for $ty {
16 fn variables(&self) -> Option<&$crate::commands::variables::Variables> {
17 self.variables
18 .as_ref()
19 }
20 fn variables_mut(&mut self) -> Option<&mut $crate::commands::variables::Variables> {
21 self.variables
22 .as_mut()
23 }
24 fn set_variables(&mut self, vars: Option<$crate::commands::variables::Variables>) {
25 self.variables = vars;
26 }
27 }
28
29 impl $ty {
30 pub fn with_variables(
32 mut self,
33 variables: $crate::commands::variables::Variables,
34 ) -> Self {
35 self.variables = Some(variables);
36 self
37 }
38 }
39 };
40 ($ty:ty, $write:ident, |$this:ident| $text:expr) => {
41 impl_dial_string_with_variables!($ty);
42
43 impl $ty {
44 pub(crate) fn module_text(&self) -> String {
46 let $this = self;
47 $text
48 }
49
50 pub(super) fn write_for(
51 &self,
52 f: &mut ::std::fmt::Formatter<'_>,
53 target: $crate::commands::variables::DialStringTarget,
54 ) -> ::std::fmt::Result {
55 $crate::commands::endpoint::write_variables(f, &self.variables, target)?;
56 $crate::commands::endpoint::$write(f, &self.module_text(), target)
57 }
58 }
59
60 impl ::std::fmt::Display for $ty {
61 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
62 self.write_for(
63 f,
64 $crate::commands::variables::DialStringCarrier::EslApi.into(),
65 )
66 }
67 }
68 };
69}
70
71macro_rules! forward_to_variant {
74 ($self:ident, $method:ident $(, $arg:expr)?) => {
75 match $self {
76 Self::Sofia(ep) => ep.$method($($arg)?),
77 Self::SofiaGateway(ep) => ep.$method($($arg)?),
78 Self::Loopback(ep) => ep.$method($($arg)?),
79 Self::User(ep) => ep.$method($($arg)?),
80 Self::SofiaContact(ep) => ep.$method($($arg)?),
81 Self::GroupCall(ep) => ep.$method($($arg)?),
82 Self::Error(ep) => ep.$method($($arg)?),
83 Self::PortAudio(ep) | Self::PulseAudio(ep) | Self::Alsa(ep) => ep.$method($($arg)?),
84 }
85 };
86}
87
88macro_rules! impl_endpoint_parse {
91 (from_str: $($ty:ident),* $(,)?) => {
92 $(
93 impl ::std::str::FromStr for $ty {
94 type Err = $crate::commands::originate::OriginateError;
95
96 fn from_str(s: &str) -> Result<Self, Self::Err> {
97 let (variables, ep) = $crate::commands::endpoint::parse_leg(
98 s,
99 $crate::commands::variables::DialStringCarrier::EslApi.into(),
100 Self::parse_bare,
101 )?;
102 Ok(Self { variables, ..ep })
103 }
104 }
105 )*
106 };
107 (expression_from_str: $($ty:ident => $kind:literal),* $(,)?) => {
108 $(
109 impl ::std::str::FromStr for $ty {
110 type Err = $crate::commands::originate::OriginateError;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 $crate::commands::endpoint::parse_leg(
115 s,
116 $crate::commands::variables::DialStringCarrier::EslApi.into(),
117 Self::parse_bare,
118 )?;
119 Err($crate::commands::originate::OriginateError::UnexpandedExpression {
120 endpoint: $kind,
121 })
122 }
123 }
124 )*
125 };
126 (config: $($ty:ident { $($required:ident: $rty:ty,)* ; $($optional:ident: $oty:ty,)* })*) => {
127 #[cfg(feature = "serde")]
128 mod config {
129 use super::*;
130
131 $(
132 #[derive(serde::Deserialize)]
133 pub(super) struct $ty {
134 $(pub(super) $required: $rty,)*
135 $(
136 #[serde(default)]
137 pub(super) $optional: $oty,
138 )*
139 }
140 )*
141 }
142
143 $(
144 #[cfg(feature = "serde")]
145 impl TryFrom<config::$ty> for $ty {
146 type Error = $crate::commands::originate::OriginateError;
147
148 fn try_from(config: config::$ty) -> Result<Self, Self::Error> {
149 let ep = Self {
150 $($required: config.$required,)*
151 $($optional: config.$optional,)*
152 };
153 ep.check_deliverable()?;
154 Ok(ep)
155 }
156 }
157 )*
158 };
159}
160
161mod audio;
162mod error;
163mod group_call;
164mod loopback;
165mod sofia;
166mod user;
167
168pub use audio::AudioEndpoint;
169pub use error::ErrorEndpoint;
170pub use group_call::{GroupCall, GroupCallOrder, ParseGroupCallOrderError};
171pub use loopback::LoopbackEndpoint;
172pub use sofia::{DestinationEncoding, SofiaContact, SofiaEndpoint, SofiaGateway};
173pub use user::UserEndpoint;
174
175use std::fmt;
176use std::str::FromStr;
177
178use super::originate::OriginateError;
179use super::variables::{
180 installed_variables, read_leg, DialStringCarrier, DialStringTarget, Variables,
181};
182use crate::switch_passes::brackets::unbalanced;
183use crate::switch_passes::escape::{escape_text, EscapedField};
184use crate::switch_passes::originate_legs::splits_into_threads;
185
186type PrefixParser = fn(&str) -> Result<Endpoint, OriginateError>;
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190#[non_exhaustive]
191pub enum EndpointFieldFault {
192 EnterpriseSeparator,
194 ModuleSeparator(&'static str),
196 ReadsAsGateway,
198 FollowsAnApplication,
200 EmptyReadsAsDefault,
202 FunctionSeparator(&'static str),
204 ReadBeforeTheFunction,
207 EmptyDialsNothing,
209}
210
211impl fmt::Display for EndpointFieldFault {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 match self {
214 Self::EnterpriseSeparator => {
215 f.write_str("carries :_:, on which the switch splits the dial string into threads")
216 }
217 Self::ModuleSeparator(sep) => {
218 write!(
219 f,
220 "carries {sep}, on which the endpoint module splits its text"
221 )
222 }
223 Self::ReadsAsGateway => f.write_str("reads as gateway, which mod_sofia dials as one"),
224 Self::FollowsAnApplication => f.write_str(
225 "follows an app= extension, which mod_loopback reads as the application's argument",
226 ),
227 Self::EmptyReadsAsDefault => {
228 f.write_str("is empty, which the switch reads as its default")
229 }
230 Self::FunctionSeparator(sep) => write!(
231 f,
232 "carries {sep}, on which the expression's function splits its argument"
233 ),
234 Self::ReadBeforeTheFunction => f.write_str(
235 "carries a space, comma, pipe, quote, backslash or unbalanced bracket, \
236 which a pass reads before the expression's function",
237 ),
238 Self::EmptyDialsNothing => {
239 f.write_str("is empty, so the endpoint module dials nothing")
240 }
241 }
242 }
243}
244
245pub(super) fn check_expression_field(
248 endpoint: &'static str,
249 field: &'static str,
250 text: &str,
251 separators: &[&'static str],
252) -> Result<(), OriginateError> {
253 let fault = if splits_into_threads(text) {
254 Some(EndpointFieldFault::EnterpriseSeparator)
255 } else if let Some(sep) = separators
256 .iter()
257 .find(|sep| text.contains(**sep))
258 {
259 Some(EndpointFieldFault::FunctionSeparator(sep))
260 } else if text.contains([' ', ',', '|', '\'', '\\'])
261 || unbalanced(text, ('{', '}')).is_some()
262 || unbalanced(text, ('(', ')')).is_some()
263 {
264 Some(EndpointFieldFault::ReadBeforeTheFunction)
265 } else {
266 None
267 };
268 fault.map_or(Ok(()), |fault| Err(undeliverable(endpoint, field, fault)))
269}
270
271pub(super) fn undeliverable(
272 endpoint: &'static str,
273 field: &'static str,
274 fault: EndpointFieldFault,
275) -> OriginateError {
276 OriginateError::UndeliverableEndpointField {
277 endpoint,
278 field,
279 fault,
280 }
281}
282
283pub(super) fn check_field(
285 endpoint: &'static str,
286 field: &'static str,
287 text: &str,
288 separators: &[&'static str],
289) -> Result<(), OriginateError> {
290 let fault = if splits_into_threads(text) {
291 Some(EndpointFieldFault::EnterpriseSeparator)
292 } else {
293 separators
294 .iter()
295 .find(|sep| text.contains(**sep))
296 .map(|sep| EndpointFieldFault::ModuleSeparator(sep))
297 };
298 fault.map_or(Ok(()), |fault| Err(undeliverable(endpoint, field, fault)))
299}
300
301pub trait DialString: fmt::Display {
306 fn variables(&self) -> Option<&Variables>;
308 fn variables_mut(&mut self) -> Option<&mut Variables>;
310 fn set_variables(&mut self, vars: Option<Variables>);
312}
313
314fn write_variables(
319 f: &mut fmt::Formatter<'_>,
320 vars: &Option<Variables>,
321 target: DialStringTarget,
322) -> fmt::Result {
323 if let Some(vars) = vars {
324 if !vars.is_empty() {
325 write!(f, "{}", vars.display_for(target))?;
326 }
327 }
328 Ok(())
329}
330
331fn write_module_text(
333 f: &mut fmt::Formatter<'_>,
334 text: &str,
335 target: DialStringTarget,
336) -> fmt::Result {
337 f.write_str(&escape_text(text, target, EscapedField::Endpoint))
338}
339
340fn write_expression(f: &mut fmt::Formatter<'_>, text: &str, _: DialStringTarget) -> fmt::Result {
342 f.write_str(text)
343}
344
345pub(super) fn after_prefix<'a>(
349 text: &'a str,
350 prefix: &str,
351 kind: &str,
352) -> Result<&'a str, OriginateError> {
353 text.strip_prefix(prefix)
354 .filter(|rest| prefix.ends_with('/') || rest.is_empty() || rest.starts_with('/'))
355 .ok_or_else(|| OriginateError::ParseError(format!("not a {kind} endpoint")))
356}
357
358pub(super) fn parse_leg<T>(
361 s: &str,
362 target: DialStringTarget,
363 bare: impl FnOnce(&str) -> Result<T, OriginateError>,
364) -> Result<(Option<Variables>, T), OriginateError> {
365 let (argument, target) = target.read_argument(s)?;
366 let (block, text) = read_leg(&argument, target)?;
367 Ok((installed_variables(block.as_ref())?, bare(&text)?))
368}
369
370#[derive(Debug, Clone, PartialEq, Eq)]
379#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
380#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
381#[non_exhaustive]
382pub enum Endpoint {
383 Sofia(SofiaEndpoint),
385 SofiaGateway(SofiaGateway),
387 Loopback(LoopbackEndpoint),
389 User(UserEndpoint),
391 SofiaContact(SofiaContact),
393 GroupCall(GroupCall),
395 Error(ErrorEndpoint),
397 #[cfg_attr(feature = "serde", serde(rename = "portaudio"))]
399 PortAudio(AudioEndpoint),
400 #[cfg_attr(feature = "serde", serde(rename = "pulseaudio"))]
402 PulseAudio(AudioEndpoint),
403 Alsa(AudioEndpoint),
405}
406
407impl From<SofiaEndpoint> for Endpoint {
412 fn from(ep: SofiaEndpoint) -> Self {
413 Self::Sofia(ep)
414 }
415}
416
417impl From<SofiaGateway> for Endpoint {
418 fn from(ep: SofiaGateway) -> Self {
419 Self::SofiaGateway(ep)
420 }
421}
422
423impl From<LoopbackEndpoint> for Endpoint {
424 fn from(ep: LoopbackEndpoint) -> Self {
425 Self::Loopback(ep)
426 }
427}
428
429impl From<UserEndpoint> for Endpoint {
430 fn from(ep: UserEndpoint) -> Self {
431 Self::User(ep)
432 }
433}
434
435impl From<SofiaContact> for Endpoint {
436 fn from(ep: SofiaContact) -> Self {
437 Self::SofiaContact(ep)
438 }
439}
440
441impl From<GroupCall> for Endpoint {
442 fn from(ep: GroupCall) -> Self {
443 Self::GroupCall(ep)
444 }
445}
446
447impl From<ErrorEndpoint> for Endpoint {
448 fn from(ep: ErrorEndpoint) -> Self {
449 Self::Error(ep)
450 }
451}
452
453impl Endpoint {
458 pub(crate) fn write_for(
459 &self,
460 f: &mut fmt::Formatter<'_>,
461 target: DialStringTarget,
462 ) -> fmt::Result {
463 match self {
464 Self::Sofia(ep) => ep.write_for(f, target),
465 Self::SofiaGateway(ep) => ep.write_for(f, target),
466 Self::Loopback(ep) => ep.write_for(f, target),
467 Self::User(ep) => ep.write_for(f, target),
468 Self::SofiaContact(ep) => ep.write_for(f, target),
469 Self::GroupCall(ep) => ep.write_for(f, target),
470 Self::Error(ep) => fmt::Display::fmt(ep, f),
471 Self::PortAudio(ep) => ep.write_with_prefix(f, "portaudio", target),
472 Self::PulseAudio(ep) => ep.write_with_prefix(f, "pulseaudio", target),
473 Self::Alsa(ep) => ep.write_with_prefix(f, "alsa", target),
474 }
475 }
476
477 pub fn display_for(&self, target: impl Into<DialStringTarget>) -> EndpointDisplay<'_> {
480 EndpointDisplay {
481 endpoint: self,
482 target: target.into(),
483 }
484 }
485
486 pub fn parse_for(s: &str, target: impl Into<DialStringTarget>) -> Result<Self, OriginateError> {
492 let target = target.into();
493 let (variables, mut endpoint) = parse_leg(s, target, Self::parse_bare)?;
494 endpoint.check_expanded(target.carrier())?;
495 if variables.is_some() {
496 endpoint.set_variables(variables);
497 if endpoint
498 .variables()
499 .is_none()
500 {
501 return Err(OriginateError::VariablesNotSupported(endpoint.kind()));
502 }
503 }
504 Ok(endpoint)
505 }
506
507 pub(crate) fn check_expanded(&self, carrier: DialStringCarrier) -> Result<(), OriginateError> {
509 match self {
510 Self::SofiaContact(_) | Self::GroupCall(_) if !carrier.expands() => {
511 Err(OriginateError::UnexpandedExpression {
512 endpoint: self.kind(),
513 })
514 }
515 _ => Ok(()),
516 }
517 }
518
519 pub(crate) fn kind(&self) -> &'static str {
521 match self {
522 Self::Sofia(_) => "sofia",
523 Self::SofiaGateway(_) => "sofia gateway",
524 Self::Loopback(_) => "loopback",
525 Self::User(_) => "user",
526 Self::SofiaContact(_) => "sofia_contact",
527 Self::GroupCall(_) => "group_call",
528 Self::Error(_) => "error",
529 Self::PortAudio(_) => "portaudio",
530 Self::PulseAudio(_) => "pulseaudio",
531 Self::Alsa(_) => "alsa",
532 }
533 }
534
535 const PREFIX_PARSERS: &[(&str, PrefixParser)] = &[
537 ("${sofia_contact(", |u| {
538 Ok(Self::SofiaContact(SofiaContact::parse_bare(u)?))
539 }),
540 ("${group_call(", |u| {
541 Ok(Self::GroupCall(GroupCall::parse_bare(u)?))
542 }),
543 ("error/", |u| Ok(Self::Error(u.parse()?))),
544 ("loopback/", |u| {
545 Ok(Self::Loopback(LoopbackEndpoint::parse_bare(u)?))
546 }),
547 ("sofia/gateway/", |u| {
549 Ok(Self::SofiaGateway(SofiaGateway::parse_bare(u)?))
550 }),
551 ("sofia/", |u| Ok(Self::Sofia(SofiaEndpoint::parse_bare(u)?))),
552 ("user/", |u| Ok(Self::User(UserEndpoint::parse_bare(u)?))),
553 ("portaudio", |u| {
554 Ok(Self::PortAudio(AudioEndpoint::parse_bare(u, "portaudio")?))
555 }),
556 ("pulseaudio", |u| {
557 Ok(Self::PulseAudio(AudioEndpoint::parse_bare(
558 u,
559 "pulseaudio",
560 )?))
561 }),
562 ("alsa", |u| {
563 Ok(Self::Alsa(AudioEndpoint::parse_bare(u, "alsa")?))
564 }),
565 ];
566
567 #[cfg(test)]
569 pub(crate) fn module_text(&self) -> String {
570 match self {
571 Self::Sofia(ep) => ep.module_text(),
572 Self::SofiaGateway(ep) => ep.module_text(),
573 Self::Loopback(ep) => ep.module_text(),
574 Self::User(ep) => ep.module_text(),
575 Self::SofiaContact(ep) => ep.module_text(),
576 Self::GroupCall(ep) => ep.module_text(),
577 Self::Error(ep) => ep.to_string(),
578 Self::PortAudio(ep) => ep.module_text("portaudio"),
579 Self::PulseAudio(ep) => ep.module_text("pulseaudio"),
580 Self::Alsa(ep) => ep.module_text("alsa"),
581 }
582 }
583
584 pub(crate) fn parse_bare(uri: &str) -> Result<Self, OriginateError> {
587 let (_, parse) = Self::PREFIX_PARSERS
588 .iter()
589 .find(|(prefix, _)| uri.starts_with(prefix))
590 .ok_or_else(|| OriginateError::UnknownEndpointType(uri.to_string()))?;
591 parse(uri)
592 }
593}
594
595#[derive(Debug, Clone, Copy)]
598pub struct EndpointDisplay<'a> {
599 endpoint: &'a Endpoint,
600 target: DialStringTarget,
601}
602
603impl fmt::Display for EndpointDisplay<'_> {
604 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605 self.target
606 .write_argument(f, |f, target| {
607 self.endpoint
608 .write_for(f, target)
609 })
610 }
611}
612
613impl fmt::Display for Endpoint {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 self.write_for(f, DialStringCarrier::EslApi.into())
616 }
617}
618
619impl FromStr for Endpoint {
624 type Err = OriginateError;
625
626 fn from_str(s: &str) -> Result<Self, Self::Err> {
627 Self::parse_for(s, DialStringCarrier::EslApi)
628 }
629}
630
631impl DialString for ErrorEndpoint {
636 fn variables(&self) -> Option<&Variables> {
637 None
638 }
639 fn variables_mut(&mut self) -> Option<&mut Variables> {
640 None
641 }
642 fn set_variables(&mut self, _vars: Option<Variables>) {}
643}
644
645impl DialString for Endpoint {
646 fn variables(&self) -> Option<&Variables> {
647 forward_to_variant!(self, variables)
648 }
649 fn variables_mut(&mut self) -> Option<&mut Variables> {
650 forward_to_variant!(self, variables_mut)
651 }
652 fn set_variables(&mut self, vars: Option<Variables>) {
653 forward_to_variant!(self, set_variables, vars)
654 }
655}
656
657#[cfg(test)]
662mod tests;
663
664#[cfg(all(test, feature = "serde"))]
665mod c_oracle;