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 const PREFIX: &'static str = ARRAY_HEADER;
48
49 pub fn parse(s: &str) -> Result<Self, EslArrayError> {
55 let body = s
56 .strip_prefix(ARRAY_HEADER)
57 .ok_or(EslArrayError::MissingPrefix)?;
58 let items: Vec<String> = body
59 .split(ARRAY_SEPARATOR)
60 .map(String::from)
61 .collect();
62 let count = items.len();
63 if count > MAX_ARRAY_ITEMS {
64 return Err(EslArrayError::TooManyItems {
65 count,
66 max: MAX_ARRAY_ITEMS,
67 });
68 }
69 Ok(Self(items))
70 }
71
72 pub fn new(items: Vec<String>) -> Self {
74 Self(items)
75 }
76
77 pub fn push(&mut self, value: String) {
79 self.0
80 .push(value);
81 }
82
83 pub fn unshift(&mut self, value: String) {
85 self.0
86 .insert(0, value);
87 }
88
89 pub fn items(&self) -> &[String] {
91 &self.0
92 }
93
94 pub fn len(&self) -> usize {
96 self.0
97 .len()
98 }
99
100 pub fn is_empty(&self) -> bool {
102 self.0
103 .is_empty()
104 }
105}
106
107impl<'a> IntoIterator for &'a EslArray {
108 type Item = &'a String;
109 type IntoIter = std::slice::Iter<'a, String>;
110
111 fn into_iter(self) -> Self::IntoIter {
112 self.0
113 .iter()
114 }
115}
116
117impl IntoIterator for EslArray {
118 type Item = String;
119 type IntoIter = std::vec::IntoIter<String>;
120
121 fn into_iter(self) -> Self::IntoIter {
122 self.0
123 .into_iter()
124 }
125}
126
127impl fmt::Display for EslArray {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_str(ARRAY_HEADER)?;
130 for (i, item) in self
131 .0
132 .iter()
133 .enumerate()
134 {
135 if i > 0 {
136 f.write_str(ARRAY_SEPARATOR)?;
137 }
138 f.write_str(item)?;
139 }
140 Ok(())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
151 fn prefix_is_the_wire_marker() {
152 assert_eq!(EslArray::PREFIX, "ARRAY::");
153 let input = format!("{}one|:two", EslArray::PREFIX);
154 assert_eq!(
155 EslArray::parse(&input)
156 .unwrap()
157 .items(),
158 &["one", "two"]
159 );
160 }
161
162 #[test]
163 fn parse_single_item() {
164 let arr = EslArray::parse("ARRAY::hello").unwrap();
165 assert_eq!(arr.items(), &["hello"]);
166 assert_eq!(arr.len(), 1);
167 }
168
169 #[test]
170 fn parse_multiple_items() {
171 let arr = EslArray::parse("ARRAY::one|:two|:three").unwrap();
172 assert_eq!(arr.items(), &["one", "two", "three"]);
173 assert_eq!(arr.len(), 3);
174 }
175
176 #[test]
177 fn parse_non_array_returns_missing_prefix() {
178 assert!(matches!(
179 EslArray::parse("not an array"),
180 Err(EslArrayError::MissingPrefix)
181 ));
182 assert!(matches!(
183 EslArray::parse(""),
184 Err(EslArrayError::MissingPrefix)
185 ));
186 assert!(matches!(
187 EslArray::parse("ARRAY:"),
188 Err(EslArrayError::MissingPrefix)
189 ));
190 }
191
192 #[test]
193 fn parse_at_max_items_succeeds() {
194 let items: Vec<&str> = (0..MAX_ARRAY_ITEMS)
195 .map(|_| "x")
196 .collect();
197 let input = format!("ARRAY::{}", items.join("|:"));
198 let arr = EslArray::parse(&input).unwrap();
199 assert_eq!(arr.len(), MAX_ARRAY_ITEMS);
200 }
201
202 #[test]
203 fn parse_over_max_items_returns_error() {
204 let items: Vec<&str> = (0..=MAX_ARRAY_ITEMS)
205 .map(|_| "x")
206 .collect();
207 let input = format!("ARRAY::{}", items.join("|:"));
208 assert_eq!(
209 EslArray::parse(&input),
210 Err(EslArrayError::TooManyItems {
211 count: MAX_ARRAY_ITEMS + 1,
212 max: MAX_ARRAY_ITEMS,
213 })
214 );
215 }
216
217 #[test]
218 fn error_display_missing_prefix() {
219 assert_eq!(
220 EslArrayError::MissingPrefix.to_string(),
221 "missing ARRAY:: prefix"
222 );
223 }
224
225 #[test]
226 fn error_display_too_many_items() {
227 let err = EslArrayError::TooManyItems {
228 count: 5000,
229 max: 4000,
230 };
231 assert_eq!(err.to_string(), "array has 5000 items, maximum is 4000");
232 }
233
234 #[test]
235 fn display_round_trip() {
236 let input = "ARRAY::one|:two|:three";
237 let arr = EslArray::parse(input).unwrap();
238 assert_eq!(arr.to_string(), input);
239 }
240
241 #[test]
242 fn display_single_item() {
243 let arr = EslArray::parse("ARRAY::only").unwrap();
244 assert_eq!(arr.to_string(), "ARRAY::only");
245 }
246
247 #[test]
248 fn empty_items_in_array() {
249 let arr = EslArray::parse("ARRAY::|:|:stuff").unwrap();
250 assert_eq!(arr.items(), &["", "", "stuff"]);
251 }
252
253 #[test]
254 fn test_new() {
255 let arr = EslArray::new(vec!["a".into(), "b".into(), "c".into()]);
256 assert_eq!(arr.items(), &["a", "b", "c"]);
257 assert_eq!(arr.len(), 3);
258 }
259
260 #[test]
261 fn test_push() {
262 let mut arr = EslArray::new(vec!["first".into()]);
263 arr.push("second".into());
264 arr.push("third".into());
265 assert_eq!(arr.items(), &["first", "second", "third"]);
266 assert_eq!(arr.to_string(), "ARRAY::first|:second|:third");
267 }
268
269 #[test]
270 fn test_unshift() {
271 let mut arr = EslArray::new(vec!["last".into()]);
272 arr.unshift("middle".into());
273 arr.unshift("first".into());
274 assert_eq!(arr.items(), &["first", "middle", "last"]);
275 assert_eq!(arr.to_string(), "ARRAY::first|:middle|:last");
276 }
277
278 #[test]
279 fn parse_sip_uris_with_colons() {
280 let input = "ARRAY::sip:alice@atlanta.example.com|:sip:bob@biloxi.example.com";
281 let arr = EslArray::parse(input).unwrap();
282 assert_eq!(
283 arr.items(),
284 &[
285 "sip:alice@atlanta.example.com",
286 "sip:bob@biloxi.example.com"
287 ]
288 );
289 assert_eq!(arr.to_string(), input);
290 }
291
292 #[test]
293 fn parse_sip_uris_with_angle_brackets_and_params() {
294 let input = "ARRAY::<sip:+15551234567@gw.example.com;user=phone>|:<tel:+15559876543>";
295 let arr = EslArray::parse(input).unwrap();
296 assert_eq!(arr.len(), 2);
297 assert_eq!(
298 arr.items()[0],
299 "<sip:+15551234567@gw.example.com;user=phone>"
300 );
301 assert_eq!(arr.items()[1], "<tel:+15559876543>");
302 assert_eq!(arr.to_string(), input);
303 }
304}