freeswitch_types/commands/endpoint/
audio.rs1use std::fmt;
2
3use super::{
4 after_prefix, check_field, parse_leg, undeliverable, write_module_text, write_variables,
5 EndpointFieldFault,
6};
7use crate::commands::originate::OriginateError;
8use crate::commands::variables::{DialStringCarrier, DialStringTarget, Variables};
9
10#[derive(Debug, Clone, Default, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[cfg_attr(feature = "serde", serde(try_from = "config::AudioEndpoint"))]
17#[non_exhaustive]
18pub struct AudioEndpoint {
19 #[cfg_attr(
21 feature = "serde",
22 serde(default, skip_serializing_if = "Option::is_none")
23 )]
24 pub destination: Option<String>,
25 #[cfg_attr(
27 feature = "serde",
28 serde(default, skip_serializing_if = "Option::is_none")
29 )]
30 pub variables: Option<Variables>,
31}
32
33impl AudioEndpoint {
34 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn with_destination(mut self, destination: impl Into<String>) -> Self {
41 self.destination = Some(destination.into());
42 self
43 }
44
45 pub fn fmt_with_prefix(&self, f: &mut fmt::Formatter<'_>, prefix: &str) -> fmt::Result {
52 self.write_with_prefix(f, prefix, DialStringCarrier::EslApi.into())
53 }
54
55 pub(super) fn write_with_prefix(
56 &self,
57 f: &mut fmt::Formatter<'_>,
58 prefix: &str,
59 target: DialStringTarget,
60 ) -> fmt::Result {
61 write_variables(f, &self.variables, target)?;
62 write_module_text(f, &self.module_text(prefix), target)
63 }
64
65 pub(crate) fn module_text(&self, prefix: &str) -> String {
67 match &self.destination {
68 Some(dest) => format!("{prefix}/{dest}"),
69 None => prefix.to_owned(),
70 }
71 }
72
73 pub(crate) fn check_deliverable(&self) -> Result<(), OriginateError> {
75 match self
76 .destination
77 .as_deref()
78 {
79 Some("") => Err(undeliverable(
80 "audio",
81 "destination",
82 EndpointFieldFault::EmptyReadsAsDefault,
83 )),
84 Some(destination) => check_field("audio", "destination", destination, &[]),
85 None => Ok(()),
86 }
87 }
88
89 pub fn parse_with_prefix(s: &str, prefix: &str) -> Result<Self, OriginateError> {
91 let (variables, ep) = parse_leg(s, DialStringCarrier::EslApi.into(), |text| {
92 Self::parse_bare(text, prefix)
93 })?;
94 Ok(Self { variables, ..ep })
95 }
96
97 pub(crate) fn parse_bare(text: &str, prefix: &str) -> Result<Self, OriginateError> {
98 let rest = after_prefix(text, prefix, prefix)?;
99 let ep = Self {
100 destination: rest
101 .strip_prefix('/')
102 .filter(|d| !d.is_empty())
103 .map(str::to_string),
104 variables: None,
105 };
106 ep.check_deliverable()?;
107 Ok(ep)
108 }
109}
110
111impl_dial_string_with_variables!(AudioEndpoint);
112
113impl_endpoint_parse!(config:
114 AudioEndpoint {
115 ;
116 destination: Option<String>,
117 variables: Option<Variables>,
118 }
119);
120
121impl fmt::Display for AudioEndpoint {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 self.fmt_with_prefix(f, "audio")
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn parse_with_prefix_portaudio() {
139 let ep = AudioEndpoint::parse_with_prefix("portaudio/auto_answer", "portaudio").unwrap();
140 assert_eq!(
141 ep.destination
142 .as_deref(),
143 Some("auto_answer")
144 );
145 assert!(ep
146 .variables
147 .is_none());
148 }
149
150 #[test]
151 fn parse_with_prefix_bare() {
152 let ep = AudioEndpoint::parse_with_prefix("alsa", "alsa").unwrap();
153 assert!(ep
154 .destination
155 .is_none());
156 }
157
158 #[test]
159 fn parse_with_prefix_trailing_slash() {
160 let ep = AudioEndpoint::parse_with_prefix("pulseaudio/", "pulseaudio").unwrap();
161 assert!(ep
162 .destination
163 .is_none());
164 }
165
166 #[test]
167 fn parse_with_prefix_wrong_module() {
168 let result = AudioEndpoint::parse_with_prefix("portaudio/x", "alsa");
169 assert!(result.is_err());
170 }
171
172 #[test]
175 fn parse_with_prefix_rejects_a_longer_module_name() {
176 assert!(AudioEndpoint::parse_with_prefix("alsafoo/bar", "alsa").is_err());
177 assert!("alsafoo/bar"
178 .parse::<super::super::Endpoint>()
179 .is_err());
180 }
181}