1use crate::{Result, Service};
7use serde::{Deserialize, Serialize};
8use std::net::IpAddr;
9use std::time::{Duration, SystemTime};
10
11#[derive(Debug, Clone)]
13pub struct EnrichedEvent<T> {
14 pub registration_id: Option<u64>,
16
17 pub speaker_ip: IpAddr,
19
20 pub service: Service,
22
23 pub event_source: EventSource,
25
26 pub timestamp: SystemTime,
28
29 pub event_data: T,
31}
32
33impl<T> EnrichedEvent<T> {
34 pub fn new(
36 speaker_ip: IpAddr,
37 service: Service,
38 event_source: EventSource,
39 event_data: T,
40 ) -> Self {
41 Self {
42 registration_id: None,
43 speaker_ip,
44 service,
45 event_source,
46 timestamp: SystemTime::now(),
47 event_data,
48 }
49 }
50
51 pub fn with_registration_id(
53 registration_id: u64,
54 speaker_ip: IpAddr,
55 service: Service,
56 event_source: EventSource,
57 event_data: T,
58 ) -> Self {
59 Self {
60 registration_id: Some(registration_id),
61 speaker_ip,
62 service,
63 event_source,
64 timestamp: SystemTime::now(),
65 event_data,
66 }
67 }
68
69 pub fn map<U, F>(self, f: F) -> EnrichedEvent<U>
71 where
72 F: FnOnce(T) -> U,
73 {
74 EnrichedEvent {
75 registration_id: self.registration_id,
76 speaker_ip: self.speaker_ip,
77 service: self.service,
78 event_source: self.event_source,
79 timestamp: self.timestamp,
80 event_data: f(self.event_data),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub enum EventSource {
88 UPnPNotification {
90 subscription_id: String,
92 },
93
94 PollingDetection {
96 poll_interval: Duration,
98 },
99
100 ResyncOperation,
102}
103
104pub trait EventParser: Send + Sync {
106 type EventData: Send + Sync + 'static;
108
109 fn parse_upnp_event(&self, xml: &str) -> Result<Self::EventData>;
111
112 fn service_type(&self) -> Service;
114}
115
116#[derive(Default)]
118pub struct EventParserRegistry {
119 parsers: std::collections::HashMap<Service, Box<dyn EventParserDyn>>,
120}
121
122impl EventParserRegistry {
123 pub fn new() -> Self {
125 Self::default()
126 }
127
128 pub fn register<P>(&mut self, parser: P)
130 where
131 P: EventParser + 'static,
132 P::EventData: 'static,
133 {
134 let service = parser.service_type();
135 self.parsers
136 .insert(service, Box::new(ParserWrapper::new(parser)));
137 }
138
139 pub fn get_parser(&self, service: &Service) -> Option<&dyn EventParserDyn> {
141 self.parsers.get(service).map(|p| p.as_ref())
142 }
143
144 pub fn has_parser(&self, service: &Service) -> bool {
146 self.parsers.contains_key(service)
147 }
148
149 pub fn supported_services(&self) -> Vec<Service> {
151 self.parsers.keys().cloned().collect()
152 }
153}
154
155pub trait EventParserDyn: Send + Sync {
157 fn parse_upnp_event_dyn(&self, xml: &str) -> Result<Box<dyn std::any::Any + Send + Sync>>;
159
160 fn service_type(&self) -> Service;
162}
163
164struct ParserWrapper<P> {
166 parser: P,
167}
168
169impl<P> ParserWrapper<P>
170where
171 P: EventParser,
172 P::EventData: 'static,
173{
174 fn new(parser: P) -> Self {
175 Self { parser }
176 }
177}
178
179impl<P> EventParserDyn for ParserWrapper<P>
180where
181 P: EventParser + Send + Sync,
182 P::EventData: Send + Sync + 'static,
183{
184 fn parse_upnp_event_dyn(&self, xml: &str) -> Result<Box<dyn std::any::Any + Send + Sync>> {
185 let event_data = self.parser.parse_upnp_event(xml)?;
186 Ok(Box::new(event_data))
187 }
188
189 fn service_type(&self) -> Service {
190 self.parser.service_type()
191 }
192}
193
194pub fn extract_xml_value(xml: &str, tag: &str) -> Option<String> {
197 let start_tag = format!("<{tag}>");
198 let end_tag = format!("</{tag}>");
199
200 if let Some(start_pos) = xml.find(&start_tag) {
201 let content_start = start_pos + start_tag.len();
202 if let Some(end_pos) = xml[content_start..].find(&end_tag) {
203 let value = xml[content_start..content_start + end_pos].trim();
204 if !value.is_empty() {
205 return Some(value.to_string());
206 }
207 }
208 }
209 None
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[derive(Debug, Clone, PartialEq)]
217 struct TestEventData {
218 value: String,
219 }
220
221 struct TestParser;
222
223 impl EventParser for TestParser {
224 type EventData = TestEventData;
225
226 fn parse_upnp_event(&self, xml: &str) -> Result<Self::EventData> {
227 Ok(TestEventData {
228 value: xml.to_string(),
229 })
230 }
231
232 fn service_type(&self) -> Service {
233 Service::AVTransport
234 }
235 }
236
237 #[test]
238 fn test_enriched_event_creation() {
239 let ip: IpAddr = "192.168.1.100".parse().unwrap();
240 let source = EventSource::UPnPNotification {
241 subscription_id: "uuid:123".to_string(),
242 };
243 let data = TestEventData {
244 value: "test".to_string(),
245 };
246
247 let event = EnrichedEvent::new(ip, Service::AVTransport, source, data.clone());
248
249 assert_eq!(event.speaker_ip, ip);
250 assert_eq!(event.service, Service::AVTransport);
251 assert_eq!(event.event_data, data);
252 assert!(event.registration_id.is_none());
253 }
254
255 #[test]
256 fn test_enriched_event_with_registration_id() {
257 let ip: IpAddr = "192.168.1.100".parse().unwrap();
258 let source = EventSource::UPnPNotification {
259 subscription_id: "uuid:123".to_string(),
260 };
261 let data = TestEventData {
262 value: "test".to_string(),
263 };
264
265 let event =
266 EnrichedEvent::with_registration_id(42, ip, Service::AVTransport, source, data.clone());
267
268 assert_eq!(event.registration_id, Some(42));
269 assert_eq!(event.event_data, data);
270 }
271
272 #[test]
273 fn test_event_mapping() {
274 let ip: IpAddr = "192.168.1.100".parse().unwrap();
275 let source = EventSource::UPnPNotification {
276 subscription_id: "uuid:123".to_string(),
277 };
278 let data = TestEventData {
279 value: "test".to_string(),
280 };
281
282 let event = EnrichedEvent::new(ip, Service::AVTransport, source, data);
283 let mapped_event = event.map(|data| data.value.len());
284
285 assert_eq!(mapped_event.event_data, 4); }
287
288 #[test]
289 fn test_parser_registry() {
290 let mut registry = EventParserRegistry::new();
291 assert!(!registry.has_parser(&Service::AVTransport));
292
293 registry.register(TestParser);
294 assert!(registry.has_parser(&Service::AVTransport));
295
296 let supported = registry.supported_services();
297 assert!(supported.contains(&Service::AVTransport));
298 }
299
300 #[test]
301 fn test_xml_value_extraction() {
302 let xml = "<Test>value</Test>";
303 assert_eq!(extract_xml_value(xml, "Test"), Some("value".to_string()));
304
305 let xml = "<Test></Test>";
306 assert_eq!(extract_xml_value(xml, "Test"), None);
307
308 let xml = "<Other>value</Other>";
309 assert_eq!(extract_xml_value(xml, "Test"), None);
310 }
311}