guts_realtime/
lib.rs

1//! # Guts Real-time
2//!
3//! Real-time WebSocket support for the Guts code collaboration platform.
4//!
5//! This crate provides WebSocket-based real-time updates for repository events,
6//! enabling live notifications and instant UI updates without page refresh.
7//!
8//! ## Features
9//!
10//! - **Event Hub**: Central management of WebSocket connections
11//! - **Subscriptions**: Channel-based subscription model
12//! - **Event Broadcasting**: Efficient event distribution to subscribed clients
13//! - **Notifications**: User notification system
14//!
15//! ## Channel Types
16//!
17//! - `repo:owner/name` - All events for a repository
18//! - `repo:owner/name/prs` - Pull request events only
19//! - `repo:owner/name/issues` - Issue events only
20//! - `user:username` - User notifications
21//! - `org:orgname` - Organization events
22//!
23//! ## Example
24//!
25//! ```rust
26//! use guts_realtime::{EventHub, EventKind};
27//! use std::sync::Arc;
28//!
29//! // Create the event hub
30//! let hub = Arc::new(EventHub::new());
31//!
32//! // Connect a client
33//! let (client, receiver) = hub.connect().unwrap();
34//!
35//! // Subscribe to a repository
36//! hub.handle_command(
37//!     &client,
38//!     guts_realtime::ClientCommand::Subscribe {
39//!         channel: "repo:alice/myrepo".to_string(),
40//!     },
41//! ).unwrap();
42//!
43//! // Emit an event
44//! hub.emit_event(
45//!     "repo:alice/myrepo".to_string(),
46//!     EventKind::Push,
47//!     serde_json::json!({
48//!         "ref": "refs/heads/main",
49//!         "before": "abc123",
50//!         "after": "def456"
51//!     }),
52//! );
53//! ```
54//!
55//! ## WebSocket Protocol
56//!
57//! ### Client -> Server Messages
58//!
59//! ```json
60//! // Subscribe to a channel
61//! {"type": "subscribe", "channel": "repo:owner/name"}
62//!
63//! // Unsubscribe from a channel
64//! {"type": "unsubscribe", "channel": "repo:owner/name"}
65//!
66//! // Ping for keepalive
67//! {"type": "ping"}
68//! ```
69//!
70//! ### Server -> Client Messages
71//!
72//! ```json
73//! // Subscription confirmed
74//! {"type": "subscribed", "channel": "repo:owner/name"}
75//!
76//! // Event notification
77//! {"type": "event", "channel": "repo:owner/name", "event": "push", ...}
78//!
79//! // Pong response
80//! {"type": "pong"}
81//! ```
82//!
83//! ## Architecture
84//!
85//! ```text
86//! ┌─────────────────────────────────────────┐
87//! │              EventHub                    │
88//! │  ┌─────────────────────────────────┐    │
89//! │  │         Clients Map              │    │
90//! │  │  client_id -> Client            │    │
91//! │  │    └─> subscriptions            │    │
92//! │  │    └─> message sender           │    │
93//! │  └─────────────────────────────────┘    │
94//! │                  │                       │
95//! │  ┌───────────────▼───────────────────┐  │
96//! │  │     Broadcast Channel             │  │
97//! │  │  (for external subscribers)       │  │
98//! │  └───────────────────────────────────┘  │
99//! └─────────────────────────────────────────┘
100//! ```
101
102pub mod client;
103pub mod error;
104pub mod event;
105pub mod hub;
106pub mod notification;
107pub mod subscription;
108
109// Re-export main types
110pub use client::{Client, ClientId, ClientReceiver};
111pub use error::RealtimeError;
112pub use event::{
113    CommentEventData, EventKind, IssueEventData, PullRequestEventData, PushEventData,
114    RealtimeEvent, ReviewEventData,
115};
116pub use hub::{ClientCommand, EventHub, HubStats, ServerMessage};
117pub use notification::{
118    Notification, NotificationMetadata, NotificationPreferences, NotificationType,
119};
120pub use subscription::{Channel, ChannelType, ClientSubscriptions, MAX_SUBSCRIPTIONS_PER_CLIENT};
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_public_api() {
128        // Test that main types are accessible
129        let hub = EventHub::new();
130        assert_eq!(hub.connection_count(), 0);
131    }
132
133    #[tokio::test]
134    async fn test_full_flow() {
135        let hub = EventHub::new();
136
137        // Connect
138        let (client, mut rx) = hub.connect().unwrap();
139        assert_eq!(hub.connection_count(), 1);
140
141        // Subscribe
142        let result = hub.handle_command(
143            &client,
144            ClientCommand::Subscribe {
145                channel: "repo:alice/myrepo".to_string(),
146            },
147        );
148        assert!(result.is_ok());
149
150        // Emit event
151        hub.emit_event(
152            "repo:alice/myrepo".to_string(),
153            EventKind::Push,
154            serde_json::json!({"ref": "refs/heads/main"}),
155        );
156
157        // Verify received
158        let msg = rx.try_recv();
159        assert!(msg.is_ok());
160
161        // Disconnect
162        hub.disconnect(&client.id);
163        assert_eq!(hub.connection_count(), 0);
164    }
165}