1use std::fmt;
2use std::str::FromStr;
3
4macro_rules! esl_event_types {
12 (@filter_channel [$($acc:ident),*]) => { &[$(EslEventType::$acc,)*] };
20 (@filter_channel [$($acc:ident),*] ($v:ident [channel $(,$_g:ident)*]) $($tail:tt)*) => {
21 esl_event_types!(@filter_channel [$($acc,)* $v] $($tail)*)
22 };
23 (@filter_channel [$($acc:ident),*] ($v:ident [$_h:ident $(,$tg:ident)*]) $($tail:tt)*) => {
24 esl_event_types!(@filter_channel [$($acc),*] ($v [$($tg),*]) $($tail)*)
25 };
26 (@filter_channel [$($acc:ident),*] ($v:ident []) $($tail:tt)*) => {
27 esl_event_types!(@filter_channel [$($acc),*] $($tail)*)
28 };
29
30 (@filter_in_call [$($acc:ident),*]) => { &[$(EslEventType::$acc,)*] };
32 (@filter_in_call [$($acc:ident),*] ($v:ident [in_call $(,$_g:ident)*]) $($tail:tt)*) => {
33 esl_event_types!(@filter_in_call [$($acc,)* $v] $($tail)*)
34 };
35 (@filter_in_call [$($acc:ident),*] ($v:ident [$_h:ident $(,$tg:ident)*]) $($tail:tt)*) => {
36 esl_event_types!(@filter_in_call [$($acc),*] ($v [$($tg),*]) $($tail)*)
37 };
38 (@filter_in_call [$($acc:ident),*] ($v:ident []) $($tail:tt)*) => {
39 esl_event_types!(@filter_in_call [$($acc),*] $($tail)*)
40 };
41
42 (@filter_media [$($acc:ident),*]) => { &[$(EslEventType::$acc,)*] };
44 (@filter_media [$($acc:ident),*] ($v:ident [media $(,$_g:ident)*]) $($tail:tt)*) => {
45 esl_event_types!(@filter_media [$($acc,)* $v] $($tail)*)
46 };
47 (@filter_media [$($acc:ident),*] ($v:ident [$_h:ident $(,$tg:ident)*]) $($tail:tt)*) => {
48 esl_event_types!(@filter_media [$($acc),*] ($v [$($tg),*]) $($tail)*)
49 };
50 (@filter_media [$($acc:ident),*] ($v:ident []) $($tail:tt)*) => {
51 esl_event_types!(@filter_media [$($acc),*] $($tail)*)
52 };
53
54 (@filter_presence [$($acc:ident),*]) => { &[$(EslEventType::$acc,)*] };
56 (@filter_presence [$($acc:ident),*] ($v:ident [presence $(,$_g:ident)*]) $($tail:tt)*) => {
57 esl_event_types!(@filter_presence [$($acc,)* $v] $($tail)*)
58 };
59 (@filter_presence [$($acc:ident),*] ($v:ident [$_h:ident $(,$tg:ident)*]) $($tail:tt)*) => {
60 esl_event_types!(@filter_presence [$($acc),*] ($v [$($tg),*]) $($tail)*)
61 };
62 (@filter_presence [$($acc:ident),*] ($v:ident []) $($tail:tt)*) => {
63 esl_event_types!(@filter_presence [$($acc),*] $($tail)*)
64 };
65
66 (@filter_system [$($acc:ident),*]) => { &[$(EslEventType::$acc,)*] };
68 (@filter_system [$($acc:ident),*] ($v:ident [system $(,$_g:ident)*]) $($tail:tt)*) => {
69 esl_event_types!(@filter_system [$($acc,)* $v] $($tail)*)
70 };
71 (@filter_system [$($acc:ident),*] ($v:ident [$_h:ident $(,$tg:ident)*]) $($tail:tt)*) => {
72 esl_event_types!(@filter_system [$($acc),*] ($v [$($tg),*]) $($tail)*)
73 };
74 (@filter_system [$($acc:ident),*] ($v:ident []) $($tail:tt)*) => {
75 esl_event_types!(@filter_system [$($acc),*] $($tail)*)
76 };
77
78 (@filter_conference [$($acc:ident),*]) => { &[$(EslEventType::$acc,)*] };
80 (@filter_conference [$($acc:ident),*] ($v:ident [conference $(,$_g:ident)*]) $($tail:tt)*) => {
81 esl_event_types!(@filter_conference [$($acc,)* $v] $($tail)*)
82 };
83 (@filter_conference [$($acc:ident),*] ($v:ident [$_h:ident $(,$tg:ident)*]) $($tail:tt)*) => {
84 esl_event_types!(@filter_conference [$($acc),*] ($v [$($tg),*]) $($tail)*)
85 };
86 (@filter_conference [$($acc:ident),*] ($v:ident []) $($tail:tt)*) => {
87 esl_event_types!(@filter_conference [$($acc),*] $($tail)*)
88 };
89
90 (
96 $(
97 $(#[$attr:meta])*
98 $variant:ident => $wire:literal [$($vgroup:ident),*]
99 ),+ $(,)?
100 ;
101 $(
103 $(#[$extra_attr:meta])*
104 $extra_variant:ident => $extra_wire:literal [$($egroup:ident),*]
105 ),* $(,)?
106 ) => {
107 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113 #[non_exhaustive]
114 #[allow(missing_docs)]
115 pub enum EslEventType {
116 $(
117 $(#[$attr])*
118 $variant,
119 )+
120 $(
121 $(#[$extra_attr])*
122 $extra_variant,
123 )*
124 }
125
126 impl fmt::Display for EslEventType {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.write_str(self.as_str())
129 }
130 }
131
132 impl EslEventType {
133 pub const fn as_str(&self) -> &'static str {
135 match self {
136 $( EslEventType::$variant => $wire, )+
137 $( EslEventType::$extra_variant => $extra_wire, )*
138 }
139 }
140
141 pub fn parse_event_type(s: &str) -> Option<Self> {
143 match s {
144 $( $wire => Some(EslEventType::$variant), )+
145 $( $extra_wire => Some(EslEventType::$extra_variant), )*
146 _ => None,
147 }
148 }
149
150 #[doc = "Every `CHANNEL_*` event type."]
161 #[doc = ""]
162 #[doc = "Covers the full channel lifecycle: creation, state changes, execution,"]
163 #[doc = "bridging, hold, park, progress, originate, and destruction."]
164 #[doc = ""]
165 #[doc = "```rust"]
166 #[doc = "use freeswitch_types::EslEventType;"]
167 #[doc = "assert!(EslEventType::CHANNEL_EVENTS.contains(&EslEventType::ChannelCreate));"]
168 #[doc = "assert!(EslEventType::CHANNEL_EVENTS.contains(&EslEventType::ChannelHangupComplete));"]
169 #[doc = "```"]
170 pub const CHANNEL_EVENTS: &[EslEventType] = esl_event_types!(
171 @filter_channel []
172 $(($variant [$($vgroup),*]))+
173 $(($extra_variant [$($egroup),*]))*
174 );
175
176 #[doc = "In-call events: DTMF, VAD speech detection, media security, and call updates."]
177 #[doc = ""]
178 #[doc = "Events that fire during an established call, tied to RTP/media activity"]
179 #[doc = "rather than signaling state transitions."]
180 #[doc = ""]
181 #[doc = "```rust"]
182 #[doc = "use freeswitch_types::EslEventType;"]
183 #[doc = "assert!(EslEventType::IN_CALL_EVENTS.contains(&EslEventType::Dtmf));"]
184 #[doc = "assert!(EslEventType::IN_CALL_EVENTS.contains(&EslEventType::Talk));"]
185 #[doc = "```"]
186 pub const IN_CALL_EVENTS: &[EslEventType] = esl_event_types!(
187 @filter_in_call []
188 $(($variant [$($vgroup),*]))+
189 $(($extra_variant [$($egroup),*]))*
190 );
191
192 #[doc = "Media-related events: playback, recording, media bugs, and detection."]
193 #[doc = ""]
194 #[doc = "Useful for IVR applications that need to track media operations without"]
195 #[doc = "subscribing to the full channel lifecycle."]
196 #[doc = ""]
197 #[doc = "```rust"]
198 #[doc = "use freeswitch_types::EslEventType;"]
199 #[doc = "assert!(EslEventType::MEDIA_EVENTS.contains(&EslEventType::PlaybackStart));"]
200 #[doc = "assert!(EslEventType::MEDIA_EVENTS.contains(&EslEventType::DetectedSpeech));"]
201 #[doc = "```"]
202 pub const MEDIA_EVENTS: &[EslEventType] = esl_event_types!(
203 @filter_media []
204 $(($variant [$($vgroup),*]))+
205 $(($extra_variant [$($egroup),*]))*
206 );
207
208 #[doc = "Presence and messaging events."]
209 #[doc = ""]
210 #[doc = "For applications that track user presence (BLF, buddy lists) or"]
211 #[doc = "message-waiting indicators (voicemail MWI)."]
212 #[doc = ""]
213 #[doc = "```rust"]
214 #[doc = "use freeswitch_types::EslEventType;"]
215 #[doc = "assert!(EslEventType::PRESENCE_EVENTS.contains(&EslEventType::PresenceIn));"]
216 #[doc = "assert!(EslEventType::PRESENCE_EVENTS.contains(&EslEventType::MessageWaiting));"]
217 #[doc = "```"]
218 pub const PRESENCE_EVENTS: &[EslEventType] = esl_event_types!(
219 @filter_presence []
220 $(($variant [$($vgroup),*]))+
221 $(($extra_variant [$($egroup),*]))*
222 );
223
224 #[doc = "System lifecycle events."]
225 #[doc = ""]
226 #[doc = "Server startup/shutdown, heartbeats, module loading, and XML reloads."]
227 #[doc = "Useful for monitoring dashboards and operational tooling."]
228 #[doc = ""]
229 #[doc = "```rust"]
230 #[doc = "use freeswitch_types::EslEventType;"]
231 #[doc = "assert!(EslEventType::SYSTEM_EVENTS.contains(&EslEventType::Heartbeat));"]
232 #[doc = "assert!(EslEventType::SYSTEM_EVENTS.contains(&EslEventType::Shutdown));"]
233 #[doc = "```"]
234 pub const SYSTEM_EVENTS: &[EslEventType] = esl_event_types!(
235 @filter_system []
236 $(($variant [$($vgroup),*]))+
237 $(($extra_variant [$($egroup),*]))*
238 );
239
240 #[doc = "Conference-related events."]
241 #[doc = ""]
242 #[doc = "```rust"]
243 #[doc = "use freeswitch_types::EslEventType;"]
244 #[doc = "assert!(EslEventType::CONFERENCE_EVENTS.contains(&EslEventType::ConferenceData));"]
245 #[doc = "```"]
246 pub const CONFERENCE_EVENTS: &[EslEventType] = esl_event_types!(
247 @filter_conference []
248 $(($variant [$($vgroup),*]))+
249 $(($extra_variant [$($egroup),*]))*
250 );
251 }
252
253 impl FromStr for EslEventType {
254 type Err = ParseEventTypeError;
255
256 fn from_str(s: &str) -> Result<Self, Self::Err> {
257 Self::parse_event_type(s).ok_or_else(|| ParseEventTypeError(s.to_string()))
258 }
259 }
260 };
261}
262
263esl_event_types! {
264 Custom => "CUSTOM" [],
265 Clone => "CLONE" [],
266 ChannelCreate => "CHANNEL_CREATE" [channel],
267 ChannelDestroy => "CHANNEL_DESTROY" [channel],
268 ChannelState => "CHANNEL_STATE" [channel],
269 ChannelCallstate => "CHANNEL_CALLSTATE" [channel],
270 ChannelAnswer => "CHANNEL_ANSWER" [channel],
271 ChannelHangup => "CHANNEL_HANGUP" [channel],
272 ChannelHangupComplete => "CHANNEL_HANGUP_COMPLETE" [channel],
273 ChannelExecute => "CHANNEL_EXECUTE" [channel],
274 ChannelExecuteComplete => "CHANNEL_EXECUTE_COMPLETE" [channel],
275 ChannelHold => "CHANNEL_HOLD" [channel],
276 ChannelUnhold => "CHANNEL_UNHOLD" [channel],
277 ChannelBridge => "CHANNEL_BRIDGE" [channel],
278 ChannelUnbridge => "CHANNEL_UNBRIDGE" [channel],
279 ChannelProgress => "CHANNEL_PROGRESS" [channel],
280 ChannelProgressMedia => "CHANNEL_PROGRESS_MEDIA" [channel],
281 ChannelOutgoing => "CHANNEL_OUTGOING" [channel],
282 ChannelPark => "CHANNEL_PARK" [channel],
283 ChannelUnpark => "CHANNEL_UNPARK" [channel],
284 ChannelApplication => "CHANNEL_APPLICATION" [channel],
285 ChannelOriginate => "CHANNEL_ORIGINATE" [channel],
286 ChannelUuid => "CHANNEL_UUID" [channel],
287 Api => "API" [],
288 Log => "LOG" [],
289 InboundChan => "INBOUND_CHAN" [],
290 OutboundChan => "OUTBOUND_CHAN" [],
291 Startup => "STARTUP" [system],
292 Shutdown => "SHUTDOWN" [system],
293 Publish => "PUBLISH" [],
294 Unpublish => "UNPUBLISH" [],
295 Talk => "TALK" [in_call],
296 Notalk => "NOTALK" [in_call],
297 SessionCrash => "SESSION_CRASH" [system],
298 ModuleLoad => "MODULE_LOAD" [system],
299 ModuleUnload => "MODULE_UNLOAD" [system],
300 Dtmf => "DTMF" [in_call],
301 Message => "MESSAGE" [],
302 PresenceIn => "PRESENCE_IN" [presence],
303 NotifyIn => "NOTIFY_IN" [],
304 PresenceOut => "PRESENCE_OUT" [presence],
305 PresenceProbe => "PRESENCE_PROBE" [presence],
306 MessageWaiting => "MESSAGE_WAITING" [presence],
307 MessageQuery => "MESSAGE_QUERY" [presence],
308 Roster => "ROSTER" [presence],
309 Codec => "CODEC" [],
310 BackgroundJob => "BACKGROUND_JOB" [],
311 DetectedSpeech => "DETECTED_SPEECH" [media],
312 DetectedTone => "DETECTED_TONE" [media],
313 PrivateCommand => "PRIVATE_COMMAND" [],
314 Heartbeat => "HEARTBEAT" [system],
315 Trap => "TRAP" [],
316 AddSchedule => "ADD_SCHEDULE" [],
317 DelSchedule => "DEL_SCHEDULE" [],
318 ExeSchedule => "EXE_SCHEDULE" [],
319 ReSchedule => "RE_SCHEDULE" [],
320 ReloadXml => "RELOADXML" [system],
321 Notify => "NOTIFY" [],
322 PhoneFeature => "PHONE_FEATURE" [],
323 PhoneFeatureSubscribe => "PHONE_FEATURE_SUBSCRIBE" [],
324 SendMessage => "SEND_MESSAGE" [],
325 RecvMessage => "RECV_MESSAGE" [],
326 RequestParams => "REQUEST_PARAMS" [],
327 ChannelData => "CHANNEL_DATA" [channel],
328 General => "GENERAL" [],
329 Command => "COMMAND" [],
330 SessionHeartbeat => "SESSION_HEARTBEAT" [system],
331 ClientDisconnected => "CLIENT_DISCONNECTED" [],
332 ServerDisconnected => "SERVER_DISCONNECTED" [],
333 SendInfo => "SEND_INFO" [],
334 RecvInfo => "RECV_INFO" [],
335 RecvRtcpMessage => "RECV_RTCP_MESSAGE" [in_call],
336 SendRtcpMessage => "SEND_RTCP_MESSAGE" [in_call],
337 CallSecure => "CALL_SECURE" [in_call],
338 Nat => "NAT" [],
339 RecordStart => "RECORD_START" [media],
340 RecordStop => "RECORD_STOP" [media],
341 PlaybackStart => "PLAYBACK_START" [media],
342 PlaybackStop => "PLAYBACK_STOP" [media],
343 CallUpdate => "CALL_UPDATE" [in_call],
344 Failure => "FAILURE" [],
345 SocketData => "SOCKET_DATA" [],
346 MediaBugStart => "MEDIA_BUG_START" [media],
347 MediaBugStop => "MEDIA_BUG_STOP" [media],
348 ConferenceDataQuery => "CONFERENCE_DATA_QUERY" [conference],
349 ConferenceData => "CONFERENCE_DATA" [conference],
350 CallSetupReq => "CALL_SETUP_REQ" [],
351 CallSetupResult => "CALL_SETUP_RESULT" [],
352 CallDetail => "CALL_DETAIL" [],
353 DeviceState => "DEVICE_STATE" [],
354 Text => "TEXT" [],
355 ShutdownRequested => "SHUTDOWN_REQUESTED" [system],
356 All => "ALL" [];
358 StartRecording => "START_RECORDING" [media],
362}
363
364parse_error! { ParseEventTypeError("event type"); }
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_notify_in_parse() {
372 assert_eq!(
373 EslEventType::parse_event_type("NOTIFY_IN"),
374 Some(EslEventType::NotifyIn)
375 );
376 assert_eq!(EslEventType::parse_event_type("notify_in"), None);
377 }
378
379 #[test]
380 fn test_notify_in_display() {
381 assert_eq!(EslEventType::NotifyIn.to_string(), "NOTIFY_IN");
382 }
383
384 #[test]
385 fn test_notify_in_distinct_from_notify() {
386 assert_ne!(EslEventType::Notify, EslEventType::NotifyIn);
387 assert_ne!(
388 EslEventType::Notify.to_string(),
389 EslEventType::NotifyIn.to_string()
390 );
391 }
392
393 #[test]
394 fn test_wire_names_match_c_esl() {
395 assert_eq!(
396 EslEventType::ChannelOutgoing.to_string(),
397 "CHANNEL_OUTGOING"
398 );
399 assert_eq!(EslEventType::Api.to_string(), "API");
400 assert_eq!(EslEventType::ReloadXml.to_string(), "RELOADXML");
401 assert_eq!(EslEventType::PresenceIn.to_string(), "PRESENCE_IN");
402 assert_eq!(EslEventType::Roster.to_string(), "ROSTER");
403 assert_eq!(EslEventType::Text.to_string(), "TEXT");
404 assert_eq!(EslEventType::ReSchedule.to_string(), "RE_SCHEDULE");
405
406 assert_eq!(
407 EslEventType::parse_event_type("CHANNEL_OUTGOING"),
408 Some(EslEventType::ChannelOutgoing)
409 );
410 assert_eq!(
411 EslEventType::parse_event_type("API"),
412 Some(EslEventType::Api)
413 );
414 assert_eq!(
415 EslEventType::parse_event_type("RELOADXML"),
416 Some(EslEventType::ReloadXml)
417 );
418 assert_eq!(
419 EslEventType::parse_event_type("PRESENCE_IN"),
420 Some(EslEventType::PresenceIn)
421 );
422 }
423
424 #[test]
425 fn test_event_type_from_str() {
426 assert_eq!(
427 "CHANNEL_ANSWER".parse::<EslEventType>(),
428 Ok(EslEventType::ChannelAnswer)
429 );
430 assert!("channel_answer"
431 .parse::<EslEventType>()
432 .is_err());
433 assert!("UNKNOWN_EVENT"
434 .parse::<EslEventType>()
435 .is_err());
436 }
437}