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 separator: Option<char>,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70#[non_exhaustive]
71pub enum DialStringCarrier {
72 EslApi,
75 Dialplan,
78}
79
80const BACKSLASH: &str = r"\\\\\\\\";
82const QUOTE_DIALPLAN: &str = r"\\'";
84const QUOTE_ESL_API: &str = r"\\\'";
85
86impl DialStringCarrier {
87 fn quote_escape(self) -> &'static str {
88 match self {
89 Self::EslApi => QUOTE_ESL_API,
90 Self::Dialplan => QUOTE_DIALPLAN,
91 }
92 }
93}
94
95fn check_representable(
103 key: &str,
104 value: &str,
105 vars_type: VariablesType,
106) -> Result<(), OriginateError> {
107 if value.is_empty() {
108 return Err(OriginateError::ParseError(format!(
109 "variable {key} has an empty value: the switch discards such a pair \
110 without logging it, so it cannot be told from an absent variable on \
111 the wire. Give it a value or remove it -- and if its presence was \
112 itself the signal, that signal needs a home outside the dial string"
113 )));
114 }
115
116 let (open, close) = vars_type.delimiters();
117 let mut depth = 0i32;
118 for ch in value.chars() {
119 if ch == open {
120 depth += 1;
121 } else if ch == close {
122 depth -= 1;
123 if depth < 0 {
124 return Err(OriginateError::ParseError(format!(
125 "variable {key} closes a '{open}' it never opened, ending the block early"
126 )));
127 }
128 }
129 }
130 if depth != 0 {
131 return Err(OriginateError::ParseError(format!(
132 "variable {key} opens a '{open}' it never closes, swallowing the block's end"
133 )));
134 }
135 Ok(())
136}
137
138fn check_separator(sep: char, vars_type: VariablesType) -> Result<(), OriginateError> {
143 let (open, close) = vars_type.delimiters();
144 if sep == open || sep == close || sep == '=' || sep == '^' {
145 return Err(OriginateError::ParseError(format!(
146 "invalid ^^ separator: '{sep}'"
147 )));
148 }
149 Ok(())
150}
151
152fn escape_value(value: &str, carrier: DialStringCarrier, commas_separate: bool) -> String {
156 let escaped = value
159 .replace('\\', BACKSLASH)
160 .replace('\'', carrier.quote_escape());
161 let escaped = if commas_separate {
162 escaped.replace(',', "\\,")
163 } else {
164 escaped
165 };
166 if escaped.contains(' ') {
167 format!("'{}'", escaped)
168 } else {
169 escaped
170 }
171}
172
173fn unescape_value(value: &str, carrier: DialStringCarrier, commas_separate: bool) -> String {
177 let s = value
178 .strip_prefix('\'')
179 .and_then(|s| s.strip_suffix('\''))
180 .unwrap_or(value);
181
182 let s = if commas_separate {
183 s.replace("\\,", ",")
184 } else {
185 s.to_string()
186 };
187 s.replace(carrier.quote_escape(), "'")
188 .replace(BACKSLASH, "\\")
189}
190
191impl Variables {
192 pub fn new(vars_type: VariablesType) -> Self {
194 Self {
195 vars_type,
196 inner: IndexMap::new(),
197 separator: None,
198 }
199 }
200
201 pub fn with_vars(
203 vars_type: VariablesType,
204 vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
205 ) -> Self {
206 Self {
207 vars_type,
208 inner: vars
209 .into_iter()
210 .map(|(k, v)| (k.into(), v.into()))
211 .collect(),
212 separator: None,
213 }
214 }
215
216 pub fn with_separator(mut self, sep: char) -> Result<Self, OriginateError> {
228 check_separator(sep, self.vars_type)?;
229 if let Some((key, _)) = self
230 .inner
231 .iter()
232 .find(|(_, v)| v.contains(sep))
233 {
234 return Err(OriginateError::ParseError(format!(
235 "variable {key} contains the chosen '{sep}' separator"
236 )));
237 }
238 self.separator = Some(sep);
239 Ok(self)
240 }
241
242 pub fn separator(&self) -> Option<char> {
244 self.separator
245 }
246
247 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) {
249 self.inner
250 .insert(key.into(), value.into());
251 }
252
253 pub fn remove(&mut self, key: &str) -> Option<String> {
255 self.inner
256 .shift_remove(key)
257 }
258
259 pub fn get(&self, key: &str) -> Option<&str> {
261 self.inner
262 .get(key)
263 .map(|s| s.as_str())
264 }
265
266 pub fn is_empty(&self) -> bool {
268 self.inner
269 .is_empty()
270 }
271
272 pub fn len(&self) -> usize {
274 self.inner
275 .len()
276 }
277
278 pub fn scope(&self) -> VariablesType {
280 self.vars_type
281 }
282
283 pub fn set_scope(&mut self, scope: VariablesType) {
285 self.vars_type = scope;
286 }
287
288 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
290 self.inner
291 .iter()
292 .map(|(k, v)| (k.as_str(), v.as_str()))
293 }
294
295 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut String)> {
297 self.inner
298 .iter_mut()
299 .map(|(k, v)| (k.as_str(), v))
300 }
301
302 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut String> {
304 self.inner
305 .values_mut()
306 }
307}
308
309#[cfg(feature = "serde")]
310impl serde::Serialize for Variables {
311 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
312 if self.vars_type == VariablesType::Default {
313 self.inner
314 .serialize(serializer)
315 } else {
316 use serde::ser::SerializeStruct;
317 let mut s = serializer.serialize_struct("Variables", 2)?;
318 s.serialize_field("scope", &self.vars_type)?;
319 s.serialize_field("vars", &self.inner)?;
320 s.end()
321 }
322 }
323}
324
325#[cfg(feature = "serde")]
326impl<'de> serde::Deserialize<'de> for Variables {
327 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
328 #[derive(serde::Deserialize)]
329 #[serde(untagged)]
330 enum VariablesRepr {
331 Scoped {
332 scope: VariablesType,
333 vars: IndexMap<String, String>,
334 },
335 Flat(IndexMap<String, String>),
336 }
337
338 let (vars_type, inner) = match VariablesRepr::deserialize(deserializer)? {
339 VariablesRepr::Scoped { scope, vars } => (scope, vars),
340 VariablesRepr::Flat(map) => (VariablesType::Default, map),
341 };
342 for (key, value) in &inner {
345 check_representable(key, value, vars_type).map_err(serde::de::Error::custom)?;
346 }
347 Ok(Self {
348 vars_type,
349 inner,
350 separator: None,
351 })
352 }
353}
354
355#[derive(Debug, Clone, Copy)]
358pub struct VariablesDisplay<'a> {
359 vars: &'a Variables,
360 carrier: DialStringCarrier,
361}
362
363impl fmt::Display for VariablesDisplay<'_> {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 self.vars
366 .write_for(f, self.carrier)
367 }
368}
369
370impl Variables {
371 pub fn display_for(&self, carrier: DialStringCarrier) -> VariablesDisplay<'_> {
374 VariablesDisplay {
375 vars: self,
376 carrier,
377 }
378 }
379
380 pub(super) fn write_for(
381 &self,
382 f: &mut fmt::Formatter<'_>,
383 carrier: DialStringCarrier,
384 ) -> fmt::Result {
385 let (open, close) = self
386 .vars_type
387 .delimiters();
388 f.write_fmt(format_args!("{}", open))?;
389 if let Some(sep) = self.separator {
390 write!(f, "^^{sep}")?;
391 }
392 let commas_separate = self
395 .separator
396 .is_none();
397 let sep = self
398 .separator
399 .unwrap_or(',');
400 for (i, (key, value)) in self
401 .inner
402 .iter()
403 .enumerate()
404 {
405 if i > 0 {
406 write!(f, "{sep}")?;
407 }
408 let value = escape_value(value, carrier, commas_separate);
409 write!(f, "{}={}", key, value)?;
410 }
411 f.write_fmt(format_args!("{}", close))
412 }
413}
414
415impl fmt::Display for Variables {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 self.write_for(f, DialStringCarrier::EslApi)
418 }
419}
420
421impl FromStr for Variables {
422 type Err = OriginateError;
423
424 fn from_str(s: &str) -> Result<Self, Self::Err> {
425 Self::parse_for(s, DialStringCarrier::EslApi)
426 }
427}
428
429impl Variables {
430 pub fn parse_for(s: &str, carrier: DialStringCarrier) -> Result<Self, OriginateError> {
435 let s = s.trim();
436 if s.len() < 2 {
437 return Err(OriginateError::ParseError(
438 "variable block too short".into(),
439 ));
440 }
441
442 let (vars_type, inner_str) = match (s.as_bytes()[0], s.as_bytes()[s.len() - 1]) {
443 (b'{', b'}') => (VariablesType::Default, &s[1..s.len() - 1]),
444 (b'<', b'>') => (VariablesType::Enterprise, &s[1..s.len() - 1]),
445 (b'[', b']') => (VariablesType::Channel, &s[1..s.len() - 1]),
446 (open, close) => {
447 return Err(OriginateError::ParseError(format!(
448 "unknown variable delimiters: {:?}..{:?}",
449 open as char, close as char
450 )));
451 }
452 };
453
454 let (sep, var_str) = match inner_str.strip_prefix("^^") {
455 Some(rest) => {
456 let sep = rest
457 .chars()
458 .next()
459 .ok_or_else(|| {
460 OriginateError::ParseError("^^ without separator character".into())
461 })?;
462 check_separator(sep, vars_type)?;
463 (sep, &rest[sep.len_utf8()..])
464 }
465 None => (',', inner_str),
466 };
467 let commas_separate = sep == ',';
468
469 let mut inner = IndexMap::new();
470 if !var_str.is_empty() {
471 for (i, part) in split_unescaped(var_str, sep)
472 .into_iter()
473 .enumerate()
474 {
475 let (key, value) = part
476 .split_once('=')
477 .ok_or_else(|| {
478 OriginateError::ParseError(format!("missing = in variable {i}"))
479 })?;
480 let value = unescape_value(value, carrier, commas_separate);
481 check_representable(key, &value, vars_type)?;
482 inner.insert(key.to_string(), value);
483 }
484 }
485
486 Ok(Self {
487 vars_type,
488 inner,
489 separator: None,
490 })
491 }
492}
493
494fn split_unescaped(s: &str, sep: char) -> Vec<&str> {
500 let mut parts = Vec::new();
501 let mut start = 0;
502 let bytes = s.as_bytes();
503
504 for (i, ch) in s.char_indices() {
505 if ch == sep {
506 let mut backslashes = 0;
507 let mut j = i;
508 while j > 0 && bytes[j - 1] == b'\\' {
509 backslashes += 1;
510 j -= 1;
511 }
512 if backslashes % 2 == 0 {
513 parts.push(&s[start..i]);
514 start = i + ch.len_utf8();
515 }
516 }
517 }
518 parts.push(&s[start..]);
519 parts
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525
526 #[test]
534 fn an_empty_value_is_refused_at_the_boundary() {
535 let err = "{k=,after=sentinel}"
536 .parse::<Variables>()
537 .unwrap_err()
538 .to_string();
539 assert!(err.contains('k'), "error does not name the variable: {err}");
540 assert!(
541 err.contains("remove it"),
542 "error does not name a remedy: {err}"
543 );
544 assert!(
547 err.contains("presence"),
548 "error prescribes a fix without allowing that presence meant something: {err}"
549 );
550 }
551
552 #[test]
556 fn an_unbalanced_bracket_is_refused_while_a_balanced_one_is_not() {
557 assert!("{k=oops}extra}"
558 .parse::<Variables>()
559 .is_err());
560
561 let balanced: Variables = "{k=${some_var}}"
562 .parse()
563 .expect("a balanced ${...} is ordinary and must parse");
564 assert_eq!(balanced.get("k"), Some("${some_var}"));
565 }
566
567 #[test]
571 fn chosen_separator_leaves_commas_alone() {
572 let mut vars = Variables::new(VariablesType::Default);
573 vars.insert("codecs", "PCMA,PCMU,G729");
574 let vars = vars
575 .with_separator(':')
576 .unwrap();
577
578 assert_eq!(vars.to_string(), "{^^:codecs=PCMA,PCMU,G729}");
579 assert_eq!(vars.separator(), Some(':'));
580 }
581
582 #[test]
583 fn separator_that_cannot_delimit_the_block_is_refused() {
584 let vars = Variables::new(VariablesType::Channel);
585 for sep in ['[', ']', '=', '^'] {
586 assert!(
587 vars.clone()
588 .with_separator(sep)
589 .is_err(),
590 "accepted {sep:?}"
591 );
592 }
593 }
594
595 #[test]
599 fn the_parser_refuses_every_separator_the_builder_does() {
600 for sep in ['[', ']', '=', '^'] {
601 assert!(
602 format!("[^^{sep}a=1{sep}b=2]")
603 .parse::<Variables>()
604 .is_err(),
605 "parser accepted {sep:?}"
606 );
607 }
608 }
609
610 #[test]
613 fn separator_already_present_in_a_value_is_refused() {
614 let mut vars = Variables::new(VariablesType::Default);
615 vars.insert("uri", "sip:bob@example.com");
616 let err = vars
617 .with_separator(':')
618 .unwrap_err()
619 .to_string();
620 assert!(
621 err.contains("uri"),
622 "error does not name the variable: {err}"
623 );
624 }
625
626 #[test]
631 fn escaping_pins_the_measured_wire_forms() {
632 let cases = [
633 (DialStringCarrier::Dialplan, "it's", r"it\\'s"),
634 (DialStringCarrier::EslApi, "it's", r"it\\\'s"),
635 (DialStringCarrier::Dialplan, r"a\nb", r"a\\\\\\\\nb"),
636 (DialStringCarrier::EslApi, r"a\nb", r"a\\\\\\\\nb"),
637 (DialStringCarrier::Dialplan, "a,b", r"a\,b"),
638 (DialStringCarrier::EslApi, "a,b", r"a\,b"),
639 ];
640 for (carrier, value, want) in cases {
641 assert_eq!(
642 escape_value(value, carrier, true),
643 want,
644 "{value:?} for {carrier:?}"
645 );
646 }
647 }
648
649 #[test]
653 fn a_chosen_separator_changes_only_the_comma() {
654 let cases = [
655 (DialStringCarrier::Dialplan, "it's", r"it\\'s"),
656 (DialStringCarrier::EslApi, "it's", r"it\\\'s"),
657 (DialStringCarrier::Dialplan, r"a\nb", r"a\\\\\\\\nb"),
658 (DialStringCarrier::EslApi, r"a\nb", r"a\\\\\\\\nb"),
659 (DialStringCarrier::Dialplan, "a,b", "a,b"),
660 (DialStringCarrier::EslApi, "a,b", "a,b"),
661 ];
662 for (carrier, value, want) in cases {
663 assert_eq!(
664 escape_value(value, carrier, false),
665 want,
666 "{value:?} for {carrier:?}"
667 );
668 }
669 }
670
671 #[test]
674 fn display_defaults_to_the_api_carrier() {
675 let mut vars = Variables::new(VariablesType::Default);
676 vars.insert("k", "it's");
677 assert_eq!(vars.to_string(), r"{k=it\\\'s}");
678 assert_eq!(
679 vars.display_for(DialStringCarrier::Dialplan)
680 .to_string(),
681 r"{k=it\\'s}"
682 );
683 }
684
685 #[test]
686 fn round_trips_at_either_carrier() {
687 for carrier in [DialStringCarrier::EslApi, DialStringCarrier::Dialplan] {
688 for value in ["it's", r"a\,b", r"C:\path", "a,b", "with space"] {
689 let mut vars = Variables::new(VariablesType::Default);
690 vars.insert("k", value);
691 vars.insert("after", "sentinel");
692 let rendered = vars
693 .display_for(carrier)
694 .to_string();
695
696 let back = Variables::parse_for(&rendered, carrier).unwrap_or_else(|e| {
697 panic!("{value:?} for {carrier:?} rendered {rendered}: {e}")
698 });
699 assert_eq!(back.get("k"), Some(value), "rendered {rendered}");
700 assert_eq!(back.get("after"), Some("sentinel"), "rendered {rendered}");
701 }
702 }
703 }
704
705 #[test]
709 fn separated_block_round_trips_at_either_carrier() {
710 for carrier in [DialStringCarrier::EslApi, DialStringCarrier::Dialplan] {
711 for value in ["it's", "a,b", r"C:\path", r"a\nb", "with space"] {
712 let mut vars = Variables::new(VariablesType::Default);
713 vars.insert("k", value);
714 vars.insert("after", "sentinel");
715 let vars = vars
716 .with_separator('~')
717 .expect("'~' appears in none of these values");
718 let rendered = vars
719 .display_for(carrier)
720 .to_string();
721
722 let back = Variables::parse_for(&rendered, carrier).unwrap_or_else(|e| {
723 panic!("{value:?} for {carrier:?} rendered {rendered}: {e}")
724 });
725 assert_eq!(back.get("k"), Some(value), "rendered {rendered}");
726 assert_eq!(back.get("after"), Some("sentinel"), "rendered {rendered}");
727 }
728 }
729 }
730
731 #[test]
736 fn value_with_backslash_round_trips() {
737 for value in [r"a\,b", r"C:\path", r"trailing\", r"\\", r"a\nb"] {
738 let mut vars = Variables::new(VariablesType::Default);
739 vars.insert("k", value);
740 vars.insert("after", "sentinel");
741 let rendered = vars.to_string();
742
743 let back: Variables = rendered
744 .parse()
745 .unwrap_or_else(|e| {
746 panic!("{value:?} rendered {rendered} and failed to parse: {e}")
747 });
748 assert_eq!(back.get("k"), Some(value), "rendered {rendered}");
749 assert_eq!(
750 back.get("after"),
751 Some("sentinel"),
752 "value {value:?} ate the next variable: {rendered}"
753 );
754 }
755 }
756
757 #[test]
760 fn missing_equals_error_omits_the_fragment() {
761 let msg = "{origination_caller_id_number=15551234567,15550009999}"
762 .parse::<Variables>()
763 .unwrap_err()
764 .to_string();
765 assert!(
766 !msg.contains("15550009999"),
767 "error quoted its input: {msg}"
768 );
769 assert!(
770 msg.contains("variable 1"),
771 "error does not name the part: {msg}"
772 );
773 }
774
775 #[test]
776 fn unknown_delimiters_error_omits_the_block() {
777 let msg = "(origination_caller_id_number=15551234567)"
778 .parse::<Variables>()
779 .unwrap_err()
780 .to_string();
781 assert!(
782 !msg.contains("15551234567"),
783 "error quoted its input: {msg}"
784 );
785 }
786
787 #[test]
788 fn variables_standard_chars() {
789 let mut vars = Variables::new(VariablesType::Default);
790 vars.insert("test_key", "this_value");
791 let result = vars.to_string();
792 assert!(result.contains("test_key"));
793 assert!(result.contains("this_value"));
794 }
795
796 #[test]
797 fn variables_comma_escaped() {
798 let mut vars = Variables::new(VariablesType::Default);
799 vars.insert("test_key", "this,is,a,value");
800 let result = vars.to_string();
801 assert!(result.contains("\\,"));
802 }
803
804 #[test]
805 fn variables_spaces_quoted() {
806 let mut vars = Variables::new(VariablesType::Default);
807 vars.insert("test_key", "this is a value");
808 let result = vars.to_string();
809 assert_eq!(
810 result
811 .matches('\'')
812 .count(),
813 2
814 );
815 }
816
817 #[test]
818 fn variables_single_quote_escaped() {
819 let mut vars = Variables::new(VariablesType::Default);
820 vars.insert("test_key", "let's_this_be_a_value");
821 let result = vars.to_string();
822 assert!(result.contains("\\'"));
823 }
824
825 #[test]
826 fn variables_enterprise_delimiters() {
827 let mut vars = Variables::new(VariablesType::Enterprise);
828 vars.insert("k", "v");
829 let result = vars.to_string();
830 assert!(result.starts_with('<'));
831 assert!(result.ends_with('>'));
832 }
833
834 #[test]
835 fn variables_channel_delimiters() {
836 let mut vars = Variables::new(VariablesType::Channel);
837 vars.insert("k", "v");
838 let result = vars.to_string();
839 assert!(result.starts_with('['));
840 assert!(result.ends_with(']'));
841 }
842
843 #[test]
844 fn variables_default_delimiters() {
845 let mut vars = Variables::new(VariablesType::Default);
846 vars.insert("k", "v");
847 let result = vars.to_string();
848 assert!(result.starts_with('{'));
849 assert!(result.ends_with('}'));
850 }
851
852 #[test]
853 fn variables_parse_round_trip() {
854 let mut vars = Variables::new(VariablesType::Default);
855 vars.insert("origination_caller_id_number", "9005551212");
856 vars.insert("sip_h_Call-Info", "<url>;meta=123,<uri>");
857 let s = vars.to_string();
858 let parsed: Variables = s
859 .parse()
860 .unwrap();
861 assert_eq!(
862 parsed.get("origination_caller_id_number"),
863 Some("9005551212")
864 );
865 assert_eq!(parsed.get("sip_h_Call-Info"), Some("<url>;meta=123,<uri>"));
866 }
867
868 #[test]
869 fn split_unescaped_basic() {
870 assert_eq!(split_unescaped("a,b,c", ','), vec!["a", "b", "c"]);
871 assert_eq!(split_unescaped("a~b~c", '~'), vec!["a", "b", "c"]);
872 }
873
874 #[test]
875 fn split_unescaped_escaped() {
876 assert_eq!(split_unescaped(r"a\,b,c", ','), vec![r"a\,b", "c"]);
877 }
878
879 #[test]
880 fn split_unescaped_double_backslash() {
881 assert_eq!(split_unescaped(r"a\\,b", ','), vec![r"a\\", "b"]);
883 }
884
885 #[test]
886 fn split_unescaped_triple_backslash() {
887 assert_eq!(split_unescaped(r"a\\\,b", ','), vec![r"a\\\,b"]);
889 }
890
891 #[test]
892 fn variables_caret_caret_separator() {
893 let vars: Variables =
894 "[^^:sip_invite_domain=pbx.example.com:presence_id=1211@pbx.example.com]"
895 .parse()
896 .unwrap();
897 assert_eq!(vars.scope(), VariablesType::Channel);
898 assert_eq!(vars.get("sip_invite_domain"), Some("pbx.example.com"));
899 assert_eq!(vars.get("presence_id"), Some("1211@pbx.example.com"));
900 }
901
902 #[test]
903 fn variables_caret_caret_display_uses_canonical_comma() {
904 let vars: Variables = "[^^:a=1:b=2]"
905 .parse()
906 .unwrap();
907 assert_eq!(vars.to_string(), "[a=1,b=2]");
908 }
909
910 #[test]
911 fn variables_caret_caret_default_scope() {
912 let vars: Variables = "{^^|x=1|y=2}"
913 .parse()
914 .unwrap();
915 assert_eq!(vars.scope(), VariablesType::Default);
916 assert_eq!(vars.get("x"), Some("1"));
917 assert_eq!(vars.get("y"), Some("2"));
918 }
919
920 #[test]
921 fn variables_caret_caret_enterprise_scope() {
922 let vars: Variables = "<^^;a=1;b=2>"
923 .parse()
924 .unwrap();
925 assert_eq!(vars.scope(), VariablesType::Enterprise);
926 assert_eq!(vars.get("a"), Some("1"));
927 }
928
929 #[test]
933 fn variables_caret_caret_keeps_an_escaped_comma_literal() {
934 let vars: Variables = r"[^^:key=val\,ue:other=x]"
935 .parse()
936 .unwrap();
937 assert_eq!(vars.get("key"), Some(r"val\,ue"));
938 }
939
940 #[test]
941 fn variables_caret_caret_values_with_commas() {
942 let vars: Variables = "[^^|sip_h_X-Call-Info=<urn:foo>;purpose=bar,<urn:baz>|other=val]"
943 .parse()
944 .unwrap();
945 assert_eq!(
946 vars.get("sip_h_X-Call-Info"),
947 Some("<urn:foo>;purpose=bar,<urn:baz>")
948 );
949 assert_eq!(vars.get("other"), Some("val"));
950 }
951
952 #[test]
953 fn variables_caret_caret_empty_vars() {
954 let vars: Variables = "[^^:]"
955 .parse()
956 .unwrap();
957 assert!(vars.is_empty());
958 assert_eq!(vars.scope(), VariablesType::Channel);
959 }
960
961 #[test]
962 fn variables_caret_caret_missing_separator() {
963 assert!("[^^]"
964 .parse::<Variables>()
965 .is_err());
966 }
967
968 #[test]
969 fn variables_caret_caret_closing_bracket_as_sep() {
970 assert!("[^^]]"
971 .parse::<Variables>()
972 .is_err());
973 }
974
975 #[test]
976 fn variables_caret_caret_equals_as_sep() {
977 assert!("[^^=a=1]"
978 .parse::<Variables>()
979 .is_err());
980 }
981
982 #[test]
983 fn variables_from_str_empty_block() {
984 let result = "{}".parse::<Variables>();
985 assert!(
986 result.is_ok(),
987 "empty variable block should parse successfully"
988 );
989 let vars = result.unwrap();
990 assert!(
991 vars.is_empty(),
992 "parsed empty block should have no variables"
993 );
994 }
995
996 #[test]
997 fn variables_from_str_empty_channel_block() {
998 let result = "[]".parse::<Variables>();
999 assert!(result.is_ok());
1000 let vars = result.unwrap();
1001 assert!(vars.is_empty());
1002 assert_eq!(vars.scope(), VariablesType::Channel);
1003 }
1004
1005 #[test]
1006 fn variables_from_str_empty_enterprise_block() {
1007 let result = "<>".parse::<Variables>();
1008 assert!(result.is_ok());
1009 let vars = result.unwrap();
1010 assert!(vars.is_empty());
1011 assert_eq!(vars.scope(), VariablesType::Enterprise);
1012 }
1013
1014 #[test]
1015 fn serde_variables_type() {
1016 let json = serde_json::to_string(&VariablesType::Enterprise).unwrap();
1017 assert_eq!(json, "\"enterprise\"");
1018 let parsed: VariablesType = serde_json::from_str(&json).unwrap();
1019 assert_eq!(parsed, VariablesType::Enterprise);
1020 }
1021
1022 #[test]
1023 fn serde_variables_flat_default() {
1024 let mut vars = Variables::new(VariablesType::Default);
1025 vars.insert("key1", "val1");
1026 vars.insert("key2", "val2");
1027 let json = serde_json::to_string(&vars).unwrap();
1028 let parsed: Variables = serde_json::from_str(&json).unwrap();
1029 assert_eq!(parsed.scope(), VariablesType::Default);
1030 assert_eq!(parsed.get("key1"), Some("val1"));
1031 assert_eq!(parsed.get("key2"), Some("val2"));
1032 }
1033
1034 #[test]
1035 fn serde_variables_scoped_enterprise() {
1036 let mut vars = Variables::new(VariablesType::Enterprise);
1037 vars.insert("key1", "val1");
1038 let json = serde_json::to_string(&vars).unwrap();
1039 assert!(json.contains("\"enterprise\""));
1040 let parsed: Variables = serde_json::from_str(&json).unwrap();
1041 assert_eq!(parsed.scope(), VariablesType::Enterprise);
1042 assert_eq!(parsed.get("key1"), Some("val1"));
1043 }
1044
1045 #[test]
1046 fn serde_variables_flat_map_deserializes_as_default() {
1047 let json = r#"{"key1":"val1","key2":"val2"}"#;
1048 let vars: Variables = serde_json::from_str(json).unwrap();
1049 assert_eq!(vars.scope(), VariablesType::Default);
1050 assert_eq!(vars.get("key1"), Some("val1"));
1051 assert_eq!(vars.get("key2"), Some("val2"));
1052 }
1053
1054 #[test]
1055 fn serde_variables_scoped_deserializes() {
1056 let json = r#"{"scope":"channel","vars":{"k":"v"}}"#;
1057 let vars: Variables = serde_json::from_str(json).unwrap();
1058 assert_eq!(vars.scope(), VariablesType::Channel);
1059 assert_eq!(vars.get("k"), Some("v"));
1060 }
1061}