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 (open, close) => {
237 return Err(OriginateError::ParseError(format!(
238 "unknown variable delimiters: {:?}..{:?}",
239 open as char, close as char
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 (i, part) in var_str
262 .split(sep)
263 .enumerate()
264 {
265 let (key, value) = part
266 .split_once('=')
267 .ok_or_else(|| {
268 OriginateError::ParseError(format!("missing = in variable {i}"))
269 })?;
270 inner.insert(key.to_string(), value.to_string());
271 }
272 }
273 } else {
274 for (i, part) in split_unescaped_commas(inner_str)
275 .into_iter()
276 .enumerate()
277 {
278 let (key, value) = part
279 .split_once('=')
280 .ok_or_else(|| {
281 OriginateError::ParseError(format!("missing = in variable {i}"))
282 })?;
283 inner.insert(key.to_string(), unescape_value(value));
284 }
285 }
286 }
287
288 Ok(Self { vars_type, inner })
289 }
290}
291
292pub(super) fn split_unescaped_commas(s: &str) -> Vec<&str> {
298 let mut parts = Vec::new();
299 let mut start = 0;
300 let bytes = s.as_bytes();
301
302 for i in 0..bytes.len() {
303 if bytes[i] == b',' {
304 let mut backslashes = 0;
305 let mut j = i;
306 while j > 0 && bytes[j - 1] == b'\\' {
307 backslashes += 1;
308 j -= 1;
309 }
310 if backslashes % 2 == 0 {
311 parts.push(&s[start..i]);
312 start = i + 1;
313 }
314 }
315 }
316 parts.push(&s[start..]);
317 parts
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
327 fn missing_equals_error_omits_the_fragment() {
328 let msg = "{origination_caller_id_number=15551234567,15550009999}"
329 .parse::<Variables>()
330 .unwrap_err()
331 .to_string();
332 assert!(
333 !msg.contains("15550009999"),
334 "error quoted its input: {msg}"
335 );
336 assert!(
337 msg.contains("variable 1"),
338 "error does not name the part: {msg}"
339 );
340 }
341
342 #[test]
343 fn unknown_delimiters_error_omits_the_block() {
344 let msg = "(origination_caller_id_number=15551234567)"
345 .parse::<Variables>()
346 .unwrap_err()
347 .to_string();
348 assert!(
349 !msg.contains("15551234567"),
350 "error quoted its input: {msg}"
351 );
352 }
353
354 #[test]
355 fn variables_standard_chars() {
356 let mut vars = Variables::new(VariablesType::Default);
357 vars.insert("test_key", "this_value");
358 let result = vars.to_string();
359 assert!(result.contains("test_key"));
360 assert!(result.contains("this_value"));
361 }
362
363 #[test]
364 fn variables_comma_escaped() {
365 let mut vars = Variables::new(VariablesType::Default);
366 vars.insert("test_key", "this,is,a,value");
367 let result = vars.to_string();
368 assert!(result.contains("\\,"));
369 }
370
371 #[test]
372 fn variables_spaces_quoted() {
373 let mut vars = Variables::new(VariablesType::Default);
374 vars.insert("test_key", "this is a value");
375 let result = vars.to_string();
376 assert_eq!(
377 result
378 .matches('\'')
379 .count(),
380 2
381 );
382 }
383
384 #[test]
385 fn variables_single_quote_escaped() {
386 let mut vars = Variables::new(VariablesType::Default);
387 vars.insert("test_key", "let's_this_be_a_value");
388 let result = vars.to_string();
389 assert!(result.contains("\\'"));
390 }
391
392 #[test]
393 fn variables_enterprise_delimiters() {
394 let mut vars = Variables::new(VariablesType::Enterprise);
395 vars.insert("k", "v");
396 let result = vars.to_string();
397 assert!(result.starts_with('<'));
398 assert!(result.ends_with('>'));
399 }
400
401 #[test]
402 fn variables_channel_delimiters() {
403 let mut vars = Variables::new(VariablesType::Channel);
404 vars.insert("k", "v");
405 let result = vars.to_string();
406 assert!(result.starts_with('['));
407 assert!(result.ends_with(']'));
408 }
409
410 #[test]
411 fn variables_default_delimiters() {
412 let mut vars = Variables::new(VariablesType::Default);
413 vars.insert("k", "v");
414 let result = vars.to_string();
415 assert!(result.starts_with('{'));
416 assert!(result.ends_with('}'));
417 }
418
419 #[test]
420 fn variables_parse_round_trip() {
421 let mut vars = Variables::new(VariablesType::Default);
422 vars.insert("origination_caller_id_number", "9005551212");
423 vars.insert("sip_h_Call-Info", "<url>;meta=123,<uri>");
424 let s = vars.to_string();
425 let parsed: Variables = s
426 .parse()
427 .unwrap();
428 assert_eq!(
429 parsed.get("origination_caller_id_number"),
430 Some("9005551212")
431 );
432 assert_eq!(parsed.get("sip_h_Call-Info"), Some("<url>;meta=123,<uri>"));
433 }
434
435 #[test]
436 fn split_unescaped_commas_basic() {
437 assert_eq!(split_unescaped_commas("a,b,c"), vec!["a", "b", "c"]);
438 }
439
440 #[test]
441 fn split_unescaped_commas_escaped() {
442 assert_eq!(split_unescaped_commas(r"a\,b,c"), vec![r"a\,b", "c"]);
443 }
444
445 #[test]
446 fn split_unescaped_commas_double_backslash() {
447 assert_eq!(split_unescaped_commas(r"a\\,b"), vec![r"a\\", "b"]);
449 }
450
451 #[test]
452 fn split_unescaped_commas_triple_backslash() {
453 assert_eq!(split_unescaped_commas(r"a\\\,b"), vec![r"a\\\,b"]);
455 }
456
457 #[test]
458 fn variables_caret_caret_separator() {
459 let vars: Variables =
460 "[^^:sip_invite_domain=pbx.example.com:presence_id=1211@pbx.example.com]"
461 .parse()
462 .unwrap();
463 assert_eq!(vars.scope(), VariablesType::Channel);
464 assert_eq!(vars.get("sip_invite_domain"), Some("pbx.example.com"));
465 assert_eq!(vars.get("presence_id"), Some("1211@pbx.example.com"));
466 }
467
468 #[test]
469 fn variables_caret_caret_display_uses_canonical_comma() {
470 let vars: Variables = "[^^:a=1:b=2]"
471 .parse()
472 .unwrap();
473 assert_eq!(vars.to_string(), "[a=1,b=2]");
474 }
475
476 #[test]
477 fn variables_caret_caret_default_scope() {
478 let vars: Variables = "{^^|x=1|y=2}"
479 .parse()
480 .unwrap();
481 assert_eq!(vars.scope(), VariablesType::Default);
482 assert_eq!(vars.get("x"), Some("1"));
483 assert_eq!(vars.get("y"), Some("2"));
484 }
485
486 #[test]
487 fn variables_caret_caret_enterprise_scope() {
488 let vars: Variables = "<^^;a=1;b=2>"
489 .parse()
490 .unwrap();
491 assert_eq!(vars.scope(), VariablesType::Enterprise);
492 assert_eq!(vars.get("a"), Some("1"));
493 }
494
495 #[test]
496 fn variables_caret_caret_no_unescape() {
497 let vars: Variables = r"[^^:key=val\,ue:other=x]"
498 .parse()
499 .unwrap();
500 assert_eq!(vars.get("key"), Some(r"val\,ue"));
501 }
502
503 #[test]
504 fn variables_caret_caret_values_with_commas() {
505 let vars: Variables = "[^^|sip_h_X-Call-Info=<urn:foo>;purpose=bar,<urn:baz>|other=val]"
506 .parse()
507 .unwrap();
508 assert_eq!(
509 vars.get("sip_h_X-Call-Info"),
510 Some("<urn:foo>;purpose=bar,<urn:baz>")
511 );
512 assert_eq!(vars.get("other"), Some("val"));
513 }
514
515 #[test]
516 fn variables_caret_caret_empty_vars() {
517 let vars: Variables = "[^^:]"
518 .parse()
519 .unwrap();
520 assert!(vars.is_empty());
521 assert_eq!(vars.scope(), VariablesType::Channel);
522 }
523
524 #[test]
525 fn variables_caret_caret_missing_separator() {
526 assert!("[^^]"
527 .parse::<Variables>()
528 .is_err());
529 }
530
531 #[test]
532 fn variables_caret_caret_closing_bracket_as_sep() {
533 assert!("[^^]]"
534 .parse::<Variables>()
535 .is_err());
536 }
537
538 #[test]
539 fn variables_caret_caret_equals_as_sep() {
540 assert!("[^^=a=1]"
541 .parse::<Variables>()
542 .is_err());
543 }
544
545 #[test]
546 fn variables_from_str_empty_block() {
547 let result = "{}".parse::<Variables>();
548 assert!(
549 result.is_ok(),
550 "empty variable block should parse successfully"
551 );
552 let vars = result.unwrap();
553 assert!(
554 vars.is_empty(),
555 "parsed empty block should have no variables"
556 );
557 }
558
559 #[test]
560 fn variables_from_str_empty_channel_block() {
561 let result = "[]".parse::<Variables>();
562 assert!(result.is_ok());
563 let vars = result.unwrap();
564 assert!(vars.is_empty());
565 assert_eq!(vars.scope(), VariablesType::Channel);
566 }
567
568 #[test]
569 fn variables_from_str_empty_enterprise_block() {
570 let result = "<>".parse::<Variables>();
571 assert!(result.is_ok());
572 let vars = result.unwrap();
573 assert!(vars.is_empty());
574 assert_eq!(vars.scope(), VariablesType::Enterprise);
575 }
576
577 #[test]
578 fn serde_variables_type() {
579 let json = serde_json::to_string(&VariablesType::Enterprise).unwrap();
580 assert_eq!(json, "\"enterprise\"");
581 let parsed: VariablesType = serde_json::from_str(&json).unwrap();
582 assert_eq!(parsed, VariablesType::Enterprise);
583 }
584
585 #[test]
586 fn serde_variables_flat_default() {
587 let mut vars = Variables::new(VariablesType::Default);
588 vars.insert("key1", "val1");
589 vars.insert("key2", "val2");
590 let json = serde_json::to_string(&vars).unwrap();
591 let parsed: Variables = serde_json::from_str(&json).unwrap();
592 assert_eq!(parsed.scope(), VariablesType::Default);
593 assert_eq!(parsed.get("key1"), Some("val1"));
594 assert_eq!(parsed.get("key2"), Some("val2"));
595 }
596
597 #[test]
598 fn serde_variables_scoped_enterprise() {
599 let mut vars = Variables::new(VariablesType::Enterprise);
600 vars.insert("key1", "val1");
601 let json = serde_json::to_string(&vars).unwrap();
602 assert!(json.contains("\"enterprise\""));
603 let parsed: Variables = serde_json::from_str(&json).unwrap();
604 assert_eq!(parsed.scope(), VariablesType::Enterprise);
605 assert_eq!(parsed.get("key1"), Some("val1"));
606 }
607
608 #[test]
609 fn serde_variables_flat_map_deserializes_as_default() {
610 let json = r#"{"key1":"val1","key2":"val2"}"#;
611 let vars: Variables = serde_json::from_str(json).unwrap();
612 assert_eq!(vars.scope(), VariablesType::Default);
613 assert_eq!(vars.get("key1"), Some("val1"));
614 assert_eq!(vars.get("key2"), Some("val2"));
615 }
616
617 #[test]
618 fn serde_variables_scoped_deserializes() {
619 let json = r#"{"scope":"channel","vars":{"k":"v"}}"#;
620 let vars: Variables = serde_json::from_str(json).unwrap();
621 assert_eq!(vars.scope(), VariablesType::Channel);
622 assert_eq!(vars.get("k"), Some("v"));
623 }
624}