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