1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// Copyright (C) 2024 Joaquín Béjar García
// Portions of this file are derived from lightstreamer-client
// Copyright (C) 2024 Daniel López Azaña
// Original project: https://github.com/daniloaz/lightstreamer-client
//
// This file is part of lightstreamer-rs.
//
// lightstreamer-rs is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// lightstreamer-rs is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with lightstreamer-rs. If not, see <https://www.gnu.org/licenses/>.
//! # Lightstreamer Rust Client
//!
//! This project is a Rust implementation of the Lightstreamer TLCP (Text-based Live Connections Protocol). It provides a robust client SDK to interact with Lightstreamer servers, enabling real-time data streaming for financial applications, IoT systems, and other use cases requiring live data updates. While it was initially developed to support the [ig_trading_api](https://github.com/joaquinbejar/ig_trading_api) project, it has evolved into a more comprehensive SDK with broader applicability.
//!
//! ## Attribution
//!
//! This project contains code derived from [lightstreamer-client](https://github.com/daniloaz/lightstreamer-client)
//! by Daniel López Azaña (@daniloaz), originally published under GPL-3.0-only license in February 2024.
//!
//! ## About Lightstreamer
//!
//! Lightstreamer is a high-performance real-time messaging server that provides several key features:
//! - Real-time data streaming with optimized bandwidth usage
//! - Support for various transport mechanisms (WebSockets, HTTP streaming, etc.)
//! - Different subscription modes for various data delivery patterns
//! - Robust connection management with automatic recovery
//! - Scalable architecture for high-volume applications
//!
//! ## Features
//!
//! This Rust client SDK provides the following capabilities:
//!
//! - **Connection Management**:
//! - Full-duplex WebSocket-based connection mode
//! - Automatic reconnection with configurable retry policies
//! - Session recovery after temporary disconnections
//! - Connection status monitoring and event notifications
//! - Proxy support for enterprise environments
//!
//! - **Subscription Capabilities**:
//! - Support for multiple subscription modes (MERGE, DISTINCT, COMMAND, RAW)
//! - Subscription to individual items or item groups
//! - Field schema definition for structured data
//! - Real-time item updates with change detection
//! - Snapshot support for initial state recovery
//!
//! - **Configuration Options**:
//! - Extensive connection options (polling intervals, timeouts, etc.)
//! - Bandwidth control and throttling
//! - Custom HTTP headers for authentication
//! - Logging configuration for debugging
//!
//! - **Event Handling**:
//! - Comprehensive event listener system
//! - Subscription lifecycle events (subscription, unsubscription)
//! - Item update notifications with field-level change detection
//! - Connection status change notifications
//! - Error handling and reporting
//!
//! ## Implementation Status
//!
//! The current implementation supports most core features of the Lightstreamer protocol, with a focus on the WebSocket transport mechanism and the MERGE subscription mode. While initially developed for specific trading API requirements, the library has expanded to include:
//!
//! - All subscription modes (MERGE, DISTINCT, COMMAND, RAW)
//! - Robust error handling and recovery mechanisms
//! - Comprehensive configuration options
//! - Thread-safe asynchronous operation using Tokio
//!
//! Some advanced features that may be implemented in future versions include:
//!
//! - Message sending capabilities (MPN)
//! - Client-side filtering and frequency limitations
//! - Additional transport mechanisms beyond WebSockets
//! - Enhanced security features
//!
//! ## Installation
//!
//! To use this SDK in your Rust project, add the following dependency to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! lightstreamer-rs = "0.3.0"
//! ```
//!
//! ## Usage
//!
//! ### Quick Start (Simplified API)
//!
//! For most use cases, use the simplified `SimpleClient` API:
//!
//! ```ignore
//! use lightstreamer_rs::client::{ClientConfig, SimpleClient, SubscriptionParams};
//! use lightstreamer_rs::subscription::SubscriptionMode;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), lightstreamer_rs::utils::LightstreamerError> {
//! // 1. Create configuration
//! let config = ClientConfig::new("http://push.lightstreamer.com/lightstreamer")
//! .adapter_set("DEMO");
//!
//! // 2. Create client
//! let client = SimpleClient::new(config)?;
//!
//! // 3. Subscribe and get channel receiver
//! let params = SubscriptionParams::new(
//! SubscriptionMode::Merge,
//! vec!["item1".to_string(), "item2".to_string()],
//! vec!["last_price".to_string(), "time".to_string()],
//! ).data_adapter("QUOTE_ADAPTER");
//!
//! let mut receiver = client.subscribe(params).await?;
//!
//! // 4. Process updates asynchronously
//! tokio::spawn(async move {
//! while let Some(update) = receiver.recv().await {
//! println!("Price: {:?}", update.get_value("last_price"));
//! }
//! });
//!
//! // 5. Connect and run
//! client.connect().await?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Advanced Usage (Full API)
//!
//! Here's a comprehensive example of how to use the Lightstreamer Rust Client SDK:
//!
//! ```ignore
//! // This example shows how to use the Lightstreamer Rust client
//! use lightstreamer_rs::client::{LightstreamerClient, Transport};
//! use lightstreamer_rs::subscription::{Subscription, SubscriptionMode, SubscriptionListener, ItemUpdate};
//! use std::sync::Arc;
//! use tokio::sync::Notify;
//! use std::time::Duration;
//!
//! // Define a custom subscription listener
//! struct MySubscriptionListener;
//!
//! impl SubscriptionListener for MySubscriptionListener {
//! fn on_subscription(&self) {
//! info!("Subscription confirmed by the server");
//! }
//!
//! fn on_item_update(&self, update: ItemUpdate) {
//! info!("Received update for item: {}", update.get_item_name());
//! for field in update.get_fields() {
//! if let Some(value) = update.get_value(field) {
//! info!(" {} = {}", field, value);
//! }
//! }
//! }
//! }
//!
//! async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! // Create a new LightstreamerClient instance
//! let result = LightstreamerClient::new(
//! Some("ws://your-lightstreamer-server.com"), // Server address
//! Some("YOUR_ADAPTER_SET"), // Adapter set
//! None, // User (optional)
//! None, // Password (optional)
//! );
//!
//! let mut client = match result {
//! Ok(client) => client,
//! Err(e) => return Err(e),
//! };
//!
//! // Configure the connection details if needed
//! client.connection_details.set_user("YOUR_USERNAME");
//! client.connection_details.set_password("YOUR_PASSWORD");
//!
//! // Configure connection options if needed
//! client.connection_options.set_content_length(50000000);
//! client.connection_options.set_keepalive_interval(5);
//! client.connection_options.set_forced_transport(Some(Transport::WsStreaming));
//!
//! // Create a shutdown signal for graceful termination
//! let shutdown_signal = Arc::new(Notify::new());
//!
//! // Connect to the Lightstreamer server
//! if let Err(e) = client.connect(shutdown_signal.clone()).await {
//! return Err(e);
//! }
//!
//! // Create a subscription
//! let subscription_result = Subscription::new(
//! SubscriptionMode::Merge,
//! Some(vec!["item1".to_string(), "item2".to_string()]),
//! Some(vec!["field1".to_string(), "field2".to_string()]),
//! );
//!
//! let subscription = match subscription_result {
//! Ok(sub) => sub,
//! Err(e) => return Err(e),
//! };
//!
//! // Add a listener to the subscription (optional)
//! let listener = Box::new(MySubscriptionListener);
//! subscription.add_listener(listener);
//!
//! // Get the subscription sender from the client
//! // Note: This method might not exist in the current API, check the documentation
//! let subscription_sender = client.get_subscriptions()[0].clone();
//!
//! // Subscribe and get the subscription ID
//! let subscription_id_result = LightstreamerClient::subscribe_get_id(
//! subscription_sender.clone(),
//! subscription
//! ).await;
//!
//! let subscription_id = match subscription_id_result {
//! Ok(id) => id,
//! Err(e) => return Err(e),
//! };
//!
//! info!("Subscribed with ID: {}", subscription_id);
//!
//! // Wait for some time (in a real application, you would wait for a shutdown signal)
//! tokio::time::sleep(Duration::from_secs(5)).await;
//!
//! // Unsubscribe before disconnecting
//! LightstreamerClient::unsubscribe(subscription_sender, subscription_id).await;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Handling Client Events
//!
//! You can also add listeners to handle client events:
//!
//! Here's an example of implementing a client listener:
//!
//! ```ignore
//! // Note: ClientListener is a private trait, this is just for illustration
//! use lightstreamer_rs::client::model::ClientStatus;
//!
//! struct MyClientListener;
//!
//! // This is just an example of what the ClientListener trait might look like
//! // The actual implementation is internal to the library
//! trait ClientListener {
//! fn on_status_change(&self, status: &ClientStatus);
//! fn on_server_error(&self, error_code: i32, error_message: &str);
//! fn on_property_change(&self, property: &str);
//! }
//!
//! impl ClientListener for MyClientListener {
//! fn on_status_change(&self, status: &ClientStatus) {
//! info!("Client status changed to: {:?}", status);
//! }
//!
//! fn on_server_error(&self, error_code: i32, error_message: &str) {
//! info!("Server error: {} - {}", error_code, error_message);
//! }
//!
//! fn on_property_change(&self, property: &str) {
//! info!("Property changed: {}", property);
//! }
//! }
//!
//! // Then add the listener to your client
//! // client.add_listener(Box::new(MyClientListener));
//! ```
//!
//! ### Channel-Based Update Processing
//!
//! For asynchronous processing of updates in separate tasks, you can use the `ChannelSubscriptionListener`:
//!
//! ```ignore
//! use lightstreamer_rs::subscription::ChannelSubscriptionListener;
//!
//! // Create a channel-based listener
//! let (listener, mut update_receiver) = ChannelSubscriptionListener::create_channel();
//!
//! // Add the listener to your subscription
//! subscription.add_listener(Box::new(listener));
//!
//! // Process updates asynchronously in a separate task
//! tokio::spawn(async move {
//! while let Some(update) = update_receiver.recv().await {
//! // Process the update
//! println!("Item: {:?}", update.get_item_name());
//! println!("Fields: {:?}", update.get_fields());
//!
//! // Perform any async operations
//! // e.g., save to database, send to another service, etc.
//! }
//! });
//! ```
//!
//! This approach allows you to:
//! - Decouple update reception from processing
//! - Process updates asynchronously without blocking the Lightstreamer event loop
//! - Easily integrate with other async workflows
//! - Handle backpressure naturally through channel buffering
//!
/// Module containing subscription-related functionality.
///
/// This module provides the necessary types and functions to create and manage subscriptions
/// to Lightstreamer items. It includes the `Subscription` struct, subscription modes,
/// item updates, and subscription listeners.
/// Module containing utility functions and error types.
///
/// This module provides common utilities and error types used throughout the library,
/// including exception types for handling illegal arguments and states.
/// Module containing client-related functionality.
///
/// This module provides the main `LightstreamerClient` type and related components for
/// connecting to Lightstreamer servers, managing sessions, and handling client events.
/// Module containing connection-related functionality.
///
/// This module provides types for managing connection details and options.
/// Prelude module for convenient imports.
///
/// Import all commonly used types with a single `use` statement:
///
/// ```ignore
/// use lightstreamer_rs::prelude::*;
/// ```