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
58impl fmt::Display for ChannelState {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 let name = match self {
61 Self::CsNew => "CS_NEW",
62 Self::CsInit => "CS_INIT",
63 Self::CsRouting => "CS_ROUTING",
64 Self::CsSoftExecute => "CS_SOFT_EXECUTE",
65 Self::CsExecute => "CS_EXECUTE",
66 Self::CsExchangeMedia => "CS_EXCHANGE_MEDIA",
67 Self::CsPark => "CS_PARK",
68 Self::CsConsumeMedia => "CS_CONSUME_MEDIA",
69 Self::CsHibernate => "CS_HIBERNATE",
70 Self::CsReset => "CS_RESET",
71 Self::CsHangup => "CS_HANGUP",
72 Self::CsReporting => "CS_REPORTING",
73 Self::CsDestroy => "CS_DESTROY",
74 Self::CsNone => "CS_NONE",
75 };
76 f.write_str(name)
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct ParseChannelStateError(pub String);
83
84impl fmt::Display for ParseChannelStateError {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 write!(f, "unknown channel state: {}", self.0)
87 }
88}
89
90impl std::error::Error for ParseChannelStateError {}
91
92impl FromStr for ChannelState {
93 type Err = ParseChannelStateError;
94
95 fn from_str(s: &str) -> Result<Self, Self::Err> {
96 match s {
97 "CS_NEW" => Ok(Self::CsNew),
98 "CS_INIT" => Ok(Self::CsInit),
99 "CS_ROUTING" => Ok(Self::CsRouting),
100 "CS_SOFT_EXECUTE" => Ok(Self::CsSoftExecute),
101 "CS_EXECUTE" => Ok(Self::CsExecute),
102 "CS_EXCHANGE_MEDIA" => Ok(Self::CsExchangeMedia),
103 "CS_PARK" => Ok(Self::CsPark),
104 "CS_CONSUME_MEDIA" => Ok(Self::CsConsumeMedia),
105 "CS_HIBERNATE" => Ok(Self::CsHibernate),
106 "CS_RESET" => Ok(Self::CsReset),
107 "CS_HANGUP" => Ok(Self::CsHangup),
108 "CS_REPORTING" => Ok(Self::CsReporting),
109 "CS_DESTROY" => Ok(Self::CsDestroy),
110 "CS_NONE" => Ok(Self::CsNone),
111 _ => Err(ParseChannelStateError(s.to_string())),
112 }
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
119#[non_exhaustive]
120#[allow(missing_docs)]
121pub enum CallState {
122 Down,
123 Dialing,
124 Ringing,
125 Early,
126 Active,
127 Held,
128 RingWait,
129 Hangup,
130 Unheld,
131}
132
133impl fmt::Display for CallState {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 let name = match self {
136 Self::Down => "DOWN",
137 Self::Dialing => "DIALING",
138 Self::Ringing => "RINGING",
139 Self::Early => "EARLY",
140 Self::Active => "ACTIVE",
141 Self::Held => "HELD",
142 Self::RingWait => "RING_WAIT",
143 Self::Hangup => "HANGUP",
144 Self::Unheld => "UNHELD",
145 };
146 f.write_str(name)
147 }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
152pub struct ParseCallStateError(pub String);
153
154impl fmt::Display for ParseCallStateError {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "unknown call state: {}", self.0)
157 }
158}
159
160impl std::error::Error for ParseCallStateError {}
161
162impl FromStr for CallState {
163 type Err = ParseCallStateError;
164
165 fn from_str(s: &str) -> Result<Self, Self::Err> {
166 match s {
167 "DOWN" => Ok(Self::Down),
168 "DIALING" => Ok(Self::Dialing),
169 "RINGING" => Ok(Self::Ringing),
170 "EARLY" => Ok(Self::Early),
171 "ACTIVE" => Ok(Self::Active),
172 "HELD" => Ok(Self::Held),
173 "RING_WAIT" => Ok(Self::RingWait),
174 "HANGUP" => Ok(Self::Hangup),
175 "UNHELD" => Ok(Self::Unheld),
176 _ => Err(ParseCallStateError(s.to_string())),
177 }
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
184#[non_exhaustive]
185#[allow(missing_docs)]
186pub enum AnswerState {
187 Hangup,
188 Answered,
189 Early,
190 Ringing,
191}
192
193impl fmt::Display for AnswerState {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 let name = match self {
196 Self::Hangup => "hangup",
197 Self::Answered => "answered",
198 Self::Early => "early",
199 Self::Ringing => "ringing",
200 };
201 f.write_str(name)
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Eq)]
207pub struct ParseAnswerStateError(pub String);
208
209impl fmt::Display for ParseAnswerStateError {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "unknown answer state: {}", self.0)
212 }
213}
214
215impl std::error::Error for ParseAnswerStateError {}
216
217impl FromStr for AnswerState {
218 type Err = ParseAnswerStateError;
219
220 fn from_str(s: &str) -> Result<Self, Self::Err> {
221 match s {
222 "hangup" => Ok(Self::Hangup),
223 "answered" => Ok(Self::Answered),
224 "early" => Ok(Self::Early),
225 "ringing" => Ok(Self::Ringing),
226 _ => Err(ParseAnswerStateError(s.to_string())),
227 }
228 }
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
233#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
234#[non_exhaustive]
235#[allow(missing_docs)]
236pub enum CallDirection {
237 Inbound,
238 Outbound,
239}
240
241impl fmt::Display for CallDirection {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 let name = match self {
244 Self::Inbound => "inbound",
245 Self::Outbound => "outbound",
246 };
247 f.write_str(name)
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq)]
253pub struct ParseCallDirectionError(pub String);
254
255impl fmt::Display for ParseCallDirectionError {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 write!(f, "unknown call direction: {}", self.0)
258 }
259}
260
261impl std::error::Error for ParseCallDirectionError {}
262
263impl FromStr for CallDirection {
264 type Err = ParseCallDirectionError;
265
266 fn from_str(s: &str) -> Result<Self, Self::Err> {
267 match s {
268 "inbound" => Ok(Self::Inbound),
269 "outbound" => Ok(Self::Outbound),
270 _ => Err(ParseCallDirectionError(s.to_string())),
271 }
272 }
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
277pub struct ParseHangupCauseError(pub String);
278
279impl fmt::Display for ParseHangupCauseError {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 write!(f, "unknown hangup cause: {}", self.0)
282 }
283}
284
285impl std::error::Error for ParseHangupCauseError {}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
294#[non_exhaustive]
295#[repr(u16)]
296#[allow(missing_docs)]
297pub enum HangupCause {
298 None = 0,
299 UnallocatedNumber = 1,
300 NoRouteTransitNet = 2,
301 NoRouteDestination = 3,
302 ChannelUnacceptable = 6,
303 CallAwardedDelivered = 7,
304 NormalClearing = 16,
305 UserBusy = 17,
306 NoUserResponse = 18,
307 NoAnswer = 19,
308 SubscriberAbsent = 20,
309 CallRejected = 21,
310 NumberChanged = 22,
311 RedirectionToNewDestination = 23,
312 ExchangeRoutingError = 25,
313 DestinationOutOfOrder = 27,
314 InvalidNumberFormat = 28,
315 FacilityRejected = 29,
316 ResponseToStatusEnquiry = 30,
317 NormalUnspecified = 31,
318 NormalCircuitCongestion = 34,
319 NetworkOutOfOrder = 38,
320 NormalTemporaryFailure = 41,
321 SwitchCongestion = 42,
322 AccessInfoDiscarded = 43,
323 RequestedChanUnavail = 44,
324 PreEmpted = 45,
325 FacilityNotSubscribed = 50,
326 OutgoingCallBarred = 52,
327 IncomingCallBarred = 54,
328 BearercapabilityNotauth = 57,
329 BearercapabilityNotavail = 58,
330 ServiceUnavailable = 63,
331 BearercapabilityNotimpl = 65,
332 ChanNotImplemented = 66,
333 FacilityNotImplemented = 69,
334 ServiceNotImplemented = 79,
335 InvalidCallReference = 81,
336 IncompatibleDestination = 88,
337 InvalidMsgUnspecified = 95,
338 MandatoryIeMissing = 96,
339 MessageTypeNonexist = 97,
340 WrongMessage = 98,
341 IeNonexist = 99,
342 InvalidIeContents = 100,
343 WrongCallState = 101,
344 RecoveryOnTimerExpire = 102,
345 MandatoryIeLengthError = 103,
346 ProtocolError = 111,
347 Interworking = 127,
348 Success = 142,
349 OriginatorCancel = 487,
350 Crash = 700,
351 SystemShutdown = 701,
352 LoseRace = 702,
353 ManagerRequest = 703,
354 BlindTransfer = 800,
355 AttendedTransfer = 801,
356 AllottedTimeout = 802,
357 UserChallenge = 803,
358 MediaTimeout = 804,
359 PickedOff = 805,
360 UserNotRegistered = 806,
361 ProgressTimeout = 807,
362 InvalidGateway = 808,
363 GatewayDown = 809,
364 InvalidUrl = 810,
365 InvalidProfile = 811,
366 NoPickup = 812,
367 SrtpReadError = 813,
368 Bowout = 814,
369 BusyEverywhere = 815,
370 Decline = 816,
371 DoesNotExistAnywhere = 817,
372 NotAcceptable = 818,
373 Unwanted = 819,
374 NoIdentity = 820,
375 BadIdentityInfo = 821,
376 UnsupportedCertificate = 822,
377 InvalidIdentity = 823,
378 StaleDate = 824,
380 RejectAll = 825,
382}
383
384impl HangupCause {
385 pub fn as_number(&self) -> u16 {
387 *self as u16
388 }
389
390 pub fn from_number(n: u16) -> Option<Self> {
392 match n {
393 0 => Some(Self::None),
394 1 => Some(Self::UnallocatedNumber),
395 2 => Some(Self::NoRouteTransitNet),
396 3 => Some(Self::NoRouteDestination),
397 6 => Some(Self::ChannelUnacceptable),
398 7 => Some(Self::CallAwardedDelivered),
399 16 => Some(Self::NormalClearing),
400 17 => Some(Self::UserBusy),
401 18 => Some(Self::NoUserResponse),
402 19 => Some(Self::NoAnswer),
403 20 => Some(Self::SubscriberAbsent),
404 21 => Some(Self::CallRejected),
405 22 => Some(Self::NumberChanged),
406 23 => Some(Self::RedirectionToNewDestination),
407 25 => Some(Self::ExchangeRoutingError),
408 27 => Some(Self::DestinationOutOfOrder),
409 28 => Some(Self::InvalidNumberFormat),
410 29 => Some(Self::FacilityRejected),
411 30 => Some(Self::ResponseToStatusEnquiry),
412 31 => Some(Self::NormalUnspecified),
413 34 => Some(Self::NormalCircuitCongestion),
414 38 => Some(Self::NetworkOutOfOrder),
415 41 => Some(Self::NormalTemporaryFailure),
416 42 => Some(Self::SwitchCongestion),
417 43 => Some(Self::AccessInfoDiscarded),
418 44 => Some(Self::RequestedChanUnavail),
419 45 => Some(Self::PreEmpted),
420 50 => Some(Self::FacilityNotSubscribed),
421 52 => Some(Self::OutgoingCallBarred),
422 54 => Some(Self::IncomingCallBarred),
423 57 => Some(Self::BearercapabilityNotauth),
424 58 => Some(Self::BearercapabilityNotavail),
425 63 => Some(Self::ServiceUnavailable),
426 65 => Some(Self::BearercapabilityNotimpl),
427 66 => Some(Self::ChanNotImplemented),
428 69 => Some(Self::FacilityNotImplemented),
429 79 => Some(Self::ServiceNotImplemented),
430 81 => Some(Self::InvalidCallReference),
431 88 => Some(Self::IncompatibleDestination),
432 95 => Some(Self::InvalidMsgUnspecified),
433 96 => Some(Self::MandatoryIeMissing),
434 97 => Some(Self::MessageTypeNonexist),
435 98 => Some(Self::WrongMessage),
436 99 => Some(Self::IeNonexist),
437 100 => Some(Self::InvalidIeContents),
438 101 => Some(Self::WrongCallState),
439 102 => Some(Self::RecoveryOnTimerExpire),
440 103 => Some(Self::MandatoryIeLengthError),
441 111 => Some(Self::ProtocolError),
442 127 => Some(Self::Interworking),
443 142 => Some(Self::Success),
444 487 => Some(Self::OriginatorCancel),
445 700 => Some(Self::Crash),
446 701 => Some(Self::SystemShutdown),
447 702 => Some(Self::LoseRace),
448 703 => Some(Self::ManagerRequest),
449 800 => Some(Self::BlindTransfer),
450 801 => Some(Self::AttendedTransfer),
451 802 => Some(Self::AllottedTimeout),
452 803 => Some(Self::UserChallenge),
453 804 => Some(Self::MediaTimeout),
454 805 => Some(Self::PickedOff),
455 806 => Some(Self::UserNotRegistered),
456 807 => Some(Self::ProgressTimeout),
457 808 => Some(Self::InvalidGateway),
458 809 => Some(Self::GatewayDown),
459 810 => Some(Self::InvalidUrl),
460 811 => Some(Self::InvalidProfile),
461 812 => Some(Self::NoPickup),
462 813 => Some(Self::SrtpReadError),
463 814 => Some(Self::Bowout),
464 815 => Some(Self::BusyEverywhere),
465 816 => Some(Self::Decline),
466 817 => Some(Self::DoesNotExistAnywhere),
467 818 => Some(Self::NotAcceptable),
468 819 => Some(Self::Unwanted),
469 820 => Some(Self::NoIdentity),
470 821 => Some(Self::BadIdentityInfo),
471 822 => Some(Self::UnsupportedCertificate),
472 823 => Some(Self::InvalidIdentity),
473 824 => Some(Self::StaleDate),
474 825 => Some(Self::RejectAll),
475 _ => None,
476 }
477 }
478}
479
480impl fmt::Display for HangupCause {
481 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 let name = match self {
483 Self::None => "NONE",
484 Self::UnallocatedNumber => "UNALLOCATED_NUMBER",
485 Self::NoRouteTransitNet => "NO_ROUTE_TRANSIT_NET",
486 Self::NoRouteDestination => "NO_ROUTE_DESTINATION",
487 Self::ChannelUnacceptable => "CHANNEL_UNACCEPTABLE",
488 Self::CallAwardedDelivered => "CALL_AWARDED_DELIVERED",
489 Self::NormalClearing => "NORMAL_CLEARING",
490 Self::UserBusy => "USER_BUSY",
491 Self::NoUserResponse => "NO_USER_RESPONSE",
492 Self::NoAnswer => "NO_ANSWER",
493 Self::SubscriberAbsent => "SUBSCRIBER_ABSENT",
494 Self::CallRejected => "CALL_REJECTED",
495 Self::NumberChanged => "NUMBER_CHANGED",
496 Self::RedirectionToNewDestination => "REDIRECTION_TO_NEW_DESTINATION",
497 Self::ExchangeRoutingError => "EXCHANGE_ROUTING_ERROR",
498 Self::DestinationOutOfOrder => "DESTINATION_OUT_OF_ORDER",
499 Self::InvalidNumberFormat => "INVALID_NUMBER_FORMAT",
500 Self::FacilityRejected => "FACILITY_REJECTED",
501 Self::ResponseToStatusEnquiry => "RESPONSE_TO_STATUS_ENQUIRY",
502 Self::NormalUnspecified => "NORMAL_UNSPECIFIED",
503 Self::NormalCircuitCongestion => "NORMAL_CIRCUIT_CONGESTION",
504 Self::NetworkOutOfOrder => "NETWORK_OUT_OF_ORDER",
505 Self::NormalTemporaryFailure => "NORMAL_TEMPORARY_FAILURE",
506 Self::SwitchCongestion => "SWITCH_CONGESTION",
507 Self::AccessInfoDiscarded => "ACCESS_INFO_DISCARDED",
508 Self::RequestedChanUnavail => "REQUESTED_CHAN_UNAVAIL",
509 Self::PreEmpted => "PRE_EMPTED",
510 Self::FacilityNotSubscribed => "FACILITY_NOT_SUBSCRIBED",
511 Self::OutgoingCallBarred => "OUTGOING_CALL_BARRED",
512 Self::IncomingCallBarred => "INCOMING_CALL_BARRED",
513 Self::BearercapabilityNotauth => "BEARERCAPABILITY_NOTAUTH",
514 Self::BearercapabilityNotavail => "BEARERCAPABILITY_NOTAVAIL",
515 Self::ServiceUnavailable => "SERVICE_UNAVAILABLE",
516 Self::BearercapabilityNotimpl => "BEARERCAPABILITY_NOTIMPL",
517 Self::ChanNotImplemented => "CHAN_NOT_IMPLEMENTED",
518 Self::FacilityNotImplemented => "FACILITY_NOT_IMPLEMENTED",
519 Self::ServiceNotImplemented => "SERVICE_NOT_IMPLEMENTED",
520 Self::InvalidCallReference => "INVALID_CALL_REFERENCE",
521 Self::IncompatibleDestination => "INCOMPATIBLE_DESTINATION",
522 Self::InvalidMsgUnspecified => "INVALID_MSG_UNSPECIFIED",
523 Self::MandatoryIeMissing => "MANDATORY_IE_MISSING",
524 Self::MessageTypeNonexist => "MESSAGE_TYPE_NONEXIST",
525 Self::WrongMessage => "WRONG_MESSAGE",
526 Self::IeNonexist => "IE_NONEXIST",
527 Self::InvalidIeContents => "INVALID_IE_CONTENTS",
528 Self::WrongCallState => "WRONG_CALL_STATE",
529 Self::RecoveryOnTimerExpire => "RECOVERY_ON_TIMER_EXPIRE",
530 Self::MandatoryIeLengthError => "MANDATORY_IE_LENGTH_ERROR",
531 Self::ProtocolError => "PROTOCOL_ERROR",
532 Self::Interworking => "INTERWORKING",
533 Self::Success => "SUCCESS",
534 Self::OriginatorCancel => "ORIGINATOR_CANCEL",
535 Self::Crash => "CRASH",
536 Self::SystemShutdown => "SYSTEM_SHUTDOWN",
537 Self::LoseRace => "LOSE_RACE",
538 Self::ManagerRequest => "MANAGER_REQUEST",
539 Self::BlindTransfer => "BLIND_TRANSFER",
540 Self::AttendedTransfer => "ATTENDED_TRANSFER",
541 Self::AllottedTimeout => "ALLOTTED_TIMEOUT",
542 Self::UserChallenge => "USER_CHALLENGE",
543 Self::MediaTimeout => "MEDIA_TIMEOUT",
544 Self::PickedOff => "PICKED_OFF",
545 Self::UserNotRegistered => "USER_NOT_REGISTERED",
546 Self::ProgressTimeout => "PROGRESS_TIMEOUT",
547 Self::InvalidGateway => "INVALID_GATEWAY",
548 Self::GatewayDown => "GATEWAY_DOWN",
549 Self::InvalidUrl => "INVALID_URL",
550 Self::InvalidProfile => "INVALID_PROFILE",
551 Self::NoPickup => "NO_PICKUP",
552 Self::SrtpReadError => "SRTP_READ_ERROR",
553 Self::Bowout => "BOWOUT",
554 Self::BusyEverywhere => "BUSY_EVERYWHERE",
555 Self::Decline => "DECLINE",
556 Self::DoesNotExistAnywhere => "DOES_NOT_EXIST_ANYWHERE",
557 Self::NotAcceptable => "NOT_ACCEPTABLE",
558 Self::Unwanted => "UNWANTED",
559 Self::NoIdentity => "NO_IDENTITY",
560 Self::BadIdentityInfo => "BAD_IDENTITY_INFO",
561 Self::UnsupportedCertificate => "UNSUPPORTED_CERTIFICATE",
562 Self::InvalidIdentity => "INVALID_IDENTITY",
563 Self::StaleDate => "STALE_DATE",
564 Self::RejectAll => "REJECT_ALL",
565 };
566 f.write_str(name)
567 }
568}
569
570impl FromStr for HangupCause {
571 type Err = ParseHangupCauseError;
572
573 fn from_str(s: &str) -> Result<Self, Self::Err> {
574 Ok(match s {
575 "NONE" => Self::None,
576 "UNALLOCATED_NUMBER" => Self::UnallocatedNumber,
577 "NO_ROUTE_TRANSIT_NET" => Self::NoRouteTransitNet,
578 "NO_ROUTE_DESTINATION" => Self::NoRouteDestination,
579 "CHANNEL_UNACCEPTABLE" => Self::ChannelUnacceptable,
580 "CALL_AWARDED_DELIVERED" => Self::CallAwardedDelivered,
581 "NORMAL_CLEARING" => Self::NormalClearing,
582 "USER_BUSY" => Self::UserBusy,
583 "NO_USER_RESPONSE" => Self::NoUserResponse,
584 "NO_ANSWER" => Self::NoAnswer,
585 "SUBSCRIBER_ABSENT" => Self::SubscriberAbsent,
586 "CALL_REJECTED" => Self::CallRejected,
587 "NUMBER_CHANGED" => Self::NumberChanged,
588 "REDIRECTION_TO_NEW_DESTINATION" => Self::RedirectionToNewDestination,
589 "EXCHANGE_ROUTING_ERROR" => Self::ExchangeRoutingError,
590 "DESTINATION_OUT_OF_ORDER" => Self::DestinationOutOfOrder,
591 "INVALID_NUMBER_FORMAT" => Self::InvalidNumberFormat,
592 "FACILITY_REJECTED" => Self::FacilityRejected,
593 "RESPONSE_TO_STATUS_ENQUIRY" => Self::ResponseToStatusEnquiry,
594 "NORMAL_UNSPECIFIED" => Self::NormalUnspecified,
595 "NORMAL_CIRCUIT_CONGESTION" => Self::NormalCircuitCongestion,
596 "NETWORK_OUT_OF_ORDER" => Self::NetworkOutOfOrder,
597 "NORMAL_TEMPORARY_FAILURE" => Self::NormalTemporaryFailure,
598 "SWITCH_CONGESTION" => Self::SwitchCongestion,
599 "ACCESS_INFO_DISCARDED" => Self::AccessInfoDiscarded,
600 "REQUESTED_CHAN_UNAVAIL" => Self::RequestedChanUnavail,
601 "PRE_EMPTED" => Self::PreEmpted,
602 "FACILITY_NOT_SUBSCRIBED" => Self::FacilityNotSubscribed,
603 "OUTGOING_CALL_BARRED" => Self::OutgoingCallBarred,
604 "INCOMING_CALL_BARRED" => Self::IncomingCallBarred,
605 "BEARERCAPABILITY_NOTAUTH" => Self::BearercapabilityNotauth,
606 "BEARERCAPABILITY_NOTAVAIL" => Self::BearercapabilityNotavail,
607 "SERVICE_UNAVAILABLE" => Self::ServiceUnavailable,
608 "BEARERCAPABILITY_NOTIMPL" => Self::BearercapabilityNotimpl,
609 "CHAN_NOT_IMPLEMENTED" => Self::ChanNotImplemented,
610 "FACILITY_NOT_IMPLEMENTED" => Self::FacilityNotImplemented,
611 "SERVICE_NOT_IMPLEMENTED" => Self::ServiceNotImplemented,
612 "INVALID_CALL_REFERENCE" => Self::InvalidCallReference,
613 "INCOMPATIBLE_DESTINATION" => Self::IncompatibleDestination,
614 "INVALID_MSG_UNSPECIFIED" => Self::InvalidMsgUnspecified,
615 "MANDATORY_IE_MISSING" => Self::MandatoryIeMissing,
616 "MESSAGE_TYPE_NONEXIST" => Self::MessageTypeNonexist,
617 "WRONG_MESSAGE" => Self::WrongMessage,
618 "IE_NONEXIST" => Self::IeNonexist,
619 "INVALID_IE_CONTENTS" => Self::InvalidIeContents,
620 "WRONG_CALL_STATE" => Self::WrongCallState,
621 "RECOVERY_ON_TIMER_EXPIRE" => Self::RecoveryOnTimerExpire,
622 "MANDATORY_IE_LENGTH_ERROR" => Self::MandatoryIeLengthError,
623 "PROTOCOL_ERROR" => Self::ProtocolError,
624 "INTERWORKING" => Self::Interworking,
625 "SUCCESS" => Self::Success,
626 "ORIGINATOR_CANCEL" => Self::OriginatorCancel,
627 "CRASH" => Self::Crash,
628 "SYSTEM_SHUTDOWN" => Self::SystemShutdown,
629 "LOSE_RACE" => Self::LoseRace,
630 "MANAGER_REQUEST" => Self::ManagerRequest,
631 "BLIND_TRANSFER" => Self::BlindTransfer,
632 "ATTENDED_TRANSFER" => Self::AttendedTransfer,
633 "ALLOTTED_TIMEOUT" => Self::AllottedTimeout,
634 "USER_CHALLENGE" => Self::UserChallenge,
635 "MEDIA_TIMEOUT" => Self::MediaTimeout,
636 "PICKED_OFF" => Self::PickedOff,
637 "USER_NOT_REGISTERED" => Self::UserNotRegistered,
638 "PROGRESS_TIMEOUT" => Self::ProgressTimeout,
639 "INVALID_GATEWAY" => Self::InvalidGateway,
640 "GATEWAY_DOWN" => Self::GatewayDown,
641 "INVALID_URL" => Self::InvalidUrl,
642 "INVALID_PROFILE" => Self::InvalidProfile,
643 "NO_PICKUP" => Self::NoPickup,
644 "SRTP_READ_ERROR" => Self::SrtpReadError,
645 "BOWOUT" => Self::Bowout,
646 "BUSY_EVERYWHERE" => Self::BusyEverywhere,
647 "DECLINE" => Self::Decline,
648 "DOES_NOT_EXIST_ANYWHERE" => Self::DoesNotExistAnywhere,
649 "NOT_ACCEPTABLE" => Self::NotAcceptable,
650 "UNWANTED" => Self::Unwanted,
651 "NO_IDENTITY" => Self::NoIdentity,
652 "BAD_IDENTITY_INFO" => Self::BadIdentityInfo,
653 "UNSUPPORTED_CERTIFICATE" => Self::UnsupportedCertificate,
654 "INVALID_IDENTITY" => Self::InvalidIdentity,
655 "STALE_DATE" => Self::StaleDate,
656 "REJECT_ALL" => Self::RejectAll,
657 _ => return Err(ParseHangupCauseError(s.to_string())),
658 })
659 }
660}
661
662#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
683#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
684#[non_exhaustive]
685pub struct ChannelTimetable {
686 pub profile_created: Option<i64>,
688 pub created: Option<i64>,
690 pub answered: Option<i64>,
692 pub progress: Option<i64>,
694 pub progress_media: Option<i64>,
696 pub hungup: Option<i64>,
698 pub transferred: Option<i64>,
700 pub resurrected: Option<i64>,
702 pub bridged: Option<i64>,
704 pub last_hold: Option<i64>,
706 pub hold_accum: Option<i64>,
708}
709
710#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
716#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
717#[non_exhaustive]
718pub enum TimetablePrefix {
719 Caller,
721 OtherLeg,
723 Channel,
725 Hunt,
727 Originator,
729 Originatee,
731 PostOriginator,
733 PostOriginatee,
735}
736
737impl TimetablePrefix {
738 pub fn as_str(&self) -> &'static str {
740 match self {
741 Self::Caller => "Caller",
742 Self::OtherLeg => "Other-Leg",
743 Self::Channel => "Channel",
744 Self::Hunt => "Hunt",
745 Self::Originator => "ORIGINATOR",
746 Self::Originatee => "ORIGINATEE",
747 Self::PostOriginator => "POST-ORIGINATOR",
748 Self::PostOriginatee => "POST-ORIGINATEE",
749 }
750 }
751}
752
753impl fmt::Display for TimetablePrefix {
754 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
755 f.write_str(self.as_str())
756 }
757}
758
759impl AsRef<str> for TimetablePrefix {
760 fn as_ref(&self) -> &str {
761 self.as_str()
762 }
763}
764
765#[derive(Debug, Clone, PartialEq, Eq)]
767#[non_exhaustive]
768pub struct ParseTimetableError {
769 pub header: String,
771 pub value: String,
773}
774
775impl fmt::Display for ParseTimetableError {
776 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777 write!(
778 f,
779 "invalid timetable value for {}: {:?}",
780 self.header, self.value
781 )
782 }
783}
784
785impl std::error::Error for ParseTimetableError {}
786
787impl ParseTimetableError {
788 pub fn new(header: impl Into<String>, value: impl Into<String>) -> Self {
790 Self {
791 header: header.into(),
792 value: value.into(),
793 }
794 }
795}
796
797impl ChannelTimetable {
798 pub const SUFFIXES: &'static [&'static str] = &[
814 "Profile-Created-Time",
815 "Channel-Created-Time",
816 "Channel-Answered-Time",
817 "Channel-Progress-Time",
818 "Channel-Progress-Media-Time",
819 "Channel-Hangup-Time",
820 "Channel-Transfer-Time",
821 "Channel-Resurrect-Time",
822 "Channel-Bridged-Time",
823 "Channel-Last-Hold",
824 "Channel-Hold-Accum",
825 ];
826
827 pub fn from_lookup<'a>(
852 prefix: impl AsRef<str>,
853 lookup: impl Fn(&str) -> Option<&'a str>,
854 ) -> Result<Option<Self>, ParseTimetableError> {
855 let prefix = prefix.as_ref();
856 let mut tt = Self::default();
857 let mut found = false;
858
859 macro_rules! field {
860 ($field:ident, $suffix:literal) => {
861 let header = format!("{}-{}", prefix, $suffix);
862 if let Some(raw) = lookup(&header) {
863 let v: i64 = raw
864 .parse()
865 .map_err(|_| ParseTimetableError {
866 header: header.clone(),
867 value: raw.to_string(),
868 })?;
869 tt.$field = Some(v);
870 found = true;
871 }
872 };
873 }
874
875 field!(profile_created, "Profile-Created-Time");
876 field!(created, "Channel-Created-Time");
877 field!(answered, "Channel-Answered-Time");
878 field!(progress, "Channel-Progress-Time");
879 field!(progress_media, "Channel-Progress-Media-Time");
880 field!(hungup, "Channel-Hangup-Time");
881 field!(transferred, "Channel-Transfer-Time");
882 field!(resurrected, "Channel-Resurrect-Time");
883 field!(bridged, "Channel-Bridged-Time");
884 field!(last_hold, "Channel-Last-Hold");
885 field!(hold_accum, "Channel-Hold-Accum");
886
887 if found {
888 Ok(Some(tt))
889 } else {
890 Ok(None)
891 }
892 }
893}
894
895#[cfg(test)]
896mod tests {
897 use super::*;
898 use crate::event::EslEvent;
899 use crate::lookup::HeaderLookup;
900
901 #[test]
904 fn test_channel_state_display() {
905 assert_eq!(ChannelState::CsNew.to_string(), "CS_NEW");
906 assert_eq!(ChannelState::CsInit.to_string(), "CS_INIT");
907 assert_eq!(ChannelState::CsRouting.to_string(), "CS_ROUTING");
908 assert_eq!(ChannelState::CsSoftExecute.to_string(), "CS_SOFT_EXECUTE");
909 assert_eq!(ChannelState::CsExecute.to_string(), "CS_EXECUTE");
910 assert_eq!(
911 ChannelState::CsExchangeMedia.to_string(),
912 "CS_EXCHANGE_MEDIA"
913 );
914 assert_eq!(ChannelState::CsPark.to_string(), "CS_PARK");
915 assert_eq!(ChannelState::CsConsumeMedia.to_string(), "CS_CONSUME_MEDIA");
916 assert_eq!(ChannelState::CsHibernate.to_string(), "CS_HIBERNATE");
917 assert_eq!(ChannelState::CsReset.to_string(), "CS_RESET");
918 assert_eq!(ChannelState::CsHangup.to_string(), "CS_HANGUP");
919 assert_eq!(ChannelState::CsReporting.to_string(), "CS_REPORTING");
920 assert_eq!(ChannelState::CsDestroy.to_string(), "CS_DESTROY");
921 assert_eq!(ChannelState::CsNone.to_string(), "CS_NONE");
922 }
923
924 #[test]
925 fn test_channel_state_from_str() {
926 assert_eq!("CS_NEW".parse::<ChannelState>(), Ok(ChannelState::CsNew));
927 assert_eq!(
928 "CS_EXECUTE".parse::<ChannelState>(),
929 Ok(ChannelState::CsExecute)
930 );
931 assert_eq!(
932 "CS_HANGUP".parse::<ChannelState>(),
933 Ok(ChannelState::CsHangup)
934 );
935 assert_eq!(
936 "CS_DESTROY".parse::<ChannelState>(),
937 Ok(ChannelState::CsDestroy)
938 );
939 }
940
941 #[test]
942 fn test_channel_state_from_str_rejects_wrong_case() {
943 assert!("cs_new"
944 .parse::<ChannelState>()
945 .is_err());
946 assert!("Cs_Routing"
947 .parse::<ChannelState>()
948 .is_err());
949 }
950
951 #[test]
952 fn test_channel_state_from_str_unknown() {
953 assert!("CS_BOGUS"
954 .parse::<ChannelState>()
955 .is_err());
956 assert!(""
957 .parse::<ChannelState>()
958 .is_err());
959 }
960
961 #[test]
962 fn test_channel_state_from_number() {
963 assert_eq!(ChannelState::from_number(0), Some(ChannelState::CsNew));
964 assert_eq!(ChannelState::from_number(4), Some(ChannelState::CsExecute));
965 assert_eq!(ChannelState::from_number(10), Some(ChannelState::CsHangup));
966 assert_eq!(ChannelState::from_number(13), Some(ChannelState::CsNone));
967 assert_eq!(ChannelState::from_number(14), None);
968 assert_eq!(ChannelState::from_number(255), None);
969 }
970
971 #[test]
972 fn test_channel_state_as_number() {
973 assert_eq!(ChannelState::CsNew.as_number(), 0);
974 assert_eq!(ChannelState::CsExecute.as_number(), 4);
975 assert_eq!(ChannelState::CsHangup.as_number(), 10);
976 assert_eq!(ChannelState::CsNone.as_number(), 13);
977 }
978
979 #[test]
980 fn channel_state_ordering_follows_lifecycle() {
981 assert!(ChannelState::CsNew < ChannelState::CsInit);
982 assert!(ChannelState::CsInit < ChannelState::CsRouting);
983 assert!(ChannelState::CsRouting < ChannelState::CsExecute);
984 assert!(ChannelState::CsExecute < ChannelState::CsHangup);
985 assert!(ChannelState::CsHangup < ChannelState::CsReporting);
986 assert!(ChannelState::CsReporting < ChannelState::CsDestroy);
987 }
988
989 #[allow(clippy::nonminimal_bool)]
991 #[test]
992 fn channel_state_teardown_check() {
993 assert!(ChannelState::CsHangup >= ChannelState::CsHangup);
994 assert!(ChannelState::CsReporting >= ChannelState::CsHangup);
995 assert!(ChannelState::CsDestroy >= ChannelState::CsHangup);
996 assert!(!(ChannelState::CsExecute >= ChannelState::CsHangup));
997 assert!(!(ChannelState::CsPark >= ChannelState::CsHangup));
998 }
999
1000 #[test]
1003 fn call_state_ordering_matches_c_enum() {
1004 assert!(CallState::Down < CallState::Dialing);
1005 assert!(CallState::Dialing < CallState::Ringing);
1006 assert!(CallState::Early < CallState::Active);
1007 assert!(CallState::Active < CallState::Hangup);
1008 }
1009
1010 #[test]
1011 fn test_call_state_display() {
1012 assert_eq!(CallState::Down.to_string(), "DOWN");
1013 assert_eq!(CallState::Dialing.to_string(), "DIALING");
1014 assert_eq!(CallState::Ringing.to_string(), "RINGING");
1015 assert_eq!(CallState::Early.to_string(), "EARLY");
1016 assert_eq!(CallState::Active.to_string(), "ACTIVE");
1017 assert_eq!(CallState::Held.to_string(), "HELD");
1018 assert_eq!(CallState::RingWait.to_string(), "RING_WAIT");
1019 assert_eq!(CallState::Hangup.to_string(), "HANGUP");
1020 assert_eq!(CallState::Unheld.to_string(), "UNHELD");
1021 }
1022
1023 #[test]
1024 fn test_call_state_from_str() {
1025 assert_eq!("DOWN".parse::<CallState>(), Ok(CallState::Down));
1026 assert_eq!("ACTIVE".parse::<CallState>(), Ok(CallState::Active));
1027 assert_eq!("RING_WAIT".parse::<CallState>(), Ok(CallState::RingWait));
1028 assert_eq!("UNHELD".parse::<CallState>(), Ok(CallState::Unheld));
1029 }
1030
1031 #[test]
1032 fn test_call_state_from_str_rejects_wrong_case() {
1033 assert!("down"
1034 .parse::<CallState>()
1035 .is_err());
1036 assert!("Active"
1037 .parse::<CallState>()
1038 .is_err());
1039 }
1040
1041 #[test]
1042 fn test_call_state_from_str_unknown() {
1043 assert!("BOGUS"
1044 .parse::<CallState>()
1045 .is_err());
1046 }
1047
1048 #[test]
1051 fn test_answer_state_display() {
1052 assert_eq!(AnswerState::Hangup.to_string(), "hangup");
1053 assert_eq!(AnswerState::Answered.to_string(), "answered");
1054 assert_eq!(AnswerState::Early.to_string(), "early");
1055 assert_eq!(AnswerState::Ringing.to_string(), "ringing");
1056 }
1057
1058 #[test]
1059 fn test_answer_state_from_str() {
1060 assert_eq!("hangup".parse::<AnswerState>(), Ok(AnswerState::Hangup));
1061 assert_eq!("answered".parse::<AnswerState>(), Ok(AnswerState::Answered));
1062 assert_eq!("early".parse::<AnswerState>(), Ok(AnswerState::Early));
1063 assert_eq!("ringing".parse::<AnswerState>(), Ok(AnswerState::Ringing));
1064 }
1065
1066 #[test]
1067 fn test_answer_state_from_str_rejects_wrong_case() {
1068 assert!("HANGUP"
1069 .parse::<AnswerState>()
1070 .is_err());
1071 assert!("Answered"
1072 .parse::<AnswerState>()
1073 .is_err());
1074 }
1075
1076 #[test]
1077 fn test_answer_state_from_str_unknown() {
1078 assert!("bogus"
1079 .parse::<AnswerState>()
1080 .is_err());
1081 }
1082
1083 #[test]
1086 fn test_call_direction_display() {
1087 assert_eq!(CallDirection::Inbound.to_string(), "inbound");
1088 assert_eq!(CallDirection::Outbound.to_string(), "outbound");
1089 }
1090
1091 #[test]
1092 fn test_call_direction_from_str() {
1093 assert_eq!(
1094 "inbound".parse::<CallDirection>(),
1095 Ok(CallDirection::Inbound)
1096 );
1097 assert_eq!(
1098 "outbound".parse::<CallDirection>(),
1099 Ok(CallDirection::Outbound)
1100 );
1101 }
1102
1103 #[test]
1104 fn test_call_direction_from_str_rejects_wrong_case() {
1105 assert!("INBOUND"
1106 .parse::<CallDirection>()
1107 .is_err());
1108 assert!("Outbound"
1109 .parse::<CallDirection>()
1110 .is_err());
1111 }
1112
1113 #[test]
1114 fn test_call_direction_from_str_unknown() {
1115 assert!("bogus"
1116 .parse::<CallDirection>()
1117 .is_err());
1118 }
1119
1120 #[test]
1123 fn hangup_cause_display() {
1124 assert_eq!(HangupCause::NormalClearing.to_string(), "NORMAL_CLEARING");
1125 assert_eq!(HangupCause::UserBusy.to_string(), "USER_BUSY");
1126 assert_eq!(
1127 HangupCause::OriginatorCancel.to_string(),
1128 "ORIGINATOR_CANCEL"
1129 );
1130 assert_eq!(HangupCause::None.to_string(), "NONE");
1131 }
1132
1133 #[test]
1134 fn hangup_cause_from_str() {
1135 assert_eq!(
1136 "NORMAL_CLEARING"
1137 .parse::<HangupCause>()
1138 .unwrap(),
1139 HangupCause::NormalClearing
1140 );
1141 assert_eq!(
1142 "USER_BUSY"
1143 .parse::<HangupCause>()
1144 .unwrap(),
1145 HangupCause::UserBusy
1146 );
1147 }
1148
1149 #[test]
1150 fn hangup_cause_from_str_rejects_wrong_case() {
1151 assert!("normal_clearing"
1152 .parse::<HangupCause>()
1153 .is_err());
1154 assert!("User_Busy"
1155 .parse::<HangupCause>()
1156 .is_err());
1157 }
1158
1159 #[test]
1160 fn hangup_cause_from_str_unknown() {
1161 assert!("BOGUS_CAUSE"
1162 .parse::<HangupCause>()
1163 .is_err());
1164 }
1165
1166 #[test]
1167 fn hangup_cause_display_round_trip() {
1168 let causes = [
1169 HangupCause::None,
1170 HangupCause::NormalClearing,
1171 HangupCause::UserBusy,
1172 HangupCause::NoAnswer,
1173 HangupCause::OriginatorCancel,
1174 HangupCause::BlindTransfer,
1175 HangupCause::InvalidIdentity,
1176 ];
1177 for cause in causes {
1178 let s = cause.to_string();
1179 let parsed: HangupCause = s
1180 .parse()
1181 .unwrap();
1182 assert_eq!(parsed, cause);
1183 }
1184 }
1185
1186 #[test]
1187 fn hangup_cause_as_number_q850() {
1188 assert_eq!(HangupCause::None.as_number(), 0);
1189 assert_eq!(HangupCause::UnallocatedNumber.as_number(), 1);
1190 assert_eq!(HangupCause::NormalClearing.as_number(), 16);
1191 assert_eq!(HangupCause::UserBusy.as_number(), 17);
1192 assert_eq!(HangupCause::NoAnswer.as_number(), 19);
1193 assert_eq!(HangupCause::CallRejected.as_number(), 21);
1194 assert_eq!(HangupCause::NormalUnspecified.as_number(), 31);
1195 assert_eq!(HangupCause::Interworking.as_number(), 127);
1196 }
1197
1198 #[test]
1199 fn hangup_cause_as_number_freeswitch_extensions() {
1200 assert_eq!(HangupCause::Success.as_number(), 142);
1201 assert_eq!(HangupCause::OriginatorCancel.as_number(), 487);
1202 assert_eq!(HangupCause::Crash.as_number(), 700);
1203 assert_eq!(HangupCause::BlindTransfer.as_number(), 800);
1204 assert_eq!(HangupCause::InvalidIdentity.as_number(), 823);
1205 }
1206
1207 #[test]
1208 fn hangup_cause_from_number_round_trip() {
1209 let codes: &[u16] = &[0, 1, 16, 17, 19, 21, 31, 127, 142, 487, 700, 800, 823];
1210 for &code in codes {
1211 let cause = HangupCause::from_number(code).unwrap();
1212 assert_eq!(cause.as_number(), code);
1213 }
1214 }
1215
1216 #[test]
1217 fn hangup_cause_from_number_unknown() {
1218 assert!(HangupCause::from_number(999).is_none());
1219 assert!(HangupCause::from_number(4).is_none());
1220 }
1221
1222 #[test]
1225 fn caller_timetable_all_fields() {
1226 let mut event = EslEvent::new();
1227 event.set_header("Caller-Profile-Created-Time", "1700000000000000");
1228 event.set_header("Caller-Channel-Created-Time", "1700000001000000");
1229 event.set_header("Caller-Channel-Answered-Time", "1700000005000000");
1230 event.set_header("Caller-Channel-Progress-Time", "1700000002000000");
1231 event.set_header("Caller-Channel-Progress-Media-Time", "1700000003000000");
1232 event.set_header("Caller-Channel-Hangup-Time", "0");
1233 event.set_header("Caller-Channel-Transfer-Time", "0");
1234 event.set_header("Caller-Channel-Resurrect-Time", "0");
1235 event.set_header("Caller-Channel-Bridged-Time", "1700000006000000");
1236 event.set_header("Caller-Channel-Last-Hold", "0");
1237 event.set_header("Caller-Channel-Hold-Accum", "0");
1238
1239 let tt = event
1240 .caller_timetable()
1241 .unwrap()
1242 .expect("should have timetable");
1243 assert_eq!(tt.profile_created, Some(1700000000000000));
1244 assert_eq!(tt.created, Some(1700000001000000));
1245 assert_eq!(tt.answered, Some(1700000005000000));
1246 assert_eq!(tt.progress, Some(1700000002000000));
1247 assert_eq!(tt.progress_media, Some(1700000003000000));
1248 assert_eq!(tt.hungup, Some(0));
1249 assert_eq!(tt.transferred, Some(0));
1250 assert_eq!(tt.resurrected, Some(0));
1251 assert_eq!(tt.bridged, Some(1700000006000000));
1252 assert_eq!(tt.last_hold, Some(0));
1253 assert_eq!(tt.hold_accum, Some(0));
1254 }
1255
1256 #[test]
1257 fn other_leg_timetable() {
1258 let mut event = EslEvent::new();
1259 event.set_header("Other-Leg-Profile-Created-Time", "1700000000000000");
1260 event.set_header("Other-Leg-Channel-Created-Time", "1700000001000000");
1261 event.set_header("Other-Leg-Channel-Answered-Time", "1700000005000000");
1262 event.set_header("Other-Leg-Channel-Progress-Time", "0");
1263 event.set_header("Other-Leg-Channel-Progress-Media-Time", "0");
1264 event.set_header("Other-Leg-Channel-Hangup-Time", "0");
1265 event.set_header("Other-Leg-Channel-Transfer-Time", "0");
1266 event.set_header("Other-Leg-Channel-Resurrect-Time", "0");
1267 event.set_header("Other-Leg-Channel-Bridged-Time", "1700000006000000");
1268 event.set_header("Other-Leg-Channel-Last-Hold", "0");
1269 event.set_header("Other-Leg-Channel-Hold-Accum", "0");
1270
1271 let tt = event
1272 .other_leg_timetable()
1273 .unwrap()
1274 .expect("should have timetable");
1275 assert_eq!(tt.created, Some(1700000001000000));
1276 assert_eq!(tt.bridged, Some(1700000006000000));
1277 }
1278
1279 #[test]
1280 fn timetable_no_headers() {
1281 let event = EslEvent::new();
1282 assert_eq!(
1283 event
1284 .caller_timetable()
1285 .unwrap(),
1286 None
1287 );
1288 assert_eq!(
1289 event
1290 .other_leg_timetable()
1291 .unwrap(),
1292 None
1293 );
1294 }
1295
1296 #[test]
1297 fn timetable_partial_headers() {
1298 let mut event = EslEvent::new();
1299 event.set_header("Caller-Channel-Created-Time", "1700000001000000");
1300
1301 let tt = event
1302 .caller_timetable()
1303 .unwrap()
1304 .expect("at least one field parsed");
1305 assert_eq!(tt.created, Some(1700000001000000));
1306 assert_eq!(tt.answered, None);
1307 assert_eq!(tt.profile_created, None);
1308 }
1309
1310 #[test]
1311 fn timetable_invalid_value_is_error() {
1312 let mut event = EslEvent::new();
1313 event.set_header("Caller-Channel-Created-Time", "not_a_number");
1314
1315 let err = event
1316 .caller_timetable()
1317 .unwrap_err();
1318 assert_eq!(err.header, "Caller-Channel-Created-Time");
1319 assert_eq!(err.value, "not_a_number");
1320 }
1321
1322 #[test]
1323 fn timetable_valid_then_invalid_is_error() {
1324 let mut event = EslEvent::new();
1325 event.set_header("Caller-Profile-Created-Time", "1700000000000000");
1326 event.set_header("Caller-Channel-Created-Time", "garbage");
1327
1328 let err = event
1329 .caller_timetable()
1330 .unwrap_err();
1331 assert_eq!(err.header, "Caller-Channel-Created-Time");
1332 assert_eq!(err.value, "garbage");
1333 }
1334
1335 #[test]
1336 fn timetable_zero_preserved() {
1337 let mut event = EslEvent::new();
1338 event.set_header("Caller-Channel-Hangup-Time", "0");
1339
1340 let tt = event
1341 .caller_timetable()
1342 .unwrap()
1343 .expect("should have timetable");
1344 assert_eq!(tt.hungup, Some(0));
1345 }
1346
1347 #[test]
1348 fn timetable_custom_prefix() {
1349 let mut event = EslEvent::new();
1350 event.set_header("Channel-Channel-Created-Time", "1700000001000000");
1351
1352 let tt = event
1353 .timetable("Channel")
1354 .unwrap()
1355 .expect("custom prefix should work");
1356 assert_eq!(tt.created, Some(1700000001000000));
1357 }
1358}