rustant_core/channels/
webchat.rs1use super::{
8 Channel, ChannelCapabilities, ChannelMessage, ChannelStatus, ChannelType, MessageId,
9 StreamingMode,
10};
11use crate::error::RustantError;
12use crate::gateway::{GatewayEvent, SharedGateway};
13use async_trait::async_trait;
14use serde::{Deserialize, Serialize};
15use std::sync::{Arc, Mutex};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct WebChatConfig {
20 pub enabled: bool,
21}
22
23impl Default for WebChatConfig {
24 fn default() -> Self {
25 Self { enabled: true }
26 }
27}
28
29pub struct WebChatChannel {
31 gateway: Option<SharedGateway>,
32 status: ChannelStatus,
33 name: String,
34 outbox: Arc<Mutex<Vec<ChannelMessage>>>,
35}
36
37impl WebChatChannel {
38 pub fn new() -> Self {
39 Self {
40 gateway: None,
41 status: ChannelStatus::Disconnected,
42 name: "webchat".to_string(),
43 outbox: Arc::new(Mutex::new(Vec::new())),
44 }
45 }
46
47 pub fn with_gateway(mut self, gw: SharedGateway) -> Self {
49 self.gateway = Some(gw);
50 self
51 }
52
53 pub fn with_name(mut self, name: impl Into<String>) -> Self {
54 self.name = name.into();
55 self
56 }
57}
58
59impl Default for WebChatChannel {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65#[async_trait]
66impl Channel for WebChatChannel {
67 fn name(&self) -> &str {
68 &self.name
69 }
70
71 fn channel_type(&self) -> ChannelType {
72 ChannelType::WebChat
73 }
74
75 async fn connect(&mut self) -> Result<(), RustantError> {
76 self.status = ChannelStatus::Connected;
77 Ok(())
78 }
79
80 async fn disconnect(&mut self) -> Result<(), RustantError> {
81 self.status = ChannelStatus::Disconnected;
82 Ok(())
83 }
84
85 async fn send_message(&self, msg: ChannelMessage) -> Result<MessageId, RustantError> {
86 let id = msg.id.clone();
87
88 if let Some(ref gw) = self.gateway {
90 let text = msg.content.as_text().unwrap_or("").to_string();
91 let gw = gw.lock().await;
92 gw.broadcast(GatewayEvent::AssistantMessage { content: text });
93 }
94
95 self.outbox.lock().unwrap().push(msg);
97 Ok(id)
98 }
99
100 async fn receive_messages(&self) -> Result<Vec<ChannelMessage>, RustantError> {
101 Ok(Vec::new())
104 }
105
106 fn status(&self) -> ChannelStatus {
107 self.status
108 }
109
110 fn capabilities(&self) -> ChannelCapabilities {
111 ChannelCapabilities {
112 supports_threads: false,
113 supports_reactions: false,
114 supports_files: true,
115 supports_voice: false,
116 supports_video: false,
117 max_message_length: None,
118 supports_editing: false,
119 supports_deletion: false,
120 }
121 }
122
123 fn streaming_mode(&self) -> StreamingMode {
124 StreamingMode::WebSocket
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::channels::ChannelUser;
132
133 #[tokio::test]
134 async fn test_webchat_lifecycle() {
135 let mut ch = WebChatChannel::new();
136 assert_eq!(ch.status(), ChannelStatus::Disconnected);
137
138 ch.connect().await.unwrap();
139 assert!(ch.is_connected());
140
141 ch.disconnect().await.unwrap();
142 assert!(!ch.is_connected());
143 }
144
145 #[tokio::test]
146 async fn test_webchat_send_message() {
147 let ch = WebChatChannel::new();
148 let outbox = ch.outbox.clone();
149
150 let sender = ChannelUser::new("web-user", ChannelType::WebChat);
151 let msg =
152 ChannelMessage::text(ChannelType::WebChat, "session-1", sender, "Hello from web!");
153 let id = ch.send_message(msg).await.unwrap();
154 assert!(!id.0.is_empty());
155
156 let outbox = outbox.lock().unwrap();
157 assert_eq!(outbox.len(), 1);
158 assert_eq!(outbox[0].content.as_text(), Some("Hello from web!"));
159 }
160
161 #[tokio::test]
162 async fn test_webchat_receive_empty() {
163 let ch = WebChatChannel::new();
164 let msgs = ch.receive_messages().await.unwrap();
165 assert!(msgs.is_empty());
166 }
167
168 #[test]
169 fn test_webchat_capabilities() {
170 let ch = WebChatChannel::new();
171 let caps = ch.capabilities();
172 assert!(!caps.supports_threads);
173 assert!(caps.supports_files);
174 assert!(caps.max_message_length.is_none());
175 }
176
177 #[test]
178 fn test_webchat_streaming_mode() {
179 let ch = WebChatChannel::new();
180 assert_eq!(ch.streaming_mode(), StreamingMode::WebSocket);
181 }
182}