Skip to main content

webex_message_handler/
types.rs

1//! Data types for webex-message-handler.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8
9/// Networking mode for the handler.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum NetworkMode {
12    /// Use built-in HTTP and WebSocket libraries (default).
13    Native,
14    /// Use provided fetch and WebSocket factory functions.
15    Injected,
16}
17
18impl Default for NetworkMode {
19    fn default() -> Self {
20        Self::Native
21    }
22}
23
24/// HTTP request for injected fetch function.
25#[derive(Debug, Clone)]
26pub struct FetchRequest {
27    pub url: String,
28    pub method: String,
29    pub headers: HashMap<String, String>,
30    pub body: Option<String>,
31}
32
33/// HTTP response from injected fetch function.
34pub struct FetchResponse {
35    pub status: u16,
36    pub ok: bool,
37    pub body: Vec<u8>,
38}
39
40/// Custom fetch function for injected mode.
41pub type FetchFn = Arc<
42    dyn Fn(FetchRequest) -> Pin<Box<dyn Future<Output = Result<FetchResponse, Box<dyn std::error::Error + Send + Sync>>> + Send>>
43        + Send
44        + Sync,
45>;
46
47/// WebSocket interface for injected mode.
48pub trait InjectedWebSocket: Send + Sync {
49    fn send(&self, data: String) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + '_>>;
50    fn receive(&self) -> Pin<Box<dyn Future<Output = Result<String, Box<dyn std::error::Error + Send + Sync>>> + Send + '_>>;
51    fn close(&self) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + '_>>;
52}
53
54/// WebSocket factory for injected mode.
55pub type WebSocketFactory = Arc<
56    dyn Fn(String) -> Pin<Box<dyn Future<Output = Result<Box<dyn InjectedWebSocket>, Box<dyn std::error::Error + Send + Sync>>> + Send>>
57        + Send
58        + Sync,
59>;
60
61/// Configuration for WebexMessageHandler.
62#[derive(Clone)]
63pub struct Config {
64    /// Webex bot or user access token (required).
65    pub token: String,
66
67    /// Networking mode: Native or Injected (default: Native).
68    pub mode: NetworkMode,
69
70    /// Optional HTTP client for proxy support (native mode only).
71    /// If None, a default client will be created.
72    pub client: Option<reqwest::Client>,
73
74    /// Custom fetch function for all HTTP requests (injected mode).
75    pub fetch: Option<FetchFn>,
76
77    /// Custom WebSocket factory (injected mode).
78    pub web_socket_factory: Option<WebSocketFactory>,
79
80    /// Mercury ping interval in seconds (default: 15).
81    pub ping_interval: f64,
82
83    /// Pong response timeout in seconds (default: 14).
84    pub pong_timeout: f64,
85
86    /// Max reconnect backoff in seconds (default: 32).
87    pub reconnect_backoff_max: f64,
88
89    /// Max consecutive reconnection attempts (default: 10).
90    pub max_reconnect_attempts: u32,
91}
92
93impl Default for Config {
94    fn default() -> Self {
95        Self {
96            token: String::new(),
97            mode: NetworkMode::Native,
98            client: None,
99            fetch: None,
100            web_socket_factory: None,
101            ping_interval: 15.0,
102            pong_timeout: 14.0,
103            reconnect_backoff_max: 32.0,
104            max_reconnect_attempts: 10,
105        }
106    }
107}
108
109/// Result of WDM device registration.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct DeviceRegistration {
112    /// Mercury WebSocket URL.
113    #[serde(rename = "webSocketUrl")]
114    pub web_socket_url: String,
115
116    /// Device URL (used as clientId for KMS).
117    #[serde(rename = "url")]
118    pub device_url: String,
119
120    /// Bot's user ID.
121    #[serde(rename = "userId")]
122    pub user_id: String,
123
124    /// Service catalog from WDM.
125    #[serde(default)]
126    pub services: HashMap<String, String>,
127
128    /// Encryption service URL extracted from services.
129    #[serde(skip)]
130    pub encryption_service_url: String,
131}
132
133/// Actor in a Mercury activity.
134#[derive(Debug, Clone, Default, Serialize, Deserialize)]
135pub struct MercuryActor {
136    #[serde(default)]
137    pub id: String,
138
139    #[serde(rename = "objectType", default)]
140    pub object_type: String,
141
142    #[serde(rename = "emailAddress", default)]
143    pub email_address: Option<String>,
144}
145
146/// Object in a Mercury activity.
147#[derive(Debug, Clone, Default, Serialize, Deserialize)]
148pub struct MercuryObject {
149    #[serde(default)]
150    pub id: String,
151
152    #[serde(rename = "objectType", default)]
153    pub object_type: String,
154
155    #[serde(rename = "displayName", default)]
156    pub display_name: Option<String>,
157
158    #[serde(default)]
159    pub content: Option<String>,
160
161    #[serde(rename = "encryptionKeyUrl", default)]
162    pub encryption_key_url: Option<String>,
163}
164
165/// Target in a Mercury activity.
166#[derive(Debug, Clone, Default, Serialize, Deserialize)]
167pub struct MercuryTarget {
168    #[serde(default)]
169    pub id: String,
170
171    #[serde(rename = "objectType", default)]
172    pub object_type: String,
173
174    #[serde(rename = "encryptionKeyUrl", default)]
175    pub encryption_key_url: Option<String>,
176
177    #[serde(default)]
178    pub tags: Vec<String>,
179}
180
181/// A Mercury conversation activity.
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct MercuryActivity {
184    #[serde(default)]
185    pub id: String,
186
187    #[serde(default)]
188    pub verb: String,
189
190    #[serde(default)]
191    pub actor: MercuryActor,
192
193    #[serde(default)]
194    pub object: MercuryObject,
195
196    #[serde(default)]
197    pub target: MercuryTarget,
198
199    #[serde(default)]
200    pub published: String,
201
202    #[serde(rename = "encryptionKeyUrl", default)]
203    pub encryption_key_url: Option<String>,
204}
205
206/// A decrypted Webex message.
207#[derive(Debug, Clone)]
208pub struct DecryptedMessage {
209    /// Unique message ID.
210    pub id: String,
211
212    /// Conversation/space ID.
213    pub room_id: String,
214
215    /// Sender's user ID.
216    pub person_id: String,
217
218    /// Sender's email address.
219    pub person_email: String,
220
221    /// Decrypted plain text.
222    pub text: String,
223
224    /// Decrypted HTML content (rich text messages).
225    pub html: Option<String>,
226
227    /// ISO 8601 timestamp.
228    pub created: String,
229
230    /// "direct", "group", or None.
231    pub room_type: Option<String>,
232
233    /// Full decrypted activity for advanced use.
234    pub raw: MercuryActivity,
235}
236
237/// A deleted Webex message notification.
238#[derive(Debug, Clone)]
239pub struct DeletedMessage {
240    pub message_id: String,
241    pub room_id: String,
242    pub person_id: String,
243}
244
245/// Overall connection state.
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum ConnectionStatus {
248    Connected,
249    Connecting,
250    Reconnecting,
251    Disconnected,
252}
253
254impl std::fmt::Display for ConnectionStatus {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        match self {
257            Self::Connected => write!(f, "connected"),
258            Self::Connecting => write!(f, "connecting"),
259            Self::Reconnecting => write!(f, "reconnecting"),
260            Self::Disconnected => write!(f, "disconnected"),
261        }
262    }
263}
264
265/// Structured health check of all connection subsystems.
266#[derive(Debug, Clone)]
267pub struct HandlerStatus {
268    /// Overall connection state.
269    pub status: ConnectionStatus,
270
271    /// Whether the Mercury WebSocket is currently open.
272    pub web_socket_open: bool,
273
274    /// Whether the KMS encryption context has been established.
275    pub kms_initialized: bool,
276
277    /// Whether the device is registered with WDM.
278    pub device_registered: bool,
279
280    /// Current auto-reconnect attempt number (0 if not reconnecting).
281    pub reconnect_attempt: u32,
282}