1use std::fmt;
4use std::str::FromStr;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[non_exhaustive]
11#[repr(u8)]
12#[allow(missing_docs)]
13pub enum ChannelState {
14 CsNew = 0,
15 CsInit = 1,
16 CsRouting = 2,
17 CsSoftExecute = 3,
18 CsExecute = 4,
19 CsExchangeMedia = 5,
20 CsPark = 6,
21 CsConsumeMedia = 7,
22 CsHibernate = 8,
23 CsReset = 9,
24 CsHangup = 10,
25 CsReporting = 11,
26 CsDestroy = 12,
27 CsNone = 13,
28}
29
30impl ChannelState {
31 pub fn from_number(n: u8) -> Option<Self> {
33 match n {
34 0 => Some(Self::CsNew),
35 1 => Some(Self::CsInit),
36 2 => Some(Self::CsRouting),
37 3 => Some(Self::CsSoftExecute),
38 4 => Some(Self::CsExecute),
39 5 => Some(Self::CsExchangeMedia),
40 6 => Some(Self::CsPark),
41 7 => Some(Self::CsConsumeMedia),
42 8 => Some(Self::CsHibernate),
43 9 => Some(Self::CsReset),
44 10 => Some(Self::CsHangup),
45 11 => Some(Self::CsReporting),
46 12 => Some(Self::CsDestroy),
47 13 => Some(Self::CsNone),
48 _ => None,
49 }
50 }
51
52 pub fn as_number(&self) -> u8 {
54 *self as u8
55 }
56
57 pub const fn as_str(&self) -> &'static str {
59 match self {
60 Self::CsNew => "CS_NEW",
61 Self::CsInit => "CS_INIT",
62 Self::CsRouting => "CS_ROUTING",
63 Self::CsSoftExecute => "CS_SOFT_EXECUTE",
64 Self::CsExecute => "CS_EXECUTE",
65 Self::CsExchangeMedia => "CS_EXCHANGE_MEDIA",
66 Self::CsPark => "CS_PARK",
67 Self::CsConsumeMedia => "CS_CONSUME_MEDIA",
68 Self::CsHibernate => "CS_HIBERNATE",
69 Self::CsReset => "CS_RESET",
70 Self::CsHangup => "CS_HANGUP",
71 Self::CsReporting => "CS_REPORTING",
72 Self::CsDestroy => "CS_DESTROY",
73 Self::CsNone => "CS_NONE",
74 }
75 }
76}
77
78impl fmt::Display for ChannelState {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.write_str(self.as_str())
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct ParseChannelStateError(pub String);
87
88impl fmt::Display for ParseChannelStateError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "unknown channel state: {}", self.0)
91 }
92}
93
94impl std::error::Error for ParseChannelStateError {}
95
96impl FromStr for ChannelState {
97 type Err = ParseChannelStateError;
98
99 fn from_str(s: &str) -> Result<Self, Self::Err> {
100 match s {
101 "CS_NEW" => Ok(Self::CsNew),
102 "CS_INIT" => Ok(Self::CsInit),
103 "CS_ROUTING" => Ok(Self::CsRouting),
104 "CS_SOFT_EXECUTE" => Ok(Self::CsSoftExecute),
105 "CS_EXECUTE" => Ok(Self::CsExecute),
106 "CS_EXCHANGE_MEDIA" => Ok(Self::CsExchangeMedia),
107 "CS_PARK" => Ok(Self::CsPark),
108 "CS_CONSUME_MEDIA" => Ok(Self::CsConsumeMedia),
109 "CS_HIBERNATE" => Ok(Self::CsHibernate),
110 "CS_RESET" => Ok(Self::CsReset),
111 "CS_HANGUP" => Ok(Self::CsHangup),
112 "CS_REPORTING" => Ok(Self::CsReporting),
113 "CS_DESTROY" => Ok(Self::CsDestroy),
114 "CS_NONE" => Ok(Self::CsNone),
115 _ => Err(ParseChannelStateError(s.to_string())),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
122#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
123#[non_exhaustive]
124#[allow(missing_docs)]
125pub enum CallState {
126 Down,
127 Dialing,
128 Ringing,
129 Early,
130 Active,
131 Held,
132 RingWait,
133 Hangup,
134 Unheld,
135}
136
137impl CallState {
138 pub const fn as_str(&self) -> &'static str {
140 match self {
141 Self::Down => "DOWN",
142 Self::Dialing => "DIALING",
143 Self::Ringing => "RINGING",
144 Self::Early => "EARLY",
145 Self::Active => "ACTIVE",
146 Self::Held => "HELD",
147 Self::RingWait => "RING_WAIT",
148 Self::Hangup => "HANGUP",
149 Self::Unheld => "UNHELD",
150 }
151 }
152}
153
154impl fmt::Display for CallState {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.write_str(self.as_str())
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct ParseCallStateError(pub String);
163
164impl fmt::Display for ParseCallStateError {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(f, "unknown call state: {}", self.0)
167 }
168}
169
170impl std::error::Error for ParseCallStateError {}
171
172impl FromStr for CallState {
173 type Err = ParseCallStateError;
174
175 fn from_str(s: &str) -> Result<Self, Self::Err> {
176 match s {
177 "DOWN" => Ok(Self::Down),
178 "DIALING" => Ok(Self::Dialing),
179 "RINGING" => Ok(Self::Ringing),
180 "EARLY" => Ok(Self::Early),
181 "ACTIVE" => Ok(Self::Active),
182 "HELD" => Ok(Self::Held),
183 "RING_WAIT" => Ok(Self::RingWait),
184 "HANGUP" => Ok(Self::Hangup),
185 "UNHELD" => Ok(Self::Unheld),
186 _ => Err(ParseCallStateError(s.to_string())),
187 }
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
194#[non_exhaustive]
195#[allow(missing_docs)]
196pub enum AnswerState {
197 Hangup,
198 Answered,
199 Early,
200 Ringing,
201}
202
203impl AnswerState {
204 pub const fn as_str(&self) -> &'static str {
206 match self {
207 Self::Hangup => "hangup",
208 Self::Answered => "answered",
209 Self::Early => "early",
210 Self::Ringing => "ringing",
211 }
212 }
213}
214
215impl fmt::Display for AnswerState {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 f.write_str(self.as_str())
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
223pub struct ParseAnswerStateError(pub String);
224
225impl fmt::Display for ParseAnswerStateError {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "unknown answer state: {}", self.0)
228 }
229}
230
231impl std::error::Error for ParseAnswerStateError {}
232
233impl FromStr for AnswerState {
234 type Err = ParseAnswerStateError;
235
236 fn from_str(s: &str) -> Result<Self, Self::Err> {
237 match s {
238 "hangup" => Ok(Self::Hangup),
239 "answered" => Ok(Self::Answered),
240 "early" => Ok(Self::Early),
241 "ringing" => Ok(Self::Ringing),
242 _ => Err(ParseAnswerStateError(s.to_string())),
243 }
244 }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
249#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
250#[non_exhaustive]
251#[allow(missing_docs)]
252pub enum CallDirection {
253 Inbound,
254 Outbound,
255}
256
257impl CallDirection {
258 pub const fn as_str(&self) -> &'static str {
260 match self {
261 Self::Inbound => "inbound",
262 Self::Outbound => "outbound",
263 }
264 }
265}
266
267impl fmt::Display for CallDirection {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 f.write_str(self.as_str())
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
275pub struct ParseCallDirectionError(pub String);
276
277impl fmt::Display for ParseCallDirectionError {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 write!(f, "unknown call direction: {}", self.0)
280 }
281}
282
283impl std::error::Error for ParseCallDirectionError {}
284
285impl FromStr for CallDirection {
286 type Err = ParseCallDirectionError;
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 match s {
290 "inbound" => Ok(Self::Inbound),
291 "outbound" => Ok(Self::Outbound),
292 _ => Err(ParseCallDirectionError(s.to_string())),
293 }
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct ParseHangupCauseError(pub String);
300
301impl fmt::Display for ParseHangupCauseError {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 write!(f, "unknown hangup cause: {}", self.0)
304 }
305}
306
307impl std::error::Error for ParseHangupCauseError {}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
315#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
316#[non_exhaustive]
317#[repr(u16)]
318#[allow(missing_docs)]
319pub enum HangupCause {
320 None = 0,
321 UnallocatedNumber = 1,
322 NoRouteTransitNet = 2,
323 NoRouteDestination = 3,
324 ChannelUnacceptable = 6,
325 CallAwardedDelivered = 7,
326 NormalClearing = 16,
327 UserBusy = 17,
328 NoUserResponse = 18,
329 NoAnswer = 19,
330 SubscriberAbsent = 20,
331 CallRejected = 21,
332 NumberChanged = 22,
333 RedirectionToNewDestination = 23,
334 ExchangeRoutingError = 25,
335 DestinationOutOfOrder = 27,
336 InvalidNumberFormat = 28,
337 FacilityRejected = 29,
338 ResponseToStatusEnquiry = 30,
339 NormalUnspecified = 31,
340 NormalCircuitCongestion = 34,
341 NetworkOutOfOrder = 38,
342 NormalTemporaryFailure = 41,
343 SwitchCongestion = 42,
344 AccessInfoDiscarded = 43,
345 RequestedChanUnavail = 44,
346 PreEmpted = 45,
347 FacilityNotSubscribed = 50,
348 OutgoingCallBarred = 52,
349 IncomingCallBarred = 54,
350 BearercapabilityNotauth = 57,
351 BearercapabilityNotavail = 58,
352 ServiceUnavailable = 63,
353 BearercapabilityNotimpl = 65,
354 ChanNotImplemented = 66,
355 FacilityNotImplemented = 69,
356 ServiceNotImplemented = 79,
357 InvalidCallReference = 81,
358 IncompatibleDestination = 88,
359 InvalidMsgUnspecified = 95,
360 MandatoryIeMissing = 96,
361 MessageTypeNonexist = 97,
362 WrongMessage = 98,
363 IeNonexist = 99,
364 InvalidIeContents = 100,
365 WrongCallState = 101,
366 RecoveryOnTimerExpire = 102,
367 MandatoryIeLengthError = 103,
368 ProtocolError = 111,
369 Interworking = 127,
370 Success = 142,
371 OriginatorCancel = 487,
372 Crash = 700,
373 SystemShutdown = 701,
374 LoseRace = 702,
375 ManagerRequest = 703,
376 BlindTransfer = 800,
377 AttendedTransfer = 801,
378 AllottedTimeout = 802,
379 UserChallenge = 803,
380 MediaTimeout = 804,
381 PickedOff = 805,
382 UserNotRegistered = 806,
383 ProgressTimeout = 807,
384 InvalidGateway = 808,
385 GatewayDown = 809,
386 InvalidUrl = 810,
387 InvalidProfile = 811,
388 NoPickup = 812,
389 SrtpReadError = 813,
390 Bowout = 814,
391 BusyEverywhere = 815,
392 Decline = 816,
393 DoesNotExistAnywhere = 817,
394 NotAcceptable = 818,
395 Unwanted = 819,
396 NoIdentity = 820,
397 BadIdentityInfo = 821,
398 UnsupportedCertificate = 822,
399 InvalidIdentity = 823,
400 StaleDate = 824,
402 RejectAll = 825,
404}
405
406impl HangupCause {
407 pub fn as_number(&self) -> u16 {
409 *self as u16
410 }
411
412 pub fn from_number(n: u16) -> Option<Self> {
414 match n {
415 0 => Some(Self::None),
416 1 => Some(Self::UnallocatedNumber),
417 2 => Some(Self::NoRouteTransitNet),
418 3 => Some(Self::NoRouteDestination),
419 6 => Some(Self::ChannelUnacceptable),
420 7 => Some(Self::CallAwardedDelivered),
421 16 => Some(Self::NormalClearing),
422 17 => Some(Self::UserBusy),
423 18 => Some(Self::NoUserResponse),
424 19 => Some(Self::NoAnswer),
425 20 => Some(Self::SubscriberAbsent),
426 21 => Some(Self::CallRejected),
427 22 => Some(Self::NumberChanged),
428 23 => Some(Self::RedirectionToNewDestination),
429 25 => Some(Self::ExchangeRoutingError),
430 27 => Some(Self::DestinationOutOfOrder),
431 28 => Some(Self::InvalidNumberFormat),
432 29 => Some(Self::FacilityRejected),
433 30 => Some(Self::ResponseToStatusEnquiry),
434 31 => Some(Self::NormalUnspecified),
435 34 => Some(Self::NormalCircuitCongestion),
436 38 => Some(Self::NetworkOutOfOrder),
437 41 => Some(Self::NormalTemporaryFailure),
438 42 => Some(Self::SwitchCongestion),
439 43 => Some(Self::AccessInfoDiscarded),
440 44 => Some(Self::RequestedChanUnavail),
441 45 => Some(Self::PreEmpted),
442 50 => Some(Self::FacilityNotSubscribed),
443 52 => Some(Self::OutgoingCallBarred),
444 54 => Some(Self::IncomingCallBarred),
445 57 => Some(Self::BearercapabilityNotauth),
446 58 => Some(Self::BearercapabilityNotavail),
447 63 => Some(Self::ServiceUnavailable),
448 65 => Some(Self::BearercapabilityNotimpl),
449 66 => Some(Self::ChanNotImplemented),
450 69 => Some(Self::FacilityNotImplemented),
451 79 => Some(Self::ServiceNotImplemented),
452 81 => Some(Self::InvalidCallReference),
453 88 => Some(Self::IncompatibleDestination),
454 95 => Some(Self::InvalidMsgUnspecified),
455 96 => Some(Self::MandatoryIeMissing),
456 97 => Some(Self::MessageTypeNonexist),
457 98 => Some(Self::WrongMessage),
458 99 => Some(Self::IeNonexist),
459 100 => Some(Self::InvalidIeContents),
460 101 => Some(Self::WrongCallState),
461 102 => Some(Self::RecoveryOnTimerExpire),
462 103 => Some(Self::MandatoryIeLengthError),
463 111 => Some(Self::ProtocolError),
464 127 => Some(Self::Interworking),
465 142 => Some(Self::Success),
466 487 => Some(Self::OriginatorCancel),
467 700 => Some(Self::Crash),
468 701 => Some(Self::SystemShutdown),
469 702 => Some(Self::LoseRace),
470 703 => Some(Self::ManagerRequest),
471 800 => Some(Self::BlindTransfer),
472 801 => Some(Self::AttendedTransfer),
473 802 => Some(Self::AllottedTimeout),
474 803 => Some(Self::UserChallenge),
475 804 => Some(Self::MediaTimeout),
476 805 => Some(Self::PickedOff),
477 806 => Some(Self::UserNotRegistered),
478 807 => Some(Self::ProgressTimeout),
479 808 => Some(Self::InvalidGateway),
480 809 => Some(Self::GatewayDown),
481 810 => Some(Self::InvalidUrl),
482 811 => Some(Self::InvalidProfile),
483 812 => Some(Self::NoPickup),
484 813 => Some(Self::SrtpReadError),
485 814 => Some(Self::Bowout),
486 815 => Some(Self::BusyEverywhere),
487 816 => Some(Self::Decline),
488 817 => Some(Self::DoesNotExistAnywhere),
489 818 => Some(Self::NotAcceptable),
490 819 => Some(Self::Unwanted),
491 820 => Some(Self::NoIdentity),
492 821 => Some(Self::BadIdentityInfo),
493 822 => Some(Self::UnsupportedCertificate),
494 823 => Some(Self::InvalidIdentity),
495 824 => Some(Self::StaleDate),
496 825 => Some(Self::RejectAll),
497 _ => None,
498 }
499 }
500
501 pub fn from_sip_response(code: u16) -> Option<Self> {
506 match code {
507 200 => Some(Self::NormalClearing),
508 401 | 402 | 403 | 407 | 603 | 608 => Some(Self::CallRejected),
509 607 => Some(Self::Unwanted),
510 404 => Some(Self::UnallocatedNumber),
511 485 | 604 => Some(Self::NoRouteDestination),
512 408 | 504 => Some(Self::RecoveryOnTimerExpire),
513 410 => Some(Self::NumberChanged),
514 413 | 414 | 416 | 420 | 421 | 423 | 505 | 513 => Some(Self::Interworking),
515 480 => Some(Self::NoUserResponse),
516 400 | 481 | 500 | 503 => Some(Self::NormalTemporaryFailure),
517 486 | 600 => Some(Self::UserBusy),
518 484 => Some(Self::InvalidNumberFormat),
519 488 | 606 => Some(Self::IncompatibleDestination),
520 502 => Some(Self::NetworkOutOfOrder),
521 405 => Some(Self::ServiceUnavailable),
522 406 | 415 | 501 => Some(Self::ServiceNotImplemented),
523 482 | 483 => Some(Self::ExchangeRoutingError),
524 487 => Some(Self::OriginatorCancel),
525 428 => Some(Self::NoIdentity),
526 429 => Some(Self::BadIdentityInfo),
527 437 => Some(Self::UnsupportedCertificate),
528 438 => Some(Self::InvalidIdentity),
529 _ => None,
530 }
531 }
532
533 pub const fn as_str(&self) -> &'static str {
535 match self {
536 Self::None => "NONE",
537 Self::UnallocatedNumber => "UNALLOCATED_NUMBER",
538 Self::NoRouteTransitNet => "NO_ROUTE_TRANSIT_NET",
539 Self::NoRouteDestination => "NO_ROUTE_DESTINATION",
540 Self::ChannelUnacceptable => "CHANNEL_UNACCEPTABLE",
541 Self::CallAwardedDelivered => "CALL_AWARDED_DELIVERED",
542 Self::NormalClearing => "NORMAL_CLEARING",
543 Self::UserBusy => "USER_BUSY",
544 Self::NoUserResponse => "NO_USER_RESPONSE",
545 Self::NoAnswer => "NO_ANSWER",
546 Self::SubscriberAbsent => "SUBSCRIBER_ABSENT",
547 Self::CallRejected => "CALL_REJECTED",
548 Self::NumberChanged => "NUMBER_CHANGED",
549 Self::RedirectionToNewDestination => "REDIRECTION_TO_NEW_DESTINATION",
550 Self::ExchangeRoutingError => "EXCHANGE_ROUTING_ERROR",
551 Self::DestinationOutOfOrder => "DESTINATION_OUT_OF_ORDER",
552 Self::InvalidNumberFormat => "INVALID_NUMBER_FORMAT",
553 Self::FacilityRejected => "FACILITY_REJECTED",
554 Self::ResponseToStatusEnquiry => "RESPONSE_TO_STATUS_ENQUIRY",
555 Self::NormalUnspecified => "NORMAL_UNSPECIFIED",
556 Self::NormalCircuitCongestion => "NORMAL_CIRCUIT_CONGESTION",
557 Self::NetworkOutOfOrder => "NETWORK_OUT_OF_ORDER",
558 Self::NormalTemporaryFailure => "NORMAL_TEMPORARY_FAILURE",
559 Self::SwitchCongestion => "SWITCH_CONGESTION",
560 Self::AccessInfoDiscarded => "ACCESS_INFO_DISCARDED",
561 Self::RequestedChanUnavail => "REQUESTED_CHAN_UNAVAIL",
562 Self::PreEmpted => "PRE_EMPTED",
563 Self::FacilityNotSubscribed => "FACILITY_NOT_SUBSCRIBED",
564 Self::OutgoingCallBarred => "OUTGOING_CALL_BARRED",
565 Self::IncomingCallBarred => "INCOMING_CALL_BARRED",
566 Self::BearercapabilityNotauth => "BEARERCAPABILITY_NOTAUTH",
567 Self::BearercapabilityNotavail => "BEARERCAPABILITY_NOTAVAIL",
568 Self::ServiceUnavailable => "SERVICE_UNAVAILABLE",
569 Self::BearercapabilityNotimpl => "BEARERCAPABILITY_NOTIMPL",
570 Self::ChanNotImplemented => "CHAN_NOT_IMPLEMENTED",
571 Self::FacilityNotImplemented => "FACILITY_NOT_IMPLEMENTED",
572 Self::ServiceNotImplemented => "SERVICE_NOT_IMPLEMENTED",
573 Self::InvalidCallReference => "INVALID_CALL_REFERENCE",
574 Self::IncompatibleDestination => "INCOMPATIBLE_DESTINATION",
575 Self::InvalidMsgUnspecified => "INVALID_MSG_UNSPECIFIED",
576 Self::MandatoryIeMissing => "MANDATORY_IE_MISSING",
577 Self::MessageTypeNonexist => "MESSAGE_TYPE_NONEXIST",
578 Self::WrongMessage => "WRONG_MESSAGE",
579 Self::IeNonexist => "IE_NONEXIST",
580 Self::InvalidIeContents => "INVALID_IE_CONTENTS",
581 Self::WrongCallState => "WRONG_CALL_STATE",
582 Self::RecoveryOnTimerExpire => "RECOVERY_ON_TIMER_EXPIRE",
583 Self::MandatoryIeLengthError => "MANDATORY_IE_LENGTH_ERROR",
584 Self::ProtocolError => "PROTOCOL_ERROR",
585 Self::Interworking => "INTERWORKING",
586 Self::Success => "SUCCESS",
587 Self::OriginatorCancel => "ORIGINATOR_CANCEL",
588 Self::Crash => "CRASH",
589 Self::SystemShutdown => "SYSTEM_SHUTDOWN",
590 Self::LoseRace => "LOSE_RACE",
591 Self::ManagerRequest => "MANAGER_REQUEST",
592 Self::BlindTransfer => "BLIND_TRANSFER",
593 Self::AttendedTransfer => "ATTENDED_TRANSFER",
594 Self::AllottedTimeout => "ALLOTTED_TIMEOUT",
595 Self::UserChallenge => "USER_CHALLENGE",
596 Self::MediaTimeout => "MEDIA_TIMEOUT",
597 Self::PickedOff => "PICKED_OFF",
598 Self::UserNotRegistered => "USER_NOT_REGISTERED",
599 Self::ProgressTimeout => "PROGRESS_TIMEOUT",
600 Self::InvalidGateway => "INVALID_GATEWAY",
601 Self::GatewayDown => "GATEWAY_DOWN",
602 Self::InvalidUrl => "INVALID_URL",
603 Self::InvalidProfile => "INVALID_PROFILE",
604 Self::NoPickup => "NO_PICKUP",
605 Self::SrtpReadError => "SRTP_READ_ERROR",
606 Self::Bowout => "BOWOUT",
607 Self::BusyEverywhere => "BUSY_EVERYWHERE",
608 Self::Decline => "DECLINE",
609 Self::DoesNotExistAnywhere => "DOES_NOT_EXIST_ANYWHERE",
610 Self::NotAcceptable => "NOT_ACCEPTABLE",
611 Self::Unwanted => "UNWANTED",
612 Self::NoIdentity => "NO_IDENTITY",
613 Self::BadIdentityInfo => "BAD_IDENTITY_INFO",
614 Self::UnsupportedCertificate => "UNSUPPORTED_CERTIFICATE",
615 Self::InvalidIdentity => "INVALID_IDENTITY",
616 Self::StaleDate => "STALE_DATE",
617 Self::RejectAll => "REJECT_ALL",
618 }
619 }
620}
621
622impl fmt::Display for HangupCause {
623 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624 f.write_str(self.as_str())
625 }
626}
627
628impl FromStr for HangupCause {
629 type Err = ParseHangupCauseError;
630
631 fn from_str(s: &str) -> Result<Self, Self::Err> {
632 Ok(match s {
633 "NONE" => Self::None,
634 "UNALLOCATED_NUMBER" => Self::UnallocatedNumber,
635 "NO_ROUTE_TRANSIT_NET" => Self::NoRouteTransitNet,
636 "NO_ROUTE_DESTINATION" => Self::NoRouteDestination,
637 "CHANNEL_UNACCEPTABLE" => Self::ChannelUnacceptable,
638 "CALL_AWARDED_DELIVERED" => Self::CallAwardedDelivered,
639 "NORMAL_CLEARING" => Self::NormalClearing,
640 "USER_BUSY" => Self::UserBusy,
641 "NO_USER_RESPONSE" => Self::NoUserResponse,
642 "NO_ANSWER" => Self::NoAnswer,
643 "SUBSCRIBER_ABSENT" => Self::SubscriberAbsent,
644 "CALL_REJECTED" => Self::CallRejected,
645 "NUMBER_CHANGED" => Self::NumberChanged,
646 "REDIRECTION_TO_NEW_DESTINATION" => Self::RedirectionToNewDestination,
647 "EXCHANGE_ROUTING_ERROR" => Self::ExchangeRoutingError,
648 "DESTINATION_OUT_OF_ORDER" => Self::DestinationOutOfOrder,
649 "INVALID_NUMBER_FORMAT" => Self::InvalidNumberFormat,
650 "FACILITY_REJECTED" => Self::FacilityRejected,
651 "RESPONSE_TO_STATUS_ENQUIRY" => Self::ResponseToStatusEnquiry,
652 "NORMAL_UNSPECIFIED" => Self::NormalUnspecified,
653 "NORMAL_CIRCUIT_CONGESTION" => Self::NormalCircuitCongestion,
654 "NETWORK_OUT_OF_ORDER" => Self::NetworkOutOfOrder,
655 "NORMAL_TEMPORARY_FAILURE" => Self::NormalTemporaryFailure,
656 "SWITCH_CONGESTION" => Self::SwitchCongestion,
657 "ACCESS_INFO_DISCARDED" => Self::AccessInfoDiscarded,
658 "REQUESTED_CHAN_UNAVAIL" => Self::RequestedChanUnavail,
659 "PRE_EMPTED" => Self::PreEmpted,
660 "FACILITY_NOT_SUBSCRIBED" => Self::FacilityNotSubscribed,
661 "OUTGOING_CALL_BARRED" => Self::OutgoingCallBarred,
662 "INCOMING_CALL_BARRED" => Self::IncomingCallBarred,
663 "BEARERCAPABILITY_NOTAUTH" => Self::BearercapabilityNotauth,
664 "BEARERCAPABILITY_NOTAVAIL" => Self::BearercapabilityNotavail,
665 "SERVICE_UNAVAILABLE" => Self::ServiceUnavailable,
666 "BEARERCAPABILITY_NOTIMPL" => Self::BearercapabilityNotimpl,
667 "CHAN_NOT_IMPLEMENTED" => Self::ChanNotImplemented,
668 "FACILITY_NOT_IMPLEMENTED" => Self::FacilityNotImplemented,
669 "SERVICE_NOT_IMPLEMENTED" => Self::ServiceNotImplemented,
670 "INVALID_CALL_REFERENCE" => Self::InvalidCallReference,
671 "INCOMPATIBLE_DESTINATION" => Self::IncompatibleDestination,
672 "INVALID_MSG_UNSPECIFIED" => Self::InvalidMsgUnspecified,
673 "MANDATORY_IE_MISSING" => Self::MandatoryIeMissing,
674 "MESSAGE_TYPE_NONEXIST" => Self::MessageTypeNonexist,
675 "WRONG_MESSAGE" => Self::WrongMessage,
676 "IE_NONEXIST" => Self::IeNonexist,
677 "INVALID_IE_CONTENTS" => Self::InvalidIeContents,
678 "WRONG_CALL_STATE" => Self::WrongCallState,
679 "RECOVERY_ON_TIMER_EXPIRE" => Self::RecoveryOnTimerExpire,
680 "MANDATORY_IE_LENGTH_ERROR" => Self::MandatoryIeLengthError,
681 "PROTOCOL_ERROR" => Self::ProtocolError,
682 "INTERWORKING" => Self::Interworking,
683 "SUCCESS" => Self::Success,
684 "ORIGINATOR_CANCEL" => Self::OriginatorCancel,
685 "CRASH" => Self::Crash,
686 "SYSTEM_SHUTDOWN" => Self::SystemShutdown,
687 "LOSE_RACE" => Self::LoseRace,
688 "MANAGER_REQUEST" => Self::ManagerRequest,
689 "BLIND_TRANSFER" => Self::BlindTransfer,
690 "ATTENDED_TRANSFER" => Self::AttendedTransfer,
691 "ALLOTTED_TIMEOUT" => Self::AllottedTimeout,
692 "USER_CHALLENGE" => Self::UserChallenge,
693 "MEDIA_TIMEOUT" => Self::MediaTimeout,
694 "PICKED_OFF" => Self::PickedOff,
695 "USER_NOT_REGISTERED" => Self::UserNotRegistered,
696 "PROGRESS_TIMEOUT" => Self::ProgressTimeout,
697 "INVALID_GATEWAY" => Self::InvalidGateway,
698 "GATEWAY_DOWN" => Self::GatewayDown,
699 "INVALID_URL" => Self::InvalidUrl,
700 "INVALID_PROFILE" => Self::InvalidProfile,
701 "NO_PICKUP" => Self::NoPickup,
702 "SRTP_READ_ERROR" => Self::SrtpReadError,
703 "BOWOUT" => Self::Bowout,
704 "BUSY_EVERYWHERE" => Self::BusyEverywhere,
705 "DECLINE" => Self::Decline,
706 "DOES_NOT_EXIST_ANYWHERE" => Self::DoesNotExistAnywhere,
707 "NOT_ACCEPTABLE" => Self::NotAcceptable,
708 "UNWANTED" => Self::Unwanted,
709 "NO_IDENTITY" => Self::NoIdentity,
710 "BAD_IDENTITY_INFO" => Self::BadIdentityInfo,
711 "UNSUPPORTED_CERTIFICATE" => Self::UnsupportedCertificate,
712 "INVALID_IDENTITY" => Self::InvalidIdentity,
713 "STALE_DATE" => Self::StaleDate,
714 "REJECT_ALL" => Self::RejectAll,
715 _ => return Err(ParseHangupCauseError(s.to_string())),
716 })
717 }
718}
719
720#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
741#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
742#[non_exhaustive]
743pub struct ChannelTimetable {
744 pub profile_created: Option<i64>,
746 pub created: Option<i64>,
748 pub answered: Option<i64>,
750 pub progress: Option<i64>,
752 pub progress_media: Option<i64>,
754 pub hungup: Option<i64>,
756 pub transferred: Option<i64>,
758 pub resurrected: Option<i64>,
760 pub bridged: Option<i64>,
762 pub last_hold: Option<i64>,
764 pub hold_accum: Option<i64>,
766}
767
768#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
774#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
775#[non_exhaustive]
776pub enum TimetablePrefix {
777 Caller,
779 OtherLeg,
781 Channel,
783 Hunt,
785 Originator,
787 Originatee,
789 PostOriginator,
791 PostOriginatee,
793}
794
795impl TimetablePrefix {
796 pub fn as_str(&self) -> &'static str {
798 match self {
799 Self::Caller => "Caller",
800 Self::OtherLeg => "Other-Leg",
801 Self::Channel => "Channel",
802 Self::Hunt => "Hunt",
803 Self::Originator => "ORIGINATOR",
804 Self::Originatee => "ORIGINATEE",
805 Self::PostOriginator => "POST-ORIGINATOR",
806 Self::PostOriginatee => "POST-ORIGINATEE",
807 }
808 }
809}
810
811impl fmt::Display for TimetablePrefix {
812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 f.write_str(self.as_str())
814 }
815}
816
817impl AsRef<str> for TimetablePrefix {
818 fn as_ref(&self) -> &str {
819 self.as_str()
820 }
821}
822
823#[derive(Debug, Clone, PartialEq, Eq)]
825#[non_exhaustive]
826pub struct ParseTimetableError {
827 pub header: String,
829 pub value: String,
831}
832
833impl fmt::Display for ParseTimetableError {
834 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835 write!(
836 f,
837 "invalid timetable value for {}: {:?}",
838 self.header, self.value
839 )
840 }
841}
842
843impl std::error::Error for ParseTimetableError {}
844
845impl ParseTimetableError {
846 pub fn new(header: impl Into<String>, value: impl Into<String>) -> Self {
848 Self {
849 header: header.into(),
850 value: value.into(),
851 }
852 }
853}
854
855impl ChannelTimetable {
856 pub const SUFFIXES: &'static [&'static str] = &[
872 "Profile-Created-Time",
873 "Channel-Created-Time",
874 "Channel-Answered-Time",
875 "Channel-Progress-Time",
876 "Channel-Progress-Media-Time",
877 "Channel-Hangup-Time",
878 "Channel-Transfer-Time",
879 "Channel-Resurrect-Time",
880 "Channel-Bridged-Time",
881 "Channel-Last-Hold",
882 "Channel-Hold-Accum",
883 ];
884
885 pub fn from_lookup<'a>(
910 prefix: impl AsRef<str>,
911 lookup: impl Fn(&str) -> Option<&'a str>,
912 ) -> Result<Option<Self>, ParseTimetableError> {
913 let prefix = prefix.as_ref();
914 let mut tt = Self::default();
915 let mut found = false;
916
917 macro_rules! field {
918 ($field:ident, $suffix:literal) => {
919 let header = format!("{}-{}", prefix, $suffix);
920 if let Some(raw) = lookup(&header) {
921 let v: i64 = raw
922 .parse()
923 .map_err(|_| ParseTimetableError {
924 header: header.clone(),
925 value: raw.to_string(),
926 })?;
927 tt.$field = Some(v);
928 found = true;
929 }
930 };
931 }
932
933 field!(profile_created, "Profile-Created-Time");
934 field!(created, "Channel-Created-Time");
935 field!(answered, "Channel-Answered-Time");
936 field!(progress, "Channel-Progress-Time");
937 field!(progress_media, "Channel-Progress-Media-Time");
938 field!(hungup, "Channel-Hangup-Time");
939 field!(transferred, "Channel-Transfer-Time");
940 field!(resurrected, "Channel-Resurrect-Time");
941 field!(bridged, "Channel-Bridged-Time");
942 field!(last_hold, "Channel-Last-Hold");
943 field!(hold_accum, "Channel-Hold-Accum");
944
945 if found {
946 Ok(Some(tt))
947 } else {
948 Ok(None)
949 }
950 }
951}
952
953#[cfg(test)]
954mod tests {
955 use super::*;
956 use crate::event::EslEvent;
957 use crate::lookup::HeaderLookup;
958
959 #[test]
962 fn test_channel_state_display() {
963 assert_eq!(ChannelState::CsNew.to_string(), "CS_NEW");
964 assert_eq!(ChannelState::CsInit.to_string(), "CS_INIT");
965 assert_eq!(ChannelState::CsRouting.to_string(), "CS_ROUTING");
966 assert_eq!(ChannelState::CsSoftExecute.to_string(), "CS_SOFT_EXECUTE");
967 assert_eq!(ChannelState::CsExecute.to_string(), "CS_EXECUTE");
968 assert_eq!(
969 ChannelState::CsExchangeMedia.to_string(),
970 "CS_EXCHANGE_MEDIA"
971 );
972 assert_eq!(ChannelState::CsPark.to_string(), "CS_PARK");
973 assert_eq!(ChannelState::CsConsumeMedia.to_string(), "CS_CONSUME_MEDIA");
974 assert_eq!(ChannelState::CsHibernate.to_string(), "CS_HIBERNATE");
975 assert_eq!(ChannelState::CsReset.to_string(), "CS_RESET");
976 assert_eq!(ChannelState::CsHangup.to_string(), "CS_HANGUP");
977 assert_eq!(ChannelState::CsReporting.to_string(), "CS_REPORTING");
978 assert_eq!(ChannelState::CsDestroy.to_string(), "CS_DESTROY");
979 assert_eq!(ChannelState::CsNone.to_string(), "CS_NONE");
980 }
981
982 #[test]
983 fn test_channel_state_from_str() {
984 assert_eq!("CS_NEW".parse::<ChannelState>(), Ok(ChannelState::CsNew));
985 assert_eq!(
986 "CS_EXECUTE".parse::<ChannelState>(),
987 Ok(ChannelState::CsExecute)
988 );
989 assert_eq!(
990 "CS_HANGUP".parse::<ChannelState>(),
991 Ok(ChannelState::CsHangup)
992 );
993 assert_eq!(
994 "CS_DESTROY".parse::<ChannelState>(),
995 Ok(ChannelState::CsDestroy)
996 );
997 }
998
999 #[test]
1000 fn test_channel_state_from_str_rejects_wrong_case() {
1001 assert!("cs_new"
1002 .parse::<ChannelState>()
1003 .is_err());
1004 assert!("Cs_Routing"
1005 .parse::<ChannelState>()
1006 .is_err());
1007 }
1008
1009 #[test]
1010 fn test_channel_state_from_str_unknown() {
1011 assert!("CS_BOGUS"
1012 .parse::<ChannelState>()
1013 .is_err());
1014 assert!(""
1015 .parse::<ChannelState>()
1016 .is_err());
1017 }
1018
1019 #[test]
1020 fn test_channel_state_from_number() {
1021 assert_eq!(ChannelState::from_number(0), Some(ChannelState::CsNew));
1022 assert_eq!(ChannelState::from_number(4), Some(ChannelState::CsExecute));
1023 assert_eq!(ChannelState::from_number(10), Some(ChannelState::CsHangup));
1024 assert_eq!(ChannelState::from_number(13), Some(ChannelState::CsNone));
1025 assert_eq!(ChannelState::from_number(14), None);
1026 assert_eq!(ChannelState::from_number(255), None);
1027 }
1028
1029 #[test]
1030 fn test_channel_state_as_number() {
1031 assert_eq!(ChannelState::CsNew.as_number(), 0);
1032 assert_eq!(ChannelState::CsExecute.as_number(), 4);
1033 assert_eq!(ChannelState::CsHangup.as_number(), 10);
1034 assert_eq!(ChannelState::CsNone.as_number(), 13);
1035 }
1036
1037 #[test]
1038 fn channel_state_ordering_follows_lifecycle() {
1039 assert!(ChannelState::CsNew < ChannelState::CsInit);
1040 assert!(ChannelState::CsInit < ChannelState::CsRouting);
1041 assert!(ChannelState::CsRouting < ChannelState::CsExecute);
1042 assert!(ChannelState::CsExecute < ChannelState::CsHangup);
1043 assert!(ChannelState::CsHangup < ChannelState::CsReporting);
1044 assert!(ChannelState::CsReporting < ChannelState::CsDestroy);
1045 }
1046
1047 #[allow(clippy::nonminimal_bool)]
1049 #[test]
1050 fn channel_state_teardown_check() {
1051 assert!(ChannelState::CsHangup >= ChannelState::CsHangup);
1052 assert!(ChannelState::CsReporting >= ChannelState::CsHangup);
1053 assert!(ChannelState::CsDestroy >= ChannelState::CsHangup);
1054 assert!(!(ChannelState::CsExecute >= ChannelState::CsHangup));
1055 assert!(!(ChannelState::CsPark >= ChannelState::CsHangup));
1056 }
1057
1058 #[test]
1061 fn call_state_ordering_matches_c_enum() {
1062 assert!(CallState::Down < CallState::Dialing);
1063 assert!(CallState::Dialing < CallState::Ringing);
1064 assert!(CallState::Early < CallState::Active);
1065 assert!(CallState::Active < CallState::Hangup);
1066 }
1067
1068 #[test]
1069 fn test_call_state_display() {
1070 assert_eq!(CallState::Down.to_string(), "DOWN");
1071 assert_eq!(CallState::Dialing.to_string(), "DIALING");
1072 assert_eq!(CallState::Ringing.to_string(), "RINGING");
1073 assert_eq!(CallState::Early.to_string(), "EARLY");
1074 assert_eq!(CallState::Active.to_string(), "ACTIVE");
1075 assert_eq!(CallState::Held.to_string(), "HELD");
1076 assert_eq!(CallState::RingWait.to_string(), "RING_WAIT");
1077 assert_eq!(CallState::Hangup.to_string(), "HANGUP");
1078 assert_eq!(CallState::Unheld.to_string(), "UNHELD");
1079 }
1080
1081 #[test]
1082 fn test_call_state_from_str() {
1083 assert_eq!("DOWN".parse::<CallState>(), Ok(CallState::Down));
1084 assert_eq!("ACTIVE".parse::<CallState>(), Ok(CallState::Active));
1085 assert_eq!("RING_WAIT".parse::<CallState>(), Ok(CallState::RingWait));
1086 assert_eq!("UNHELD".parse::<CallState>(), Ok(CallState::Unheld));
1087 }
1088
1089 #[test]
1090 fn test_call_state_from_str_rejects_wrong_case() {
1091 assert!("down"
1092 .parse::<CallState>()
1093 .is_err());
1094 assert!("Active"
1095 .parse::<CallState>()
1096 .is_err());
1097 }
1098
1099 #[test]
1100 fn test_call_state_from_str_unknown() {
1101 assert!("BOGUS"
1102 .parse::<CallState>()
1103 .is_err());
1104 }
1105
1106 #[test]
1109 fn test_answer_state_display() {
1110 assert_eq!(AnswerState::Hangup.to_string(), "hangup");
1111 assert_eq!(AnswerState::Answered.to_string(), "answered");
1112 assert_eq!(AnswerState::Early.to_string(), "early");
1113 assert_eq!(AnswerState::Ringing.to_string(), "ringing");
1114 }
1115
1116 #[test]
1117 fn test_answer_state_from_str() {
1118 assert_eq!("hangup".parse::<AnswerState>(), Ok(AnswerState::Hangup));
1119 assert_eq!("answered".parse::<AnswerState>(), Ok(AnswerState::Answered));
1120 assert_eq!("early".parse::<AnswerState>(), Ok(AnswerState::Early));
1121 assert_eq!("ringing".parse::<AnswerState>(), Ok(AnswerState::Ringing));
1122 }
1123
1124 #[test]
1125 fn test_answer_state_from_str_rejects_wrong_case() {
1126 assert!("HANGUP"
1127 .parse::<AnswerState>()
1128 .is_err());
1129 assert!("Answered"
1130 .parse::<AnswerState>()
1131 .is_err());
1132 }
1133
1134 #[test]
1135 fn test_answer_state_from_str_unknown() {
1136 assert!("bogus"
1137 .parse::<AnswerState>()
1138 .is_err());
1139 }
1140
1141 #[test]
1144 fn test_call_direction_display() {
1145 assert_eq!(CallDirection::Inbound.to_string(), "inbound");
1146 assert_eq!(CallDirection::Outbound.to_string(), "outbound");
1147 }
1148
1149 #[test]
1150 fn test_call_direction_from_str() {
1151 assert_eq!(
1152 "inbound".parse::<CallDirection>(),
1153 Ok(CallDirection::Inbound)
1154 );
1155 assert_eq!(
1156 "outbound".parse::<CallDirection>(),
1157 Ok(CallDirection::Outbound)
1158 );
1159 }
1160
1161 #[test]
1162 fn test_call_direction_from_str_rejects_wrong_case() {
1163 assert!("INBOUND"
1164 .parse::<CallDirection>()
1165 .is_err());
1166 assert!("Outbound"
1167 .parse::<CallDirection>()
1168 .is_err());
1169 }
1170
1171 #[test]
1172 fn test_call_direction_from_str_unknown() {
1173 assert!("bogus"
1174 .parse::<CallDirection>()
1175 .is_err());
1176 }
1177
1178 #[test]
1181 fn hangup_cause_display() {
1182 assert_eq!(HangupCause::NormalClearing.to_string(), "NORMAL_CLEARING");
1183 assert_eq!(HangupCause::UserBusy.to_string(), "USER_BUSY");
1184 assert_eq!(
1185 HangupCause::OriginatorCancel.to_string(),
1186 "ORIGINATOR_CANCEL"
1187 );
1188 assert_eq!(HangupCause::None.to_string(), "NONE");
1189 }
1190
1191 #[test]
1192 fn hangup_cause_from_str() {
1193 assert_eq!(
1194 "NORMAL_CLEARING"
1195 .parse::<HangupCause>()
1196 .unwrap(),
1197 HangupCause::NormalClearing
1198 );
1199 assert_eq!(
1200 "USER_BUSY"
1201 .parse::<HangupCause>()
1202 .unwrap(),
1203 HangupCause::UserBusy
1204 );
1205 }
1206
1207 #[test]
1208 fn hangup_cause_from_str_rejects_wrong_case() {
1209 assert!("normal_clearing"
1210 .parse::<HangupCause>()
1211 .is_err());
1212 assert!("User_Busy"
1213 .parse::<HangupCause>()
1214 .is_err());
1215 }
1216
1217 #[test]
1218 fn hangup_cause_from_str_unknown() {
1219 assert!("BOGUS_CAUSE"
1220 .parse::<HangupCause>()
1221 .is_err());
1222 }
1223
1224 #[test]
1225 fn hangup_cause_display_round_trip() {
1226 let causes = [
1227 HangupCause::None,
1228 HangupCause::NormalClearing,
1229 HangupCause::UserBusy,
1230 HangupCause::NoAnswer,
1231 HangupCause::OriginatorCancel,
1232 HangupCause::BlindTransfer,
1233 HangupCause::InvalidIdentity,
1234 ];
1235 for cause in causes {
1236 let s = cause.to_string();
1237 let parsed: HangupCause = s
1238 .parse()
1239 .unwrap();
1240 assert_eq!(parsed, cause);
1241 }
1242 }
1243
1244 #[test]
1245 fn hangup_cause_as_number_q850() {
1246 assert_eq!(HangupCause::None.as_number(), 0);
1247 assert_eq!(HangupCause::UnallocatedNumber.as_number(), 1);
1248 assert_eq!(HangupCause::NormalClearing.as_number(), 16);
1249 assert_eq!(HangupCause::UserBusy.as_number(), 17);
1250 assert_eq!(HangupCause::NoAnswer.as_number(), 19);
1251 assert_eq!(HangupCause::CallRejected.as_number(), 21);
1252 assert_eq!(HangupCause::NormalUnspecified.as_number(), 31);
1253 assert_eq!(HangupCause::Interworking.as_number(), 127);
1254 }
1255
1256 #[test]
1257 fn hangup_cause_as_number_freeswitch_extensions() {
1258 assert_eq!(HangupCause::Success.as_number(), 142);
1259 assert_eq!(HangupCause::OriginatorCancel.as_number(), 487);
1260 assert_eq!(HangupCause::Crash.as_number(), 700);
1261 assert_eq!(HangupCause::BlindTransfer.as_number(), 800);
1262 assert_eq!(HangupCause::InvalidIdentity.as_number(), 823);
1263 }
1264
1265 #[test]
1266 fn hangup_cause_from_number_round_trip() {
1267 let codes: &[u16] = &[0, 1, 16, 17, 19, 21, 31, 127, 142, 487, 700, 800, 823];
1268 for &code in codes {
1269 let cause = HangupCause::from_number(code).unwrap();
1270 assert_eq!(cause.as_number(), code);
1271 }
1272 }
1273
1274 #[test]
1275 fn hangup_cause_from_number_unknown() {
1276 assert!(HangupCause::from_number(999).is_none());
1277 assert!(HangupCause::from_number(4).is_none());
1278 }
1279
1280 #[test]
1283 fn from_sip_response_success() {
1284 assert_eq!(
1285 HangupCause::from_sip_response(200),
1286 Some(HangupCause::NormalClearing)
1287 );
1288 }
1289
1290 #[test]
1291 fn from_sip_response_4xx_auth_rejection() {
1292 for code in [401, 402, 403, 407] {
1293 assert_eq!(
1294 HangupCause::from_sip_response(code),
1295 Some(HangupCause::CallRejected),
1296 "SIP {code}"
1297 );
1298 }
1299 }
1300
1301 #[test]
1302 fn from_sip_response_4xx_routing() {
1303 assert_eq!(
1304 HangupCause::from_sip_response(404),
1305 Some(HangupCause::UnallocatedNumber)
1306 );
1307 assert_eq!(
1308 HangupCause::from_sip_response(485),
1309 Some(HangupCause::NoRouteDestination)
1310 );
1311 assert_eq!(
1312 HangupCause::from_sip_response(484),
1313 Some(HangupCause::InvalidNumberFormat)
1314 );
1315 assert_eq!(
1316 HangupCause::from_sip_response(410),
1317 Some(HangupCause::NumberChanged)
1318 );
1319 }
1320
1321 #[test]
1322 fn from_sip_response_4xx_service() {
1323 assert_eq!(
1324 HangupCause::from_sip_response(405),
1325 Some(HangupCause::ServiceUnavailable)
1326 );
1327 for code in [406, 415, 501] {
1328 assert_eq!(
1329 HangupCause::from_sip_response(code),
1330 Some(HangupCause::ServiceNotImplemented),
1331 "SIP {code}"
1332 );
1333 }
1334 }
1335
1336 #[test]
1337 fn from_sip_response_4xx_interworking() {
1338 for code in [413, 414, 416, 420, 421, 423, 505, 513] {
1339 assert_eq!(
1340 HangupCause::from_sip_response(code),
1341 Some(HangupCause::Interworking),
1342 "SIP {code}"
1343 );
1344 }
1345 }
1346
1347 #[test]
1348 fn from_sip_response_4xx_timeout_and_busy() {
1349 assert_eq!(
1350 HangupCause::from_sip_response(408),
1351 Some(HangupCause::RecoveryOnTimerExpire)
1352 );
1353 assert_eq!(
1354 HangupCause::from_sip_response(504),
1355 Some(HangupCause::RecoveryOnTimerExpire)
1356 );
1357 assert_eq!(
1358 HangupCause::from_sip_response(480),
1359 Some(HangupCause::NoUserResponse)
1360 );
1361 assert_eq!(
1362 HangupCause::from_sip_response(486),
1363 Some(HangupCause::UserBusy)
1364 );
1365 assert_eq!(
1366 HangupCause::from_sip_response(487),
1367 Some(HangupCause::OriginatorCancel)
1368 );
1369 }
1370
1371 #[test]
1372 fn from_sip_response_4xx_temporary_failure() {
1373 for code in [400, 481, 500, 503] {
1374 assert_eq!(
1375 HangupCause::from_sip_response(code),
1376 Some(HangupCause::NormalTemporaryFailure),
1377 "SIP {code}"
1378 );
1379 }
1380 }
1381
1382 #[test]
1383 fn from_sip_response_4xx_exchange_routing() {
1384 for code in [482, 483] {
1385 assert_eq!(
1386 HangupCause::from_sip_response(code),
1387 Some(HangupCause::ExchangeRoutingError),
1388 "SIP {code}"
1389 );
1390 }
1391 }
1392
1393 #[test]
1394 fn from_sip_response_4xx_media() {
1395 assert_eq!(
1396 HangupCause::from_sip_response(488),
1397 Some(HangupCause::IncompatibleDestination)
1398 );
1399 assert_eq!(
1400 HangupCause::from_sip_response(606),
1401 Some(HangupCause::IncompatibleDestination)
1402 );
1403 }
1404
1405 #[test]
1406 fn from_sip_response_5xx() {
1407 assert_eq!(
1408 HangupCause::from_sip_response(502),
1409 Some(HangupCause::NetworkOutOfOrder)
1410 );
1411 }
1412
1413 #[test]
1414 fn from_sip_response_6xx() {
1415 assert_eq!(
1416 HangupCause::from_sip_response(600),
1417 Some(HangupCause::UserBusy)
1418 );
1419 assert_eq!(
1420 HangupCause::from_sip_response(603),
1421 Some(HangupCause::CallRejected)
1422 );
1423 assert_eq!(
1424 HangupCause::from_sip_response(604),
1425 Some(HangupCause::NoRouteDestination)
1426 );
1427 assert_eq!(
1428 HangupCause::from_sip_response(607),
1429 Some(HangupCause::Unwanted)
1430 );
1431 assert_eq!(
1432 HangupCause::from_sip_response(608),
1433 Some(HangupCause::CallRejected)
1434 );
1435 }
1436
1437 #[test]
1438 fn from_sip_response_stir_shaken() {
1439 assert_eq!(
1440 HangupCause::from_sip_response(428),
1441 Some(HangupCause::NoIdentity)
1442 );
1443 assert_eq!(
1444 HangupCause::from_sip_response(429),
1445 Some(HangupCause::BadIdentityInfo)
1446 );
1447 assert_eq!(
1448 HangupCause::from_sip_response(437),
1449 Some(HangupCause::UnsupportedCertificate)
1450 );
1451 assert_eq!(
1452 HangupCause::from_sip_response(438),
1453 Some(HangupCause::InvalidIdentity)
1454 );
1455 }
1456
1457 #[test]
1458 fn from_sip_response_unmapped_returns_none() {
1459 for code in [100, 180, 183, 301, 302] {
1461 assert_eq!(
1462 HangupCause::from_sip_response(code),
1463 None,
1464 "SIP {code} should be None"
1465 );
1466 }
1467 for code in [409, 411, 412, 422, 489, 491, 493, 506, 580] {
1469 assert_eq!(
1470 HangupCause::from_sip_response(code),
1471 None,
1472 "SIP {code} should be None"
1473 );
1474 }
1475 }
1476
1477 #[test]
1480 fn caller_timetable_all_fields() {
1481 let mut event = EslEvent::new();
1482 event.set_header("Caller-Profile-Created-Time", "1700000000000000");
1483 event.set_header("Caller-Channel-Created-Time", "1700000001000000");
1484 event.set_header("Caller-Channel-Answered-Time", "1700000005000000");
1485 event.set_header("Caller-Channel-Progress-Time", "1700000002000000");
1486 event.set_header("Caller-Channel-Progress-Media-Time", "1700000003000000");
1487 event.set_header("Caller-Channel-Hangup-Time", "0");
1488 event.set_header("Caller-Channel-Transfer-Time", "0");
1489 event.set_header("Caller-Channel-Resurrect-Time", "0");
1490 event.set_header("Caller-Channel-Bridged-Time", "1700000006000000");
1491 event.set_header("Caller-Channel-Last-Hold", "0");
1492 event.set_header("Caller-Channel-Hold-Accum", "0");
1493
1494 let tt = event
1495 .caller_timetable()
1496 .unwrap()
1497 .expect("should have timetable");
1498 assert_eq!(tt.profile_created, Some(1700000000000000));
1499 assert_eq!(tt.created, Some(1700000001000000));
1500 assert_eq!(tt.answered, Some(1700000005000000));
1501 assert_eq!(tt.progress, Some(1700000002000000));
1502 assert_eq!(tt.progress_media, Some(1700000003000000));
1503 assert_eq!(tt.hungup, Some(0));
1504 assert_eq!(tt.transferred, Some(0));
1505 assert_eq!(tt.resurrected, Some(0));
1506 assert_eq!(tt.bridged, Some(1700000006000000));
1507 assert_eq!(tt.last_hold, Some(0));
1508 assert_eq!(tt.hold_accum, Some(0));
1509 }
1510
1511 #[test]
1512 fn other_leg_timetable() {
1513 let mut event = EslEvent::new();
1514 event.set_header("Other-Leg-Profile-Created-Time", "1700000000000000");
1515 event.set_header("Other-Leg-Channel-Created-Time", "1700000001000000");
1516 event.set_header("Other-Leg-Channel-Answered-Time", "1700000005000000");
1517 event.set_header("Other-Leg-Channel-Progress-Time", "0");
1518 event.set_header("Other-Leg-Channel-Progress-Media-Time", "0");
1519 event.set_header("Other-Leg-Channel-Hangup-Time", "0");
1520 event.set_header("Other-Leg-Channel-Transfer-Time", "0");
1521 event.set_header("Other-Leg-Channel-Resurrect-Time", "0");
1522 event.set_header("Other-Leg-Channel-Bridged-Time", "1700000006000000");
1523 event.set_header("Other-Leg-Channel-Last-Hold", "0");
1524 event.set_header("Other-Leg-Channel-Hold-Accum", "0");
1525
1526 let tt = event
1527 .other_leg_timetable()
1528 .unwrap()
1529 .expect("should have timetable");
1530 assert_eq!(tt.created, Some(1700000001000000));
1531 assert_eq!(tt.bridged, Some(1700000006000000));
1532 }
1533
1534 #[test]
1535 fn timetable_no_headers() {
1536 let event = EslEvent::new();
1537 assert_eq!(
1538 event
1539 .caller_timetable()
1540 .unwrap(),
1541 None
1542 );
1543 assert_eq!(
1544 event
1545 .other_leg_timetable()
1546 .unwrap(),
1547 None
1548 );
1549 }
1550
1551 #[test]
1552 fn timetable_partial_headers() {
1553 let mut event = EslEvent::new();
1554 event.set_header("Caller-Channel-Created-Time", "1700000001000000");
1555
1556 let tt = event
1557 .caller_timetable()
1558 .unwrap()
1559 .expect("at least one field parsed");
1560 assert_eq!(tt.created, Some(1700000001000000));
1561 assert_eq!(tt.answered, None);
1562 assert_eq!(tt.profile_created, None);
1563 }
1564
1565 #[test]
1566 fn timetable_invalid_value_is_error() {
1567 let mut event = EslEvent::new();
1568 event.set_header("Caller-Channel-Created-Time", "not_a_number");
1569
1570 let err = event
1571 .caller_timetable()
1572 .unwrap_err();
1573 assert_eq!(err.header, "Caller-Channel-Created-Time");
1574 assert_eq!(err.value, "not_a_number");
1575 }
1576
1577 #[test]
1578 fn timetable_valid_then_invalid_is_error() {
1579 let mut event = EslEvent::new();
1580 event.set_header("Caller-Profile-Created-Time", "1700000000000000");
1581 event.set_header("Caller-Channel-Created-Time", "garbage");
1582
1583 let err = event
1584 .caller_timetable()
1585 .unwrap_err();
1586 assert_eq!(err.header, "Caller-Channel-Created-Time");
1587 assert_eq!(err.value, "garbage");
1588 }
1589
1590 #[test]
1591 fn timetable_zero_preserved() {
1592 let mut event = EslEvent::new();
1593 event.set_header("Caller-Channel-Hangup-Time", "0");
1594
1595 let tt = event
1596 .caller_timetable()
1597 .unwrap()
1598 .expect("should have timetable");
1599 assert_eq!(tt.hungup, Some(0));
1600 }
1601
1602 #[test]
1603 fn timetable_custom_prefix() {
1604 let mut event = EslEvent::new();
1605 event.set_header("Channel-Channel-Created-Time", "1700000001000000");
1606
1607 let tt = event
1608 .timetable("Channel")
1609 .unwrap()
1610 .expect("custom prefix should work");
1611 assert_eq!(tt.created, Some(1700000001000000));
1612 }
1613}