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."]
153 #[doc = ""]
154 #[doc = "Covers the full channel lifecycle: creation, state changes, execution,"]
155 #[doc = "bridging, hold, park, progress, originate, and destruction."]
156 #[doc = ""]
157 #[doc = "```rust"]
158 #[doc = "use freeswitch_types::EslEventType;"]
159 #[doc = "assert!(EslEventType::CHANNEL_EVENTS.contains(&EslEventType::ChannelCreate));"]
160 #[doc = "assert!(EslEventType::CHANNEL_EVENTS.contains(&EslEventType::ChannelHangupComplete));"]
161 #[doc = "```"]
162 pub const CHANNEL_EVENTS: &[EslEventType] = esl_event_types!(
163 @filter_channel []
164 $(($variant [$($vgroup),*]))+
165 $(($extra_variant [$($egroup),*]))*
166 );
167
168 #[doc = "In-call events: DTMF, VAD speech detection, media security, and call updates."]
169 #[doc = ""]
170 #[doc = "Events that fire during an established call, tied to RTP/media activity"]
171 #[doc = "rather than signaling state transitions."]
172 #[doc = ""]
173 #[doc = "```rust"]
174 #[doc = "use freeswitch_types::EslEventType;"]
175 #[doc = "assert!(EslEventType::IN_CALL_EVENTS.contains(&EslEventType::Dtmf));"]
176 #[doc = "assert!(EslEventType::IN_CALL_EVENTS.contains(&EslEventType::Talk));"]
177 #[doc = "```"]
178 pub const IN_CALL_EVENTS: &[EslEventType] = esl_event_types!(
179 @filter_in_call []
180 $(($variant [$($vgroup),*]))+
181 $(($extra_variant [$($egroup),*]))*
182 );
183
184 #[doc = "Media-related events: playback, recording, media bugs, and detection."]
185 #[doc = ""]
186 #[doc = "Useful for IVR applications that need to track media operations without"]
187 #[doc = "subscribing to the full channel lifecycle."]
188 #[doc = ""]
189 #[doc = "```rust"]
190 #[doc = "use freeswitch_types::EslEventType;"]
191 #[doc = "assert!(EslEventType::MEDIA_EVENTS.contains(&EslEventType::PlaybackStart));"]
192 #[doc = "assert!(EslEventType::MEDIA_EVENTS.contains(&EslEventType::DetectedSpeech));"]
193 #[doc = "```"]
194 pub const MEDIA_EVENTS: &[EslEventType] = esl_event_types!(
195 @filter_media []
196 $(($variant [$($vgroup),*]))+
197 $(($extra_variant [$($egroup),*]))*
198 );
199
200 #[doc = "Presence and messaging events."]
201 #[doc = ""]
202 #[doc = "For applications that track user presence (BLF, buddy lists) or"]
203 #[doc = "message-waiting indicators (voicemail MWI)."]
204 #[doc = ""]
205 #[doc = "```rust"]
206 #[doc = "use freeswitch_types::EslEventType;"]
207 #[doc = "assert!(EslEventType::PRESENCE_EVENTS.contains(&EslEventType::PresenceIn));"]
208 #[doc = "assert!(EslEventType::PRESENCE_EVENTS.contains(&EslEventType::MessageWaiting));"]
209 #[doc = "```"]
210 pub const PRESENCE_EVENTS: &[EslEventType] = esl_event_types!(
211 @filter_presence []
212 $(($variant [$($vgroup),*]))+
213 $(($extra_variant [$($egroup),*]))*
214 );
215
216 #[doc = "System lifecycle events."]
217 #[doc = ""]
218 #[doc = "Server startup/shutdown, heartbeats, module loading, and XML reloads."]
219 #[doc = "Useful for monitoring dashboards and operational tooling."]
220 #[doc = ""]
221 #[doc = "```rust"]
222 #[doc = "use freeswitch_types::EslEventType;"]
223 #[doc = "assert!(EslEventType::SYSTEM_EVENTS.contains(&EslEventType::Heartbeat));"]
224 #[doc = "assert!(EslEventType::SYSTEM_EVENTS.contains(&EslEventType::Shutdown));"]
225 #[doc = "```"]
226 pub const SYSTEM_EVENTS: &[EslEventType] = esl_event_types!(
227 @filter_system []
228 $(($variant [$($vgroup),*]))+
229 $(($extra_variant [$($egroup),*]))*
230 );
231
232 #[doc = "Conference-related events."]
233 #[doc = ""]
234 #[doc = "```rust"]
235 #[doc = "use freeswitch_types::EslEventType;"]
236 #[doc = "assert!(EslEventType::CONFERENCE_EVENTS.contains(&EslEventType::ConferenceData));"]
237 #[doc = "```"]
238 pub const CONFERENCE_EVENTS: &[EslEventType] = esl_event_types!(
239 @filter_conference []
240 $(($variant [$($vgroup),*]))+
241 $(($extra_variant [$($egroup),*]))*
242 );
243 }
244
245 impl FromStr for EslEventType {
246 type Err = ParseEventTypeError;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 Self::parse_event_type(s).ok_or_else(|| ParseEventTypeError(s.to_string()))
250 }
251 }
252 };
253}
254
255esl_event_types! {
256 Custom => "CUSTOM" [],
257 Clone => "CLONE" [],
258 ChannelCreate => "CHANNEL_CREATE" [channel],
259 ChannelDestroy => "CHANNEL_DESTROY" [channel],
260 ChannelState => "CHANNEL_STATE" [channel],
261 ChannelCallstate => "CHANNEL_CALLSTATE" [channel],
262 ChannelAnswer => "CHANNEL_ANSWER" [channel],
263 ChannelHangup => "CHANNEL_HANGUP" [channel],
264 ChannelHangupComplete => "CHANNEL_HANGUP_COMPLETE" [channel],
265 ChannelExecute => "CHANNEL_EXECUTE" [channel],
266 ChannelExecuteComplete => "CHANNEL_EXECUTE_COMPLETE" [channel],
267 ChannelHold => "CHANNEL_HOLD" [channel],
268 ChannelUnhold => "CHANNEL_UNHOLD" [channel],
269 ChannelBridge => "CHANNEL_BRIDGE" [channel],
270 ChannelUnbridge => "CHANNEL_UNBRIDGE" [channel],
271 ChannelProgress => "CHANNEL_PROGRESS" [channel],
272 ChannelProgressMedia => "CHANNEL_PROGRESS_MEDIA" [channel],
273 ChannelOutgoing => "CHANNEL_OUTGOING" [channel],
274 ChannelPark => "CHANNEL_PARK" [channel],
275 ChannelUnpark => "CHANNEL_UNPARK" [channel],
276 ChannelApplication => "CHANNEL_APPLICATION" [channel],
277 ChannelOriginate => "CHANNEL_ORIGINATE" [channel],
278 ChannelUuid => "CHANNEL_UUID" [channel],
279 Api => "API" [],
280 Log => "LOG" [],
281 InboundChan => "INBOUND_CHAN" [],
282 OutboundChan => "OUTBOUND_CHAN" [],
283 Startup => "STARTUP" [system],
284 Shutdown => "SHUTDOWN" [system],
285 Publish => "PUBLISH" [],
286 Unpublish => "UNPUBLISH" [],
287 Talk => "TALK" [in_call],
288 Notalk => "NOTALK" [in_call],
289 SessionCrash => "SESSION_CRASH" [system],
290 ModuleLoad => "MODULE_LOAD" [system],
291 ModuleUnload => "MODULE_UNLOAD" [system],
292 Dtmf => "DTMF" [in_call],
293 Message => "MESSAGE" [],
294 PresenceIn => "PRESENCE_IN" [presence],
295 NotifyIn => "NOTIFY_IN" [],
296 PresenceOut => "PRESENCE_OUT" [presence],
297 PresenceProbe => "PRESENCE_PROBE" [presence],
298 MessageWaiting => "MESSAGE_WAITING" [presence],
299 MessageQuery => "MESSAGE_QUERY" [presence],
300 Roster => "ROSTER" [presence],
301 Codec => "CODEC" [],
302 BackgroundJob => "BACKGROUND_JOB" [],
303 DetectedSpeech => "DETECTED_SPEECH" [media],
304 DetectedTone => "DETECTED_TONE" [media],
305 PrivateCommand => "PRIVATE_COMMAND" [],
306 Heartbeat => "HEARTBEAT" [system],
307 Trap => "TRAP" [],
308 AddSchedule => "ADD_SCHEDULE" [],
309 DelSchedule => "DEL_SCHEDULE" [],
310 ExeSchedule => "EXE_SCHEDULE" [],
311 ReSchedule => "RE_SCHEDULE" [],
312 ReloadXml => "RELOADXML" [system],
313 Notify => "NOTIFY" [],
314 PhoneFeature => "PHONE_FEATURE" [],
315 PhoneFeatureSubscribe => "PHONE_FEATURE_SUBSCRIBE" [],
316 SendMessage => "SEND_MESSAGE" [],
317 RecvMessage => "RECV_MESSAGE" [],
318 RequestParams => "REQUEST_PARAMS" [],
319 ChannelData => "CHANNEL_DATA" [channel],
320 General => "GENERAL" [],
321 Command => "COMMAND" [],
322 SessionHeartbeat => "SESSION_HEARTBEAT" [system],
323 ClientDisconnected => "CLIENT_DISCONNECTED" [],
324 ServerDisconnected => "SERVER_DISCONNECTED" [],
325 SendInfo => "SEND_INFO" [],
326 RecvInfo => "RECV_INFO" [],
327 RecvRtcpMessage => "RECV_RTCP_MESSAGE" [in_call],
328 SendRtcpMessage => "SEND_RTCP_MESSAGE" [in_call],
329 CallSecure => "CALL_SECURE" [in_call],
330 Nat => "NAT" [],
331 RecordStart => "RECORD_START" [media],
332 RecordStop => "RECORD_STOP" [media],
333 PlaybackStart => "PLAYBACK_START" [media],
334 PlaybackStop => "PLAYBACK_STOP" [media],
335 CallUpdate => "CALL_UPDATE" [in_call],
336 Failure => "FAILURE" [],
337 SocketData => "SOCKET_DATA" [],
338 MediaBugStart => "MEDIA_BUG_START" [media],
339 MediaBugStop => "MEDIA_BUG_STOP" [media],
340 ConferenceDataQuery => "CONFERENCE_DATA_QUERY" [conference],
341 ConferenceData => "CONFERENCE_DATA" [conference],
342 CallSetupReq => "CALL_SETUP_REQ" [],
343 CallSetupResult => "CALL_SETUP_RESULT" [],
344 CallDetail => "CALL_DETAIL" [],
345 DeviceState => "DEVICE_STATE" [],
346 Text => "TEXT" [],
347 ShutdownRequested => "SHUTDOWN_REQUESTED" [system],
348 All => "ALL" [];
350 StartRecording => "START_RECORDING" [media],
354}
355
356parse_error! { ParseEventTypeError("event type"); }
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_notify_in_parse() {
364 assert_eq!(
365 EslEventType::parse_event_type("NOTIFY_IN"),
366 Some(EslEventType::NotifyIn)
367 );
368 assert_eq!(EslEventType::parse_event_type("notify_in"), None);
369 }
370
371 #[test]
372 fn test_notify_in_display() {
373 assert_eq!(EslEventType::NotifyIn.to_string(), "NOTIFY_IN");
374 }
375
376 #[test]
377 fn test_notify_in_distinct_from_notify() {
378 assert_ne!(EslEventType::Notify, EslEventType::NotifyIn);
379 assert_ne!(
380 EslEventType::Notify.to_string(),
381 EslEventType::NotifyIn.to_string()
382 );
383 }
384
385 #[test]
386 fn test_wire_names_match_c_esl() {
387 assert_eq!(
388 EslEventType::ChannelOutgoing.to_string(),
389 "CHANNEL_OUTGOING"
390 );
391 assert_eq!(EslEventType::Api.to_string(), "API");
392 assert_eq!(EslEventType::ReloadXml.to_string(), "RELOADXML");
393 assert_eq!(EslEventType::PresenceIn.to_string(), "PRESENCE_IN");
394 assert_eq!(EslEventType::Roster.to_string(), "ROSTER");
395 assert_eq!(EslEventType::Text.to_string(), "TEXT");
396 assert_eq!(EslEventType::ReSchedule.to_string(), "RE_SCHEDULE");
397
398 assert_eq!(
399 EslEventType::parse_event_type("CHANNEL_OUTGOING"),
400 Some(EslEventType::ChannelOutgoing)
401 );
402 assert_eq!(
403 EslEventType::parse_event_type("API"),
404 Some(EslEventType::Api)
405 );
406 assert_eq!(
407 EslEventType::parse_event_type("RELOADXML"),
408 Some(EslEventType::ReloadXml)
409 );
410 assert_eq!(
411 EslEventType::parse_event_type("PRESENCE_IN"),
412 Some(EslEventType::PresenceIn)
413 );
414 }
415
416 #[test]
417 fn test_event_type_from_str() {
418 assert_eq!(
419 "CHANNEL_ANSWER".parse::<EslEventType>(),
420 Ok(EslEventType::ChannelAnswer)
421 );
422 assert!("channel_answer"
423 .parse::<EslEventType>()
424 .is_err());
425 assert!("UNKNOWN_EVENT"
426 .parse::<EslEventType>()
427 .is_err());
428 }
429}