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