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
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[derive(Debug, Clone, PartialEq)]
199 struct TestEventData {
200 value: String,
201 }
202
203 struct TestParser;
204
205 impl EventParser for TestParser {
206 type EventData = TestEventData;
207
208 fn parse_upnp_event(&self, xml: &str) -> Result<Self::EventData> {
209 Ok(TestEventData {
210 value: xml.to_string(),
211 })
212 }
213
214 fn service_type(&self) -> Service {
215 Service::AVTransport
216 }
217 }
218
219 #[test]
220 fn test_enriched_event_creation() {
221 let ip: IpAddr = "192.168.1.100".parse().unwrap();
222 let source = EventSource::UPnPNotification {
223 subscription_id: "uuid:123".to_string(),
224 };
225 let data = TestEventData {
226 value: "test".to_string(),
227 };
228
229 let event = EnrichedEvent::new(ip, Service::AVTransport, source, data.clone());
230
231 assert_eq!(event.speaker_ip, ip);
232 assert_eq!(event.service, Service::AVTransport);
233 assert_eq!(event.event_data, data);
234 assert!(event.registration_id.is_none());
235 }
236
237 #[test]
238 fn test_enriched_event_with_registration_id() {
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 =
248 EnrichedEvent::with_registration_id(42, ip, Service::AVTransport, source, data.clone());
249
250 assert_eq!(event.registration_id, Some(42));
251 assert_eq!(event.event_data, data);
252 }
253
254 #[test]
255 fn test_event_mapping() {
256 let ip: IpAddr = "192.168.1.100".parse().unwrap();
257 let source = EventSource::UPnPNotification {
258 subscription_id: "uuid:123".to_string(),
259 };
260 let data = TestEventData {
261 value: "test".to_string(),
262 };
263
264 let event = EnrichedEvent::new(ip, Service::AVTransport, source, data);
265 let mapped_event = event.map(|data| data.value.len());
266
267 assert_eq!(mapped_event.event_data, 4); }
269
270 #[test]
271 fn test_parser_registry() {
272 let mut registry = EventParserRegistry::new();
273 assert!(!registry.has_parser(&Service::AVTransport));
274
275 registry.register(TestParser);
276 assert!(registry.has_parser(&Service::AVTransport));
277
278 let supported = registry.supported_services();
279 assert!(supported.contains(&Service::AVTransport));
280 }
281}