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