Skip to main content

freeswitch_types/sofia/
event_subclass.rs

1//! Typed Sofia event subclass names (`Event-Subclass` header values).
2
3use std::fmt;
4use std::str::FromStr;
5
6/// Error returned when parsing an unrecognized Sofia event subclass.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct ParseSofiaEventSubclassError(pub String);
9
10impl fmt::Display for ParseSofiaEventSubclassError {
11    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12        write!(f, "unknown sofia event subclass: {}", self.0)
13    }
14}
15
16impl std::error::Error for ParseSofiaEventSubclassError {}
17
18/// Sofia event subclass values from `mod_sofia.h` `MY_EVENT_*` defines.
19///
20/// These appear as the `Event-Subclass` header on `CUSTOM` events fired by
21/// mod_sofia. Use with [`EventSubscription::sofia_event()`](crate::EventSubscription::sofia_event)
22/// for typed subscriptions, or parse from received events via
23/// [`HeaderLookup::sofia_event_subclass()`](crate::HeaderLookup::sofia_event_subclass).
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[non_exhaustive]
27#[allow(missing_docs)]
28pub enum SofiaEventSubclass {
29    Register,
30    PreRegister,
31    RegisterAttempt,
32    RegisterFailure,
33    Unregister,
34    Expire,
35    GatewayState,
36    SipUserState,
37    NotifyRefer,
38    Reinvite,
39    GatewayAdd,
40    GatewayDelete,
41    GatewayInvalidDigestReq,
42    RecoveryRecv,
43    RecoverySend,
44    RecoveryRecovered,
45    Error,
46    ProfileStart,
47    NotifyWatchedHeader,
48    WrongCallState,
49    Transferor,
50    Transferee,
51    Replaced,
52    Intercepted,
53    ByeResponse,
54}
55
56impl SofiaEventSubclass {
57    /// Wire-format string including the `sofia::` prefix.
58    pub const fn as_str(&self) -> &'static str {
59        match self {
60            Self::Register => "sofia::register",
61            Self::PreRegister => "sofia::pre_register",
62            Self::RegisterAttempt => "sofia::register_attempt",
63            Self::RegisterFailure => "sofia::register_failure",
64            Self::Unregister => "sofia::unregister",
65            Self::Expire => "sofia::expire",
66            Self::GatewayState => "sofia::gateway_state",
67            Self::SipUserState => "sofia::sip_user_state",
68            Self::NotifyRefer => "sofia::notify_refer",
69            Self::Reinvite => "sofia::reinvite",
70            Self::GatewayAdd => "sofia::gateway_add",
71            Self::GatewayDelete => "sofia::gateway_delete",
72            Self::GatewayInvalidDigestReq => "sofia::gateway_invalid_digest_req",
73            Self::RecoveryRecv => "sofia::recovery_recv",
74            Self::RecoverySend => "sofia::recovery_send",
75            Self::RecoveryRecovered => "sofia::recovery_recovered",
76            Self::Error => "sofia::error",
77            Self::ProfileStart => "sofia::profile_start",
78            Self::NotifyWatchedHeader => "sofia::notify_watched_header",
79            Self::WrongCallState => "sofia::wrong_call_state",
80            Self::Transferor => "sofia::transferor",
81            Self::Transferee => "sofia::transferee",
82            Self::Replaced => "sofia::replaced",
83            Self::Intercepted => "sofia::intercepted",
84            Self::ByeResponse => "sofia::bye_response",
85        }
86    }
87
88    /// All Sofia event subclass variants.
89    pub const ALL: &[SofiaEventSubclass] = &[
90        Self::Register,
91        Self::PreRegister,
92        Self::RegisterAttempt,
93        Self::RegisterFailure,
94        Self::Unregister,
95        Self::Expire,
96        Self::GatewayState,
97        Self::SipUserState,
98        Self::NotifyRefer,
99        Self::Reinvite,
100        Self::GatewayAdd,
101        Self::GatewayDelete,
102        Self::GatewayInvalidDigestReq,
103        Self::RecoveryRecv,
104        Self::RecoverySend,
105        Self::RecoveryRecovered,
106        Self::Error,
107        Self::ProfileStart,
108        Self::NotifyWatchedHeader,
109        Self::WrongCallState,
110        Self::Transferor,
111        Self::Transferee,
112        Self::Replaced,
113        Self::Intercepted,
114        Self::ByeResponse,
115    ];
116
117    /// Registration-related subclasses.
118    pub const REGISTRATION_EVENTS: &[SofiaEventSubclass] = &[
119        Self::Register,
120        Self::PreRegister,
121        Self::RegisterAttempt,
122        Self::RegisterFailure,
123        Self::Unregister,
124        Self::Expire,
125    ];
126
127    /// Gateway lifecycle and state subclasses.
128    pub const GATEWAY_EVENTS: &[SofiaEventSubclass] = &[
129        Self::GatewayState,
130        Self::GatewayAdd,
131        Self::GatewayDelete,
132        Self::GatewayInvalidDigestReq,
133    ];
134}
135
136impl fmt::Display for SofiaEventSubclass {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.write_str(self.as_str())
139    }
140}
141
142impl FromStr for SofiaEventSubclass {
143    type Err = ParseSofiaEventSubclassError;
144
145    fn from_str(s: &str) -> Result<Self, Self::Err> {
146        match s {
147            "sofia::register" => Ok(Self::Register),
148            "sofia::pre_register" => Ok(Self::PreRegister),
149            "sofia::register_attempt" => Ok(Self::RegisterAttempt),
150            "sofia::register_failure" => Ok(Self::RegisterFailure),
151            "sofia::unregister" => Ok(Self::Unregister),
152            "sofia::expire" => Ok(Self::Expire),
153            "sofia::gateway_state" => Ok(Self::GatewayState),
154            "sofia::sip_user_state" => Ok(Self::SipUserState),
155            "sofia::notify_refer" => Ok(Self::NotifyRefer),
156            "sofia::reinvite" => Ok(Self::Reinvite),
157            "sofia::gateway_add" => Ok(Self::GatewayAdd),
158            "sofia::gateway_delete" => Ok(Self::GatewayDelete),
159            "sofia::gateway_invalid_digest_req" => Ok(Self::GatewayInvalidDigestReq),
160            "sofia::recovery_recv" => Ok(Self::RecoveryRecv),
161            "sofia::recovery_send" => Ok(Self::RecoverySend),
162            "sofia::recovery_recovered" => Ok(Self::RecoveryRecovered),
163            "sofia::error" => Ok(Self::Error),
164            "sofia::profile_start" => Ok(Self::ProfileStart),
165            "sofia::notify_watched_header" => Ok(Self::NotifyWatchedHeader),
166            "sofia::wrong_call_state" => Ok(Self::WrongCallState),
167            "sofia::transferor" => Ok(Self::Transferor),
168            "sofia::transferee" => Ok(Self::Transferee),
169            "sofia::replaced" => Ok(Self::Replaced),
170            "sofia::intercepted" => Ok(Self::Intercepted),
171            "sofia::bye_response" => Ok(Self::ByeResponse),
172            _ => Err(ParseSofiaEventSubclassError(s.to_string())),
173        }
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn display_from_str_round_trip_all() {
183        for &variant in SofiaEventSubclass::ALL {
184            let wire = variant.to_string();
185            assert!(wire.starts_with("sofia::"), "missing prefix: {wire}");
186            let parsed: SofiaEventSubclass = wire
187                .parse()
188                .unwrap();
189            assert_eq!(parsed, variant, "round-trip failed for {wire}");
190        }
191    }
192
193    #[test]
194    fn all_contains_every_variant() {
195        assert_eq!(SofiaEventSubclass::ALL.len(), 25);
196    }
197
198    #[test]
199    fn registration_events_subset() {
200        for &ev in SofiaEventSubclass::REGISTRATION_EVENTS {
201            assert!(SofiaEventSubclass::ALL.contains(&ev), "{ev} not in ALL");
202        }
203        assert_eq!(SofiaEventSubclass::REGISTRATION_EVENTS.len(), 6);
204    }
205
206    #[test]
207    fn gateway_events_subset() {
208        for &ev in SofiaEventSubclass::GATEWAY_EVENTS {
209            assert!(SofiaEventSubclass::ALL.contains(&ev), "{ev} not in ALL");
210        }
211        assert_eq!(SofiaEventSubclass::GATEWAY_EVENTS.len(), 4);
212    }
213
214    #[test]
215    fn from_str_rejects_unknown() {
216        assert!("sofia::nonexistent"
217            .parse::<SofiaEventSubclass>()
218            .is_err());
219        assert!("register"
220            .parse::<SofiaEventSubclass>()
221            .is_err());
222        assert!(""
223            .parse::<SofiaEventSubclass>()
224            .is_err());
225    }
226
227    #[test]
228    fn from_str_is_case_sensitive() {
229        assert!("sofia::Register"
230            .parse::<SofiaEventSubclass>()
231            .is_err());
232        assert!("SOFIA::REGISTER"
233            .parse::<SofiaEventSubclass>()
234            .is_err());
235    }
236}