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