1use serde::de::{Deserializer, Error as DeError, Visitor};
8use serde::ser::Serializer;
9use serde::{Deserialize, Serialize};
10use std::fmt;
11use std::str::FromStr;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
40pub enum Routing {
41 None,
43 Account(String),
45 Forward(String),
47 Voicemail(String),
49 Sip(String),
51 System(String),
53 Group(String),
55 Queue(String),
57 Ivr(String),
59 Callback(String),
61 TimeCondition(String),
63 Disa(String),
65 Did(String),
67 Phone(String),
69 Unknown { tag: String, value: String },
72}
73
74impl Routing {
75 pub fn tag(&self) -> &str {
77 match self {
78 Routing::None => "none",
79 Routing::Account(_) => "account",
80 Routing::Forward(_) => "fwd",
81 Routing::Voicemail(_) => "vm",
82 Routing::Sip(_) => "sip",
83 Routing::System(_) => "sys",
84 Routing::Group(_) => "grp",
85 Routing::Queue(_) => "queue",
86 Routing::Ivr(_) => "ivr",
87 Routing::Callback(_) => "cb",
88 Routing::TimeCondition(_) => "tc",
89 Routing::Disa(_) => "disa",
90 Routing::Did(_) => "did",
91 Routing::Phone(_) => "phone",
92 Routing::Unknown { tag, .. } => tag,
93 }
94 }
95
96 pub fn value(&self) -> &str {
98 match self {
99 Routing::None => "",
100 Routing::Account(v)
101 | Routing::Forward(v)
102 | Routing::Voicemail(v)
103 | Routing::Sip(v)
104 | Routing::System(v)
105 | Routing::Group(v)
106 | Routing::Queue(v)
107 | Routing::Ivr(v)
108 | Routing::Callback(v)
109 | Routing::TimeCondition(v)
110 | Routing::Disa(v)
111 | Routing::Did(v)
112 | Routing::Phone(v) => v,
113 Routing::Unknown { value, .. } => value,
114 }
115 }
116}
117
118impl fmt::Display for Routing {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "{}:{}", self.tag(), self.value())
121 }
122}
123
124impl FromStr for Routing {
129 type Err = RoutingParseError;
130
131 fn from_str(s: &str) -> Result<Self, Self::Err> {
132 let (tag, value) = match s.find(':') {
133 Some(i) => (&s[..i], &s[i + 1..]),
134 None => return Err(RoutingParseError::MissingColon),
135 };
136
137 Ok(match tag {
138 "none" => Routing::None,
139 "account" => Routing::Account(value.into()),
140 "fwd" => Routing::Forward(value.into()),
141 "vm" => Routing::Voicemail(value.into()),
142 "sip" => Routing::Sip(value.into()),
143 "sys" => Routing::System(value.into()),
144 "grp" => Routing::Group(value.into()),
145 "queue" => Routing::Queue(value.into()),
146 "ivr" => Routing::Ivr(value.into()),
147 "cb" => Routing::Callback(value.into()),
148 "tc" => Routing::TimeCondition(value.into()),
149 "disa" => Routing::Disa(value.into()),
150 "did" => Routing::Did(value.into()),
151 "phone" => Routing::Phone(value.into()),
152 other => Routing::Unknown {
153 tag: other.to_string(),
154 value: value.to_string(),
155 },
156 })
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
162pub enum RoutingParseError {
163 MissingColon,
165}
166
167impl fmt::Display for RoutingParseError {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 RoutingParseError::MissingColon => {
171 f.write_str("routing string is missing required `:` separator")
172 }
173 }
174 }
175}
176
177impl std::error::Error for RoutingParseError {}
178
179impl Serialize for Routing {
180 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181 where
182 S: Serializer,
183 {
184 serializer.collect_str(self)
185 }
186}
187
188impl<'de> Deserialize<'de> for Routing {
189 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
190 where
191 D: Deserializer<'de>,
192 {
193 struct RoutingVisitor;
194
195 impl<'de> Visitor<'de> for RoutingVisitor {
196 type Value = Routing;
197
198 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 f.write_str("a VoIP.ms routing string of the form `tag:value`")
200 }
201
202 fn visit_str<E>(self, v: &str) -> Result<Routing, E>
203 where
204 E: DeError,
205 {
206 Routing::from_str(v).map_err(E::custom)
207 }
208
209 fn visit_string<E>(self, v: String) -> Result<Routing, E>
210 where
211 E: DeError,
212 {
213 Routing::from_str(&v).map_err(E::custom)
214 }
215 }
216
217 deserializer.deserialize_str(RoutingVisitor)
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
229pub enum Seconds {
230 Value(u64),
232 Unlimited,
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
241pub enum WaitTime {
242 Value(u64),
244 Unlimited,
246}
247
248macro_rules! impl_seconds {
249 ($name:ident, $unlimited_wire:literal, $expecting:literal) => {
250 impl From<u64> for $name {
251 fn from(v: u64) -> Self {
252 $name::Value(v)
253 }
254 }
255
256 impl Serialize for $name {
257 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
258 where
259 S: Serializer,
260 {
261 match self {
262 $name::Value(v) => serializer.serialize_str(&v.to_string()),
263 $name::Unlimited => serializer.serialize_str($unlimited_wire),
264 }
265 }
266 }
267
268 impl<'de> Deserialize<'de> for $name {
269 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
270 where
271 D: Deserializer<'de>,
272 {
273 struct SecondsVisitor;
274
275 impl<'de> Visitor<'de> for SecondsVisitor {
276 type Value = $name;
277
278 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 f.write_str($expecting)
280 }
281
282 fn visit_u64<E>(self, v: u64) -> Result<$name, E>
283 where
284 E: DeError,
285 {
286 Ok($name::Value(v))
287 }
288
289 fn visit_str<E>(self, v: &str) -> Result<$name, E>
290 where
291 E: DeError,
292 {
293 let t = v.trim();
294 match t.to_ascii_lowercase().as_str() {
295 "none" | "unlimited" => Ok($name::Unlimited),
296 _ => t
297 .parse::<u64>()
298 .map($name::Value)
299 .map_err(|_| E::custom(format!("invalid seconds value {v}"))),
300 }
301 }
302 }
303
304 deserializer.deserialize_any(SecondsVisitor)
305 }
306 }
307 };
308}
309
310impl_seconds!(Seconds, "none", "a number of seconds or `none`");
311impl_seconds!(WaitTime, "unlimited", "a number of seconds or `unlimited`");
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn parses_documented_tags() {
319 assert_eq!(Routing::from_str("none:").unwrap(), Routing::None);
320 assert_eq!(
321 Routing::from_str("account:100001_VoIP").unwrap(),
322 Routing::Account("100001_VoIP".into()),
323 );
324 assert_eq!(
325 Routing::from_str("fwd:15555").unwrap(),
326 Routing::Forward("15555".into()),
327 );
328 assert_eq!(
329 Routing::from_str("vm:101").unwrap(),
330 Routing::Voicemail("101".into()),
331 );
332 assert_eq!(
333 Routing::from_str("cb:2359").unwrap(),
334 Routing::Callback("2359".into()),
335 );
336 }
337
338 #[test]
339 fn preserves_unknown_tags() {
340 let r = Routing::from_str("future:abc").unwrap();
341 assert_eq!(
342 r,
343 Routing::Unknown {
344 tag: "future".into(),
345 value: "abc".into(),
346 },
347 );
348 assert_eq!(r.to_string(), "future:abc");
349 }
350
351 #[test]
352 fn sip_value_can_contain_colons() {
353 let r = Routing::from_str("sip:5552223333@sip.voip.ms:5060").unwrap();
355 assert_eq!(r, Routing::Sip("5552223333@sip.voip.ms:5060".into()));
356 assert_eq!(r.to_string(), "sip:5552223333@sip.voip.ms:5060");
357 }
358
359 #[test]
360 fn rejects_missing_colon() {
361 assert_eq!(
362 Routing::from_str("nocolon"),
363 Err(RoutingParseError::MissingColon),
364 );
365 }
366
367 #[test]
368 fn round_trips_through_serde() {
369 let r = Routing::Forward("19998887777".into());
370 let json = serde_json::to_string(&r).unwrap();
371 assert_eq!(json, "\"fwd:19998887777\"");
372 let back: Routing = serde_json::from_str(&json).unwrap();
373 assert_eq!(back, r);
374 }
375
376 #[test]
377 fn deserialize_none() {
378 let r: Routing = serde_json::from_str("\"none:\"").unwrap();
379 assert_eq!(r, Routing::None);
380 }
381
382 #[test]
383 fn seconds_serializes_value_and_sentinel() {
384 assert_eq!(
385 serde_json::to_string(&Seconds::Value(30)).unwrap(),
386 "\"30\""
387 );
388 assert_eq!(
389 serde_json::to_string(&Seconds::Unlimited).unwrap(),
390 "\"none\""
391 );
392 assert_eq!(
393 serde_json::to_string(&WaitTime::Unlimited).unwrap(),
394 "\"unlimited\""
395 );
396 }
397
398 #[test]
399 fn seconds_deserializes_number_string_and_sentinels() {
400 assert_eq!(
402 serde_json::from_str::<Seconds>("45").unwrap(),
403 Seconds::Value(45)
404 );
405 assert_eq!(
406 serde_json::from_str::<Seconds>("\"45\"").unwrap(),
407 Seconds::Value(45)
408 );
409 for s in ["\"none\"", "\"NONE\"", "\"unlimited\""] {
410 assert_eq!(
411 serde_json::from_str::<Seconds>(s).unwrap(),
412 Seconds::Unlimited,
413 "{s}"
414 );
415 }
416 assert_eq!(
418 serde_json::from_str::<WaitTime>("\"unlimited\"").unwrap(),
419 WaitTime::Unlimited
420 );
421 }
422}