1use std::fmt;
4use std::str::FromStr;
5
6#[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#[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 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 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 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 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}