Skip to main content

hsipc/
event.rs

1//! Event trait and subscription system for publish/subscribe pattern
2
3use crate::Result;
4use async_trait::async_trait;
5use dashmap::DashMap;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use uuid::Uuid;
11
12/// Trait for events that can be published
13pub trait Event: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {
14    /// Get the topic for this event
15    fn topic(&self) -> String;
16}
17
18/// Subscriber trait for handling events
19#[async_trait]
20pub trait Subscriber: Send + Sync + 'static {
21    /// Topic pattern to subscribe to
22    fn topic_pattern(&self) -> &str;
23
24    /// Handle an event
25    async fn handle(&mut self, topic: &str, payload: Vec<u8>) -> Result<()>;
26}
27
28/// Sync subscriber trait
29pub trait SyncSubscriber: Send + Sync + 'static {
30    /// Topic pattern to subscribe to
31    fn topic_pattern(&self) -> &str;
32
33    /// Handle an event synchronously
34    fn handle_sync(&mut self, topic: &str, payload: Vec<u8>) -> Result<()>;
35}
36
37/// Subscription handle
38pub struct Subscription {
39    pub id: Uuid,
40    pub topic_pattern: String,
41    registry: Arc<SubscriptionRegistry>,
42}
43
44impl Subscription {
45    /// Unsubscribe
46    pub async fn unsubscribe(self) -> Result<()> {
47        self.registry.unsubscribe(&self.id).await
48    }
49}
50
51impl Drop for Subscription {
52    fn drop(&mut self) {
53        // Queue unsubscribe for later processing
54        let id = self.id;
55        let registry = self.registry.clone();
56        tokio::spawn(async move {
57            let _ = registry.unsubscribe(&id).await;
58        });
59    }
60}
61
62/// Subscription registry
63pub struct SubscriptionRegistry {
64    /// Map of subscription ID to subscriber
65    subscribers: Arc<DashMap<Uuid, Box<dyn Subscriber>>>,
66
67    /// Map of topic pattern to subscription IDs
68    topic_subscriptions: Arc<RwLock<HashMap<String, Vec<Uuid>>>>,
69}
70
71impl SubscriptionRegistry {
72    pub fn new() -> Self {
73        Self {
74            subscribers: Arc::new(DashMap::new()),
75            topic_subscriptions: Arc::new(RwLock::new(HashMap::new())),
76        }
77    }
78
79    /// Subscribe to a topic pattern
80    pub async fn subscribe<S: Subscriber>(&self, subscriber: S) -> Result<Subscription> {
81        let id = Uuid::new_v4();
82        let topic_pattern = subscriber.topic_pattern().to_string();
83
84        // Store subscriber
85        self.subscribers.insert(id, Box::new(subscriber));
86
87        // Update topic mapping
88        let mut topics = self.topic_subscriptions.write().await;
89        topics
90            .entry(topic_pattern.clone())
91            .or_insert_with(Vec::new)
92            .push(id);
93
94        Ok(Subscription {
95            id,
96            topic_pattern,
97            registry: Arc::new(self.clone()),
98        })
99    }
100
101    /// Unsubscribe
102    pub async fn unsubscribe(&self, id: &Uuid) -> Result<()> {
103        // Remove subscriber
104        if let Some((_, subscriber)) = self.subscribers.remove(id) {
105            let topic_pattern = subscriber.topic_pattern();
106
107            // Remove from topic mapping
108            let mut topics = self.topic_subscriptions.write().await;
109            if let Some(subs) = topics.get_mut(topic_pattern) {
110                subs.retain(|sub_id| sub_id != id);
111                if subs.is_empty() {
112                    topics.remove(topic_pattern);
113                }
114            }
115        }
116
117        Ok(())
118    }
119
120    /// Publish an event to matching subscribers
121    pub async fn publish(&self, topic: &str, payload: Vec<u8>) -> Result<()> {
122        let topics = self.topic_subscriptions.read().await;
123        let mut matching_ids = Vec::new();
124
125        // Find matching subscriptions
126        for (pattern, ids) in topics.iter() {
127            if topic_matches(topic, pattern) {
128                matching_ids.extend(ids.iter().copied());
129            }
130        }
131        drop(topics);
132
133        // Deliver to subscribers
134        for id in matching_ids {
135            if let Some(mut subscriber) = self.subscribers.get_mut(&id) {
136                // Clone payload for each subscriber
137                let _ = subscriber.handle(topic, payload.clone()).await;
138            }
139        }
140
141        Ok(())
142    }
143}
144
145impl Clone for SubscriptionRegistry {
146    fn clone(&self) -> Self {
147        Self {
148            subscribers: self.subscribers.clone(),
149            topic_subscriptions: self.topic_subscriptions.clone(),
150        }
151    }
152}
153
154impl Default for SubscriptionRegistry {
155    fn default() -> Self {
156        Self::new()
157    }
158}
159
160/// Check if a topic matches a pattern
161/// Supports wildcards: + (single level), # (multi level)
162fn topic_matches(topic: &str, pattern: &str) -> bool {
163    // Exact match
164    if topic == pattern {
165        return true;
166    }
167
168    let topic_parts: Vec<&str> = topic.split('/').collect();
169    let pattern_parts: Vec<&str> = pattern.split('/').collect();
170
171    let mut t_idx = 0;
172    let mut p_idx = 0;
173
174    while p_idx < pattern_parts.len() && t_idx < topic_parts.len() {
175        match pattern_parts[p_idx] {
176            "#" => return true, // Multi-level wildcard matches everything
177            "+" => {
178                // Single-level wildcard matches one part
179                t_idx += 1;
180                p_idx += 1;
181            }
182            part => {
183                if part != topic_parts[t_idx] {
184                    return false;
185                }
186                t_idx += 1;
187                p_idx += 1;
188            }
189        }
190    }
191
192    // Both should be exhausted for a match
193    t_idx == topic_parts.len() && p_idx == pattern_parts.len()
194}
195
196/// Adapter for sync subscribers
197pub struct SyncSubscriberAdapter<S: SyncSubscriber> {
198    inner: S,
199}
200
201impl<S: SyncSubscriber> SyncSubscriberAdapter<S> {
202    pub fn new(subscriber: S) -> Self {
203        Self { inner: subscriber }
204    }
205}
206
207#[async_trait]
208impl<S: SyncSubscriber> Subscriber for SyncSubscriberAdapter<S> {
209    fn topic_pattern(&self) -> &str {
210        self.inner.topic_pattern()
211    }
212
213    async fn handle(&mut self, _topic: &str, _payload: Vec<u8>) -> Result<()> {
214        // Note: This is a simplified implementation
215        // In practice, we'd need better integration between sync and async
216        Ok(())
217    }
218}