freeswitch_types/variables/
esl_array.rs1use std::fmt;
2
3const ARRAY_HEADER: &str = "ARRAY::";
4const ARRAY_SEPARATOR: &str = "|:";
5
6pub const MAX_ARRAY_ITEMS: usize = 4000;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum EslArrayError {
16 MissingPrefix,
18 TooManyItems {
20 count: usize,
22 max: usize,
24 },
25}
26
27impl fmt::Display for EslArrayError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::MissingPrefix => f.write_str("missing ARRAY:: prefix"),
31 Self::TooManyItems { count, max } => {
32 write!(f, "array has {count} items, maximum is {max}")
33 }
34 }
35 }
36}
37
38impl std::error::Error for EslArrayError {}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct EslArray(Vec<String>);
44
45impl EslArray {
46 pub fn parse(s: &str) -> Result<Self, EslArrayError> {
52 let body = s
53 .strip_prefix(ARRAY_HEADER)
54 .ok_or(EslArrayError::MissingPrefix)?;
55 let items: Vec<String> = body
56 .split(ARRAY_SEPARATOR)
57 .map(String::from)
58 .collect();
59 let count = items.len();
60 if count > MAX_ARRAY_ITEMS {
61 return Err(EslArrayError::TooManyItems {
62 count,
63 max: MAX_ARRAY_ITEMS,
64 });
65 }
66 Ok(Self(items))
67 }
68
69 pub fn new(items: Vec<String>) -> Self {
71 Self(items)
72 }
73
74 pub fn push(&mut self, value: String) {
76 self.0
77 .push(value);
78 }
79
80 pub fn unshift(&mut self, value: String) {
82 self.0
83 .insert(0, value);
84 }
85
86 pub fn items(&self) -> &[String] {
88 &self.0
89 }
90
91 pub fn len(&self) -> usize {
93 self.0
94 .len()
95 }
96
97 pub fn is_empty(&self) -> bool {
99 self.0
100 .is_empty()
101 }
102}
103
104impl<'a> IntoIterator for &'a EslArray {
105 type Item = &'a String;
106 type IntoIter = std::slice::Iter<'a, String>;
107
108 fn into_iter(self) -> Self::IntoIter {
109 self.0
110 .iter()
111 }
112}
113
114impl IntoIterator for EslArray {
115 type Item = String;
116 type IntoIter = std::vec::IntoIter<String>;
117
118 fn into_iter(self) -> Self::IntoIter {
119 self.0
120 .into_iter()
121 }
122}
123
124impl fmt::Display for EslArray {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 f.write_str(ARRAY_HEADER)?;
127 for (i, item) in self
128 .0
129 .iter()
130 .enumerate()
131 {
132 if i > 0 {
133 f.write_str(ARRAY_SEPARATOR)?;
134 }
135 f.write_str(item)?;
136 }
137 Ok(())
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn parse_single_item() {
147 let arr = EslArray::parse("ARRAY::hello").unwrap();
148 assert_eq!(arr.items(), &["hello"]);
149 assert_eq!(arr.len(), 1);
150 }
151
152 #[test]
153 fn parse_multiple_items() {
154 let arr = EslArray::parse("ARRAY::one|:two|:three").unwrap();
155 assert_eq!(arr.items(), &["one", "two", "three"]);
156 assert_eq!(arr.len(), 3);
157 }
158
159 #[test]
160 fn parse_non_array_returns_missing_prefix() {
161 assert!(matches!(
162 EslArray::parse("not an array"),
163 Err(EslArrayError::MissingPrefix)
164 ));
165 assert!(matches!(
166 EslArray::parse(""),
167 Err(EslArrayError::MissingPrefix)
168 ));
169 assert!(matches!(
170 EslArray::parse("ARRAY:"),
171 Err(EslArrayError::MissingPrefix)
172 ));
173 }
174
175 #[test]
176 fn parse_at_max_items_succeeds() {
177 let items: Vec<&str> = (0..MAX_ARRAY_ITEMS)
178 .map(|_| "x")
179 .collect();
180 let input = format!("ARRAY::{}", items.join("|:"));
181 let arr = EslArray::parse(&input).unwrap();
182 assert_eq!(arr.len(), MAX_ARRAY_ITEMS);
183 }
184
185 #[test]
186 fn parse_over_max_items_returns_error() {
187 let items: Vec<&str> = (0..=MAX_ARRAY_ITEMS)
188 .map(|_| "x")
189 .collect();
190 let input = format!("ARRAY::{}", items.join("|:"));
191 assert_eq!(
192 EslArray::parse(&input),
193 Err(EslArrayError::TooManyItems {
194 count: MAX_ARRAY_ITEMS + 1,
195 max: MAX_ARRAY_ITEMS,
196 })
197 );
198 }
199
200 #[test]
201 fn error_display_missing_prefix() {
202 assert_eq!(
203 EslArrayError::MissingPrefix.to_string(),
204 "missing ARRAY:: prefix"
205 );
206 }
207
208 #[test]
209 fn error_display_too_many_items() {
210 let err = EslArrayError::TooManyItems {
211 count: 5000,
212 max: 4000,
213 };
214 assert_eq!(err.to_string(), "array has 5000 items, maximum is 4000");
215 }
216
217 #[test]
218 fn display_round_trip() {
219 let input = "ARRAY::one|:two|:three";
220 let arr = EslArray::parse(input).unwrap();
221 assert_eq!(arr.to_string(), input);
222 }
223
224 #[test]
225 fn display_single_item() {
226 let arr = EslArray::parse("ARRAY::only").unwrap();
227 assert_eq!(arr.to_string(), "ARRAY::only");
228 }
229
230 #[test]
231 fn empty_items_in_array() {
232 let arr = EslArray::parse("ARRAY::|:|:stuff").unwrap();
233 assert_eq!(arr.items(), &["", "", "stuff"]);
234 }
235
236 #[test]
237 fn test_new() {
238 let arr = EslArray::new(vec!["a".into(), "b".into(), "c".into()]);
239 assert_eq!(arr.items(), &["a", "b", "c"]);
240 assert_eq!(arr.len(), 3);
241 }
242
243 #[test]
244 fn test_push() {
245 let mut arr = EslArray::new(vec!["first".into()]);
246 arr.push("second".into());
247 arr.push("third".into());
248 assert_eq!(arr.items(), &["first", "second", "third"]);
249 assert_eq!(arr.to_string(), "ARRAY::first|:second|:third");
250 }
251
252 #[test]
253 fn test_unshift() {
254 let mut arr = EslArray::new(vec!["last".into()]);
255 arr.unshift("middle".into());
256 arr.unshift("first".into());
257 assert_eq!(arr.items(), &["first", "middle", "last"]);
258 assert_eq!(arr.to_string(), "ARRAY::first|:middle|:last");
259 }
260
261 #[test]
262 fn parse_sip_uris_with_colons() {
263 let input = "ARRAY::sip:alice@atlanta.example.com|:sip:bob@biloxi.example.com";
264 let arr = EslArray::parse(input).unwrap();
265 assert_eq!(
266 arr.items(),
267 &[
268 "sip:alice@atlanta.example.com",
269 "sip:bob@biloxi.example.com"
270 ]
271 );
272 assert_eq!(arr.to_string(), input);
273 }
274
275 #[test]
276 fn parse_sip_uris_with_angle_brackets_and_params() {
277 let input = "ARRAY::<sip:+15551234567@gw.example.com;user=phone>|:<tel:+15559876543>";
278 let arr = EslArray::parse(input).unwrap();
279 assert_eq!(arr.len(), 2);
280 assert_eq!(
281 arr.items()[0],
282 "<sip:+15551234567@gw.example.com;user=phone>"
283 );
284 assert_eq!(arr.items()[1], "<tel:+15559876543>");
285 assert_eq!(arr.to_string(), input);
286 }
287}