1use indexmap::IndexMap;
4use std::fmt;
5use std::str::FromStr;
6
7use super::originate::OriginateError;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
17#[non_exhaustive]
18pub enum VariablesType {
19 Enterprise,
21 Default,
23 Channel,
25}
26
27impl VariablesType {
28 pub(super) fn delimiters(self) -> (char, char) {
29 match self {
30 Self::Enterprise => ('<', '>'),
31 Self::Default => ('{', '}'),
32 Self::Channel => ('[', ']'),
33 }
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct Variables {
50 vars_type: VariablesType,
51 inner: IndexMap<String, String>,
52}
53
54pub(super) fn escape_value(value: &str) -> String {
55 let escaped = value
56 .replace('\'', "\\'")
57 .replace(',', "\\,");
58 if escaped.contains(' ') {
59 format!("'{}'", escaped)
60 } else {
61 escaped
62 }
63}
64
65fn unescape_value(value: &str) -> String {
66 let s = value
67 .strip_prefix('\'')
68 .and_then(|s| s.strip_suffix('\''))
69 .unwrap_or(value);
70 s.replace("\\,", ",")
71 .replace("\\'", "'")
72}
73
74impl Variables {
75 pub fn new(vars_type: VariablesType) -> Self {
77 Self {
78 vars_type,
79 inner: IndexMap::new(),
80 }
81 }
82
83 pub fn with_vars(
85 vars_type: VariablesType,
86 vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
87 ) -> Self {
88 Self {
89 vars_type,
90 inner: vars
91 .into_iter()
92 .map(|(k, v)| (k.into(), v.into()))
93 .collect(),
94 }
95 }
96
97 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) {
99 self.inner
100 .insert(key.into(), value.into());
101 }
102
103 pub fn remove(&mut self, key: &str) -> Option<String> {
105 self.inner
106 .shift_remove(key)
107 }
108
109 pub fn get(&self, key: &str) -> Option<&str> {
111 self.inner
112 .get(key)
113 .map(|s| s.as_str())
114 }
115
116 pub fn is_empty(&self) -> bool {
118 self.inner
119 .is_empty()
120 }
121
122 pub fn len(&self) -> usize {
124 self.inner
125 .len()
126 }
127
128 pub fn scope(&self) -> VariablesType {
130 self.vars_type
131 }
132
133 pub fn set_scope(&mut self, scope: VariablesType) {
135 self.vars_type = scope;
136 }
137
138 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
140 self.inner
141 .iter()
142 .map(|(k, v)| (k.as_str(), v.as_str()))
143 }
144
145 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut String)> {
147 self.inner
148 .iter_mut()
149 .map(|(k, v)| (k.as_str(), v))
150 }
151
152 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut String> {
154 self.inner
155 .values_mut()
156 }
157}
158
159#[cfg(feature = "serde")]
160impl serde::Serialize for Variables {
161 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
162 if self.vars_type == VariablesType::Default {
163 self.inner
164 .serialize(serializer)
165 } else {
166 use serde::ser::SerializeStruct;
167 let mut s = serializer.serialize_struct("Variables", 2)?;
168 s.serialize_field("scope", &self.vars_type)?;
169 s.serialize_field("vars", &self.inner)?;
170 s.end()
171 }
172 }
173}
174
175#[cfg(feature = "serde")]
176impl<'de> serde::Deserialize<'de> for Variables {
177 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
178 #[derive(serde::Deserialize)]
179 #[serde(untagged)]
180 enum VariablesRepr {
181 Scoped {
182 scope: VariablesType,
183 vars: IndexMap<String, String>,
184 },
185 Flat(IndexMap<String, String>),
186 }
187
188 match VariablesRepr::deserialize(deserializer)? {
189 VariablesRepr::Scoped { scope, vars } => Ok(Self {
190 vars_type: scope,
191 inner: vars,
192 }),
193 VariablesRepr::Flat(map) => Ok(Self {
194 vars_type: VariablesType::Default,
195 inner: map,
196 }),
197 }
198 }
199}
200
201impl fmt::Display for Variables {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 let (open, close) = self
204 .vars_type
205 .delimiters();
206 f.write_fmt(format_args!("{}", open))?;
207 for (i, (key, value)) in self
208 .inner
209 .iter()
210 .enumerate()
211 {
212 if i > 0 {
213 f.write_str(",")?;
214 }
215 write!(f, "{}={}", key, escape_value(value))?;
216 }
217 f.write_fmt(format_args!("{}", close))
218 }
219}
220
221impl FromStr for Variables {
222 type Err = OriginateError;
223
224 fn from_str(s: &str) -> Result<Self, Self::Err> {
225 let s = s.trim();
226 if s.len() < 2 {
227 return Err(OriginateError::ParseError(
228 "variable block too short".into(),
229 ));
230 }
231
232 let (vars_type, inner_str) = match (s.as_bytes()[0], s.as_bytes()[s.len() - 1]) {
233 (b'{', b'}') => (VariablesType::Default, &s[1..s.len() - 1]),
234 (b'<', b'>') => (VariablesType::Enterprise, &s[1..s.len() - 1]),
235 (b'[', b']') => (VariablesType::Channel, &s[1..s.len() - 1]),
236 _ => {
237 return Err(OriginateError::ParseError(format!(
238 "unknown variable delimiters: {}",
239 s
240 )));
241 }
242 };
243
244 let mut inner = IndexMap::new();
245 if !inner_str.is_empty() {
246 if let Some(rest) = inner_str.strip_prefix("^^") {
247 let sep = rest
248 .chars()
249 .next()
250 .ok_or_else(|| {
251 OriginateError::ParseError("^^ without separator character".into())
252 })?;
253 let (_, close) = vars_type.delimiters();
254 if sep == close || sep == '=' {
255 return Err(OriginateError::ParseError(format!(
256 "invalid ^^ separator: '{sep}'"
257 )));
258 }
259 let var_str = &rest[sep.len_utf8()..];
260 if !var_str.is_empty() {
261 for part in var_str.split(sep) {
262 let (key, value) = part
263 .split_once('=')
264 .ok_or_else(|| {
265 OriginateError::ParseError(format!("missing = in variable: {part}"))
266 })?;
267 inner.insert(key.to_string(), value.to_string());
268 }
269 }
270 } else {
271 for part in split_unescaped_commas(inner_str) {
272 let (key, value) = part
273 .split_once('=')
274 .ok_or_else(|| {
275 OriginateError::ParseError(format!("missing = in variable: {part}"))
276 })?;
277 inner.insert(key.to_string(), unescape_value(value));
278 }
279 }
280 }
281
282 Ok(Self { vars_type, inner })
283 }
284}
285
286pub(super) fn split_unescaped_commas(s: &str) -> Vec<&str> {
292 let mut parts = Vec::new();
293 let mut start = 0;
294 let bytes = s.as_bytes();
295
296 for i in 0..bytes.len() {
297 if bytes[i] == b',' {
298 let mut backslashes = 0;
299 let mut j = i;
300 while j > 0 && bytes[j - 1] == b'\\' {
301 backslashes += 1;
302 j -= 1;
303 }
304 if backslashes % 2 == 0 {
305 parts.push(&s[start..i]);
306 start = i + 1;
307 }
308 }
309 }
310 parts.push(&s[start..]);
311 parts
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317
318 #[test]
319 fn variables_standard_chars() {
320 let mut vars = Variables::new(VariablesType::Default);
321 vars.insert("test_key", "this_value");
322 let result = vars.to_string();
323 assert!(result.contains("test_key"));
324 assert!(result.contains("this_value"));
325 }
326
327 #[test]
328 fn variables_comma_escaped() {
329 let mut vars = Variables::new(VariablesType::Default);
330 vars.insert("test_key", "this,is,a,value");
331 let result = vars.to_string();
332 assert!(result.contains("\\,"));
333 }
334
335 #[test]
336 fn variables_spaces_quoted() {
337 let mut vars = Variables::new(VariablesType::Default);
338 vars.insert("test_key", "this is a value");
339 let result = vars.to_string();
340 assert_eq!(
341 result
342 .matches('\'')
343 .count(),
344 2
345 );
346 }
347
348 #[test]
349 fn variables_single_quote_escaped() {
350 let mut vars = Variables::new(VariablesType::Default);
351 vars.insert("test_key", "let's_this_be_a_value");
352 let result = vars.to_string();
353 assert!(result.contains("\\'"));
354 }
355
356 #[test]
357 fn variables_enterprise_delimiters() {
358 let mut vars = Variables::new(VariablesType::Enterprise);
359 vars.insert("k", "v");
360 let result = vars.to_string();
361 assert!(result.starts_with('<'));
362 assert!(result.ends_with('>'));
363 }
364
365 #[test]
366 fn variables_channel_delimiters() {
367 let mut vars = Variables::new(VariablesType::Channel);
368 vars.insert("k", "v");
369 let result = vars.to_string();
370 assert!(result.starts_with('['));
371 assert!(result.ends_with(']'));
372 }
373
374 #[test]
375 fn variables_default_delimiters() {
376 let mut vars = Variables::new(VariablesType::Default);
377 vars.insert("k", "v");
378 let result = vars.to_string();
379 assert!(result.starts_with('{'));
380 assert!(result.ends_with('}'));
381 }
382
383 #[test]
384 fn variables_parse_round_trip() {
385 let mut vars = Variables::new(VariablesType::Default);
386 vars.insert("origination_caller_id_number", "9005551212");
387 vars.insert("sip_h_Call-Info", "<url>;meta=123,<uri>");
388 let s = vars.to_string();
389 let parsed: Variables = s
390 .parse()
391 .unwrap();
392 assert_eq!(
393 parsed.get("origination_caller_id_number"),
394 Some("9005551212")
395 );
396 assert_eq!(parsed.get("sip_h_Call-Info"), Some("<url>;meta=123,<uri>"));
397 }
398
399 #[test]
400 fn split_unescaped_commas_basic() {
401 assert_eq!(split_unescaped_commas("a,b,c"), vec!["a", "b", "c"]);
402 }
403
404 #[test]
405 fn split_unescaped_commas_escaped() {
406 assert_eq!(split_unescaped_commas(r"a\,b,c"), vec![r"a\,b", "c"]);
407 }
408
409 #[test]
410 fn split_unescaped_commas_double_backslash() {
411 assert_eq!(split_unescaped_commas(r"a\\,b"), vec![r"a\\", "b"]);
413 }
414
415 #[test]
416 fn split_unescaped_commas_triple_backslash() {
417 assert_eq!(split_unescaped_commas(r"a\\\,b"), vec![r"a\\\,b"]);
419 }
420
421 #[test]
422 fn variables_caret_caret_separator() {
423 let vars: Variables =
424 "[^^:sip_invite_domain=pbx.example.com:presence_id=1211@pbx.example.com]"
425 .parse()
426 .unwrap();
427 assert_eq!(vars.scope(), VariablesType::Channel);
428 assert_eq!(vars.get("sip_invite_domain"), Some("pbx.example.com"));
429 assert_eq!(vars.get("presence_id"), Some("1211@pbx.example.com"));
430 }
431
432 #[test]
433 fn variables_caret_caret_display_uses_canonical_comma() {
434 let vars: Variables = "[^^:a=1:b=2]"
435 .parse()
436 .unwrap();
437 assert_eq!(vars.to_string(), "[a=1,b=2]");
438 }
439
440 #[test]
441 fn variables_caret_caret_default_scope() {
442 let vars: Variables = "{^^|x=1|y=2}"
443 .parse()
444 .unwrap();
445 assert_eq!(vars.scope(), VariablesType::Default);
446 assert_eq!(vars.get("x"), Some("1"));
447 assert_eq!(vars.get("y"), Some("2"));
448 }
449
450 #[test]
451 fn variables_caret_caret_enterprise_scope() {
452 let vars: Variables = "<^^;a=1;b=2>"
453 .parse()
454 .unwrap();
455 assert_eq!(vars.scope(), VariablesType::Enterprise);
456 assert_eq!(vars.get("a"), Some("1"));
457 }
458
459 #[test]
460 fn variables_caret_caret_no_unescape() {
461 let vars: Variables = r"[^^:key=val\,ue:other=x]"
462 .parse()
463 .unwrap();
464 assert_eq!(vars.get("key"), Some(r"val\,ue"));
465 }
466
467 #[test]
468 fn variables_caret_caret_values_with_commas() {
469 let vars: Variables = "[^^|sip_h_X-Call-Info=<urn:foo>;purpose=bar,<urn:baz>|other=val]"
470 .parse()
471 .unwrap();
472 assert_eq!(
473 vars.get("sip_h_X-Call-Info"),
474 Some("<urn:foo>;purpose=bar,<urn:baz>")
475 );
476 assert_eq!(vars.get("other"), Some("val"));
477 }
478
479 #[test]
480 fn variables_caret_caret_empty_vars() {
481 let vars: Variables = "[^^:]"
482 .parse()
483 .unwrap();
484 assert!(vars.is_empty());
485 assert_eq!(vars.scope(), VariablesType::Channel);
486 }
487
488 #[test]
489 fn variables_caret_caret_missing_separator() {
490 assert!("[^^]"
491 .parse::<Variables>()
492 .is_err());
493 }
494
495 #[test]
496 fn variables_caret_caret_closing_bracket_as_sep() {
497 assert!("[^^]]"
498 .parse::<Variables>()
499 .is_err());
500 }
501
502 #[test]
503 fn variables_caret_caret_equals_as_sep() {
504 assert!("[^^=a=1]"
505 .parse::<Variables>()
506 .is_err());
507 }
508
509 #[test]
510 fn variables_from_str_empty_block() {
511 let result = "{}".parse::<Variables>();
512 assert!(
513 result.is_ok(),
514 "empty variable block should parse successfully"
515 );
516 let vars = result.unwrap();
517 assert!(
518 vars.is_empty(),
519 "parsed empty block should have no variables"
520 );
521 }
522
523 #[test]
524 fn variables_from_str_empty_channel_block() {
525 let result = "[]".parse::<Variables>();
526 assert!(result.is_ok());
527 let vars = result.unwrap();
528 assert!(vars.is_empty());
529 assert_eq!(vars.scope(), VariablesType::Channel);
530 }
531
532 #[test]
533 fn variables_from_str_empty_enterprise_block() {
534 let result = "<>".parse::<Variables>();
535 assert!(result.is_ok());
536 let vars = result.unwrap();
537 assert!(vars.is_empty());
538 assert_eq!(vars.scope(), VariablesType::Enterprise);
539 }
540
541 #[test]
542 fn serde_variables_type() {
543 let json = serde_json::to_string(&VariablesType::Enterprise).unwrap();
544 assert_eq!(json, "\"enterprise\"");
545 let parsed: VariablesType = serde_json::from_str(&json).unwrap();
546 assert_eq!(parsed, VariablesType::Enterprise);
547 }
548
549 #[test]
550 fn serde_variables_flat_default() {
551 let mut vars = Variables::new(VariablesType::Default);
552 vars.insert("key1", "val1");
553 vars.insert("key2", "val2");
554 let json = serde_json::to_string(&vars).unwrap();
555 let parsed: Variables = serde_json::from_str(&json).unwrap();
556 assert_eq!(parsed.scope(), VariablesType::Default);
557 assert_eq!(parsed.get("key1"), Some("val1"));
558 assert_eq!(parsed.get("key2"), Some("val2"));
559 }
560
561 #[test]
562 fn serde_variables_scoped_enterprise() {
563 let mut vars = Variables::new(VariablesType::Enterprise);
564 vars.insert("key1", "val1");
565 let json = serde_json::to_string(&vars).unwrap();
566 assert!(json.contains("\"enterprise\""));
567 let parsed: Variables = serde_json::from_str(&json).unwrap();
568 assert_eq!(parsed.scope(), VariablesType::Enterprise);
569 assert_eq!(parsed.get("key1"), Some("val1"));
570 }
571
572 #[test]
573 fn serde_variables_flat_map_deserializes_as_default() {
574 let json = r#"{"key1":"val1","key2":"val2"}"#;
575 let vars: Variables = serde_json::from_str(json).unwrap();
576 assert_eq!(vars.scope(), VariablesType::Default);
577 assert_eq!(vars.get("key1"), Some("val1"));
578 assert_eq!(vars.get("key2"), Some("val2"));
579 }
580
581 #[test]
582 fn serde_variables_scoped_deserializes() {
583 let json = r#"{"scope":"channel","vars":{"k":"v"}}"#;
584 let vars: Variables = serde_json::from_str(json).unwrap();
585 assert_eq!(vars.scope(), VariablesType::Channel);
586 assert_eq!(vars.get("k"), Some("v"));
587 }
588}