diff options
author | 2024-03-24 21:47:33 +0100 | |
---|---|---|
committer | 2024-03-24 21:47:33 +0100 | |
commit | b4e12fd1165b5e3960a1294dadec45eb40893b37 (patch) | |
tree | 214908f3aaf1bea984adfdce4d9b755ba71be670 | |
parent | 7e1eb27a06e5545b3d1b77b5998dc0463df27d70 (diff) |
Unstable commit, won't compile.
-rw-r--r-- | src/connection_details.rs | 362 | ||||
-rw-r--r-- | src/lib.rs | 21 | ||||
-rw-r--r-- | src/lightstreamer_client.rs | 25 | ||||
-rw-r--r-- | src/main.rs | 55 | ||||
-rw-r--r-- | src/subscription.rs | 23 | ||||
-rw-r--r-- | src/subscription_listener.rs | 48 |
6 files changed, 509 insertions, 25 deletions
diff --git a/src/connection_details.rs b/src/connection_details.rs index 03f33f5..1d18bf6 100644 --- a/src/connection_details.rs +++ b/src/connection_details.rs @@ -1,3 +1,363 @@ +use crate::client_listener::ClientListener; +use crate::IllegalArgumentException; + +/// Used by `LightstreamerClient` to provide a basic connection properties data object. +/// +/// Data object that contains the configuration settings needed to connect to a Lightstreamer Server. +/// +/// An instance of this class is attached to every `LightstreamerClient` as `LightstreamerClient.connectionDetails` +/// +/// See also `LightstreamerClient` pub struct ConnectionDetails { + adapter_set: String, + client_ip: Option<String>, + server_address: String, + server_instance_address: Option<String>, + server_socket_name: Option<String>, + session_id: Option<String>, + user: Option<String>, + password: Option<String>, + listeners: Vec<Box<dyn ClientListener>>, +} + +impl ConnectionDetails { + /// Inquiry method that gets the name of the Adapter Set (which defines the Metadata Adapter + /// and one or several Data Adapters) mounted on Lightstreamer Server that supply all the + /// items used in this application. + /// + /// # Returns + /// + /// The name of the Adapter Set; returns `None` if no name has been configured, that means + /// that the "DEFAULT" Adapter Set is used. + /// + /// See also `setAdapterSet()` + pub fn get_adapter_set(&self) -> &String { + self.adapter_set.as_ref() + } + + /// Inquiry method that gets the IP address of this client as seen by the Server which is + /// serving the current session as the client remote address (note that it may not correspond + /// to the client host; for instance it may refer to an intermediate proxy). If, upon a new + /// session, this address changes, it may be a hint that the intermediary network nodes handling + /// the connection have changed, hence the network capabilities may be different. The library + /// uses this information to optimize the connection. + /// + /// Note that in case of polling or in case rebind requests are needed, subsequent requests + /// related to the same session may, in principle, expose a different IP address to the Server; + /// these changes would not be reported. + /// + /// If a session is not currently active, `None` is returned; soon after a session is established, + /// the value may become available; but it is possible that this information is not provided + /// by the Server and that it will never be available. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "clientIp" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Returns + /// + /// A canonical representation of an IP address (it can be either IPv4 or IPv6), or `None`. + pub fn get_client_ip(&self) -> Option<&String> { + self.client_ip.as_ref() + } + + /// Inquiry method that gets the configured address of Lightstreamer Server. + /// + /// # Returns + /// + /// The configured address of Lightstreamer Server. + pub fn get_server_address(&self) -> Option<&String> { + self.server_address.as_ref() + } + + /// Inquiry method that gets the server address to be used to issue all requests related to + /// the current session. In fact, when a Server cluster is in place, the Server address specified + /// through `setServerAddress()` can identify various Server instances; in order to ensure that + /// all requests related to a session are issued to the same Server instance, the Server can + /// answer to the session opening request by providing an address which uniquely identifies + /// its own instance. When this is the case, this address is returned by the method; otherwise, + /// `None` is returned. + /// + /// Note that the addresses will always have the `http:` or `https:` scheme. In case WebSockets + /// are used, the specified scheme is internally converted to match the related WebSocket protocol + /// (i.e. `http` becomes `ws` while `https` becomes `wss`). + /// + /// Server Clustering is an optional feature, available depending on Edition and License Type. + /// To know what features are enabled by your license, please see the License tab of the Monitoring + /// Dashboard (by default, available at /dashboard). + /// + /// The method gives a meaningful answer only when a session is currently active. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "serverInstanceAddress" on any `ClientListener` listening to the related + /// `LightstreamerClient`. + /// + /// # Returns + /// + /// Address used to issue all requests related to the current session. + pub fn get_server_instance_address(&self) -> Option<&String> { + self.server_instance_address.as_ref() + } + + /// Inquiry method that gets the instance name of the Server which is serving the current session. + /// To be more precise, each answering port configured on a Server instance (through a `<http_server>` + /// or `<https_server>` element in the Server configuration file) can be given a different name; + /// the name related to the port to which the session opening request has been issued is returned. + /// + /// Note that each rebind to the same session can, potentially, reach the Server on a port different + /// than the one used for the previous request, depending on the behavior of intermediate nodes. + /// However, the only meaningful case is when a Server cluster is in place and it is configured + /// in such a way that the port used for all `bind_session` requests differs from the port used + /// for the initial `create_session` request. + /// + /// Server Clustering is an optional feature, available depending on Edition and License Type. + /// To know what features are enabled by your license, please see the License tab of the Monitoring + /// Dashboard (by default, available at /dashboard). + /// + /// If a session is not currently active, `None` is returned; soon after a session is established, + /// the value will become available. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "serverSocketName" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Returns + /// + /// Name configured for the Server instance which is managing the current session, or `None`. + pub fn get_server_socket_name(&self) -> Option<&String> { + self.server_socket_name.as_ref() + } + + /// Inquiry method that gets the ID associated by the server to this client session. + /// + /// The method gives a meaningful answer only when a session is currently active. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "sessionId" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Returns + /// + /// ID assigned by the Server to this client session. + pub fn get_session_id(&self) -> Option<&String> { + self.session_id.as_ref() + } + + /// Inquiry method that gets the username to be used for the authentication on Lightstreamer + /// Server when initiating the session. + /// + /// # Returns + /// + /// The username to be used for the authentication on Lightstreamer Server; returns `None` + /// if no user name has been configured. + pub fn get_user(&self) -> Option<&String> { + self.user.as_ref() + } + + /// Creates a new ConnectionDetails object with default values. + pub fn new(server_address: Option<String>, adapter_set: Option<String>) -> ConnectionDetails { + if let Some(server_address) = server_address { + ConnectionDetails { + server_address: Some(server_address), + adapter_set, + ..Default::default() + } + } else { + ConnectionDetails { + adapter_set, + ..Default::default() + } + } + } + + /// Setter method that sets the name of the Adapter Set mounted on Lightstreamer Server to + /// be used to handle all requests in the session. + /// + /// An Adapter Set defines the Metadata Adapter and one or several Data Adapters. It is configured + /// on the server side through an "adapters.xml" file; the name is configured through the "id" + /// attribute in the `<adapters_conf>` element. + /// + /// The default Adapter Set, configured as "DEFAULT" on the Server. + /// + /// The Adapter Set name should be set on the `LightstreamerClient.connectionDetails` object + /// before calling the `LightstreamerClient.connect()` method. However, the value can be changed + /// at any time: the supplied value will be used for the next time a new session is requested + /// to the server. + /// + /// This setting can also be specified in the `LightstreamerClient` constructor. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "adapterSet" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Parameters + /// + /// * `adapter_set`: The name of the Adapter Set to be used. A `None` value is equivalent to + /// the "DEFAULT" name. + pub fn set_adapter_set(&mut self, adapter_set: Option<String>) { + self.adapter_set = adapter_set; + + // Notify listeners about the property change + for listener in &self.listeners { + listener.on_property_change("adapterSet"); + } + } + + /// Setter method that sets the password to be used for the authentication on Lightstreamer + /// Server when initiating the session. The Metadata Adapter is responsible for checking the + /// credentials (username and password). + /// + /// If no password is supplied, no password information will be sent at session initiation. + /// The Metadata Adapter, however, may still allow the session. + /// + /// The password should be set on the `LightstreamerClient.connectionDetails` object before + /// calling the `LightstreamerClient.connect()` method. However, the value can be changed at + /// any time: the supplied value will be used for the next time a new session is requested to + /// the server. + /// + /// NOTE: The password string will be stored in the current instance. That is necessary in order + /// to allow automatic reconnection/reauthentication for fail-over. For maximum security, avoid + /// using an actual private password to authenticate on Lightstreamer Server; rather use a session-id + /// originated by your web/application server, that can be checked by your Metadata Adapter. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "password" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Parameters + /// + /// * `password`: The password to be used for the authentication on Lightstreamer Server. The + /// password can be `None`. + /// + /// See also `setUser()` + pub fn set_password(&mut self, password: Option<String>) { + self.password = password; + + // Notify listeners about the property change + for listener in &self.listeners { + listener.on_property_change("password"); + } + } + + /// Setter method that sets the address of Lightstreamer Server. + /// + /// Note that the addresses specified must always have the `http:` or `https:` scheme. In case + /// WebSockets are used, the specified scheme is internally converted to match the related WebSocket + /// protocol (i.e. `http` becomes `ws` while `https` becomes `wss`). + /// + /// WSS/HTTPS is an optional feature, available depending on Edition and License Type. To know + /// what features are enabled by your license, please see the License tab of the Monitoring + /// Dashboard (by default, available at /dashboard). + /// + /// If no server address is supplied the client will be unable to connect. + /// + /// This method can be called at any time. If called while connected, it will be applied when + /// the next session creation request is issued. This setting can also be specified in the + /// `LightstreamerClient` constructor. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "serverAddress" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Parameters + /// + /// * `server_address`: The full address of Lightstreamer Server. A `None` value can also be + /// used, to restore the default value. + /// + /// An IPv4 or IPv6 can also be used in place of a hostname. Some examples of valid values include: + /// + /// - `http://push.mycompany.com` + /// - `http://push.mycompany.com:8080` + /// - `http://79.125.7.252` + /// - `http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]` + /// - `http://[2001:0db8:85a3::8a2e:0370:7334]:8080` + /// + /// # Raises + /// + /// * `IllegalArgumentException`: if the given address is not valid. + pub fn set_server_address( + &mut self, + server_address: Option<String>, + ) -> Result<(), IllegalArgumentException> { + // Validate the server address + if let Some(address) = &server_address { + if !address.starts_with("http://") && !address.starts_with("https://") { + return Err(IllegalArgumentException::new( + "Invalid server address: must start with http:// or https://", + )); + } + } + + self.server_address = server_address; + + // Notify listeners about the property change + for listener in &self.listeners { + listener.on_property_change("serverAddress"); + } + + Ok(()) + } + + /// Setter method that sets the username to be used for the authentication on Lightstreamer + /// Server when initiating the session. The Metadata Adapter is responsible for checking the + /// credentials (username and password). + /// + /// If no username is supplied, no user information will be sent at session initiation. The + /// Metadata Adapter, however, may still allow the session. + /// + /// The username should be set on the `LightstreamerClient.connectionDetails` object before + /// calling the `LightstreamerClient.connect()` method. However, the value can be changed at + /// any time: the supplied value will be used for the next time a new session is requested to + /// the server. + /// + /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()` + /// with argument "user" on any `ClientListener` listening to the related `LightstreamerClient`. + /// + /// # Parameters + /// + /// * `user`: The username to be used for the authentication on Lightstreamer Server. The username + /// can be `None`. + /// + /// See also `setPassword()` + pub fn set_user(&mut self, user: Option<String>) { + self.user = user; + + // Notify listeners about the property change + for listener in &self.listeners { + listener.on_property_change("user"); + } + } + + /// Adds a listener that will receive events related to changes in the `ConnectionDetails`. + /// + /// The same listener can be added to multiple instances of `ConnectionDetails`. + /// + /// # Parameters + /// + /// * `listener`: An object that will receive the events as documented in the `ClientListener` + /// interface. + pub fn add_listener(&mut self, listener: Box<dyn ClientListener>) { + self.listeners.push(listener); + } + + /// Removes a listener from the `ConnectionDetails` instance so that it will not receive events + /// anymore. + /// + /// # Parameters + /// + /// * `listener`: The listener to be removed. + pub fn remove_listener(&mut self, listener: Box<dyn ClientListener>) { + unimplemented!("Implement mechanism to remove listener from ConnectionDetails."); + //self.listeners.remove(&listener); + } +} -}
\ No newline at end of file +impl Default for ConnectionDetails { + fn default() -> Self { + ConnectionDetails { + adapter_set: None, + client_ip: None, + server_address: None, + server_instance_address: None, + server_socket_name: None, + session_id: None, + user: None, + password: None, + listeners: Vec::new(), + } + } +} @@ -12,6 +12,27 @@ pub mod proxy; pub mod subscription; #[derive(Debug)] +pub struct IllegalArgumentException(String); + +impl IllegalArgumentException { + pub fn new(msg: &str) -> IllegalArgumentException { + IllegalArgumentException(msg.to_string()) + } +} + +impl fmt::Display for IllegalArgumentException { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Error for IllegalArgumentException { + fn description(&self) -> &str { + &self.0 + } +} + +#[derive(Debug)] pub struct IllegalStateException { details: String } diff --git a/src/lightstreamer_client.rs b/src/lightstreamer_client.rs index 2012c28..f91e843 100644 --- a/src/lightstreamer_client.rs +++ b/src/lightstreamer_client.rs @@ -45,8 +45,8 @@ use cookie::Cookie; /// * `IllegalArgumentException`: if a not valid address is passed. See `ConnectionDetails.setServerAddress()` /// for details. pub struct LightstreamerClient { - server_address: Option<String>, - adapter_set: Option<String>, + server_address: String, + adapter_set: String, /// Data object that contains the details needed to open a connection to a Lightstreamer Server. /// This instance is set up by the `LightstreamerClient` object at its own creation. Properties /// of this object can be overwritten by values received from a Lightstreamer Server. @@ -134,10 +134,6 @@ impl LightstreamerClient { /// /// See also `ConnectionDetails.setServerAddress()` pub fn connect(&mut self) -> Result<(), IllegalStateException> { - if self.server_address.is_none() { - return Err(IllegalStateException::new("No server address was configured.")); - } - // Implementation for connect Ok(()) } @@ -236,6 +232,23 @@ impl LightstreamerClient { &self.subscriptions } + pub fn new (server_address: &str, adapter_set: &str) -> Result<LightstreamerClient, IllegalStateException> { + let connection_details = ConnectionDetails::new( + server_address.to_string(), + adapter_set.to_string(), + ); + let connection_options = ConnectionOptions::new(); + + Ok(LightstreamerClient { + server_address: server_address.map(|s| s.to_string()), + adapter_set: adapter_set.map(|s| s.to_string()), + connection_details, + connection_options, + listeners: Vec::new(), + subscriptions: Vec::new(), + }) + } + /// Removes a listener from the `LightstreamerClient` instance so that it will not receive /// events anymore. /// diff --git a/src/main.rs b/src/main.rs index d782d2b..07a3bf4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,20 @@ +use crate::item_update::ItemUpdate; +use crate::subscription::{Subscription, SubscriptionMode}; +use crate::subscription_listener::SubscriptionListener; + use futures::stream::StreamExt; use futures::SinkExt; +use lightstreamer_client::lightstreamer_client::LightstreamerClient; use reqwest::Client; use serde_urlencoded; use std::error::Error; use std::sync::Arc; -use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; use tokio::sync::Mutex; +use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; + +mod item_update; +mod subscription; +mod subscription_listener; async fn establish_persistent_http_connection( session_id_shared: Arc<Mutex<String>>, @@ -116,7 +125,12 @@ async fn subscribe_to_channel(session_id: String) -> Result<(), reqwest::Error> // Function to subscribe to a channel using WebSocket async fn subscribe_to_channel_ws( session_id: String, - mut write: futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, tokio_tungstenite::tungstenite::protocol::Message>, + mut write: futures::stream::SplitSink< + tokio_tungstenite::WebSocketStream< + tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>, + >, + tokio_tungstenite::tungstenite::protocol::Message, + >, ) -> Result<(), Box<dyn Error>> { // Example subscription to ITEM1 in MERGE mode from the DEMO adapter set let sub_params = [ @@ -131,15 +145,14 @@ async fn subscribe_to_channel_ws( let encoded_sub_params = serde_urlencoded::to_string(&sub_params)?; // Send the subscription message - write - .send(Message::Text(encoded_sub_params)) - .await?; + write.send(Message::Text(encoded_sub_params)).await?; println!("Subscribed to channel with session ID: {}", session_id); Ok(()) } +/* #[tokio::main] async fn main() -> Result<(), Box<dyn Error>> { @@ -172,4 +185,34 @@ async fn main() -> Result<(), Box<dyn Error>> { task2.await?; Ok(()) -}
\ No newline at end of file +} +*/ + +pub struct MySubscriptionListener {} + +impl SubscriptionListener for MySubscriptionListener { + fn on_item_update(&mut self, update: ItemUpdate) { + println!( + "UPDATE {} {}", + update.get_value("stock_name").unwrap(), + update.get_value("last_price").unwrap() + ); + } +} + +#[tokio::main] +async fn main() -> Result<(), Box<dyn Error>> { + let mut subscription = Subscription::new( + SubscriptionMode::Merge, + Some(vec!["item1".to_string(),"item2".to_string(),"item3".to_string()]), + Some(vec!["stock_name".to_string(),"last_price".to_string()]), + )?; + + subscription.add_listener(Box::new(MySubscriptionListener {})); + + let client = LightstreamerClient::new("http://push.lightstreamer.com/lightstreamer", "DEMO")?; + + println!("Subscription: {:?}", subscription); + + Ok(()) +} diff --git a/src/subscription.rs b/src/subscription.rs index 035614d..f7e0d88 100644 --- a/src/subscription.rs +++ b/src/subscription.rs @@ -1,5 +1,6 @@ use crate::subscription_listener::SubscriptionListener; use std::collections::HashMap; +use std::fmt::{self, Debug, Formatter}; /// Enum representing the subscription mode #[derive(Debug, PartialEq, Eq)] @@ -837,4 +838,26 @@ impl Subscription { } } */ +} + +impl Debug for Subscription { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("Subscription") + .field("mode", &self.mode) + .field("item_group", &self.item_group) + .field("items", &self.items) + .field("field_schema", &self.field_schema) + .field("fields", &self.fields) + .field("data_adapter", &self.data_adapter) + .field("command_second_level_data_adapter", &self.command_second_level_data_adapter) + .field("command_second_level_field_schema", &self.command_second_level_field_schema) + .field("command_second_level_fields", &self.command_second_level_fields) + .field("requested_buffer_size", &self.requested_buffer_size) + .field("requested_max_frequency", &self.requested_max_frequency) + .field("requested_snapshot", &self.requested_snapshot) + .field("selector", &self.selector) + .field("is_active", &self.is_active) + .field("is_subscribed", &self.is_subscribed) + .finish() + } }
\ No newline at end of file diff --git a/src/subscription_listener.rs b/src/subscription_listener.rs index 5fb6f85..aaadfd9 100644 --- a/src/subscription_listener.rs +++ b/src/subscription_listener.rs @@ -32,7 +32,9 @@ pub trait SubscriptionListener { /// - `item_name`: name of the involved item. If the Subscription was initialized using an /// "Item Group" then a `None` value is supplied. /// - `item_pos`: 1-based position of the item within the "Item List" or "Item Group". - fn on_clear_snapshot(&mut self, item_name: Option<&str>, item_pos: usize); + fn on_clear_snapshot(&mut self, item_name: Option<&str>, item_pos: usize) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer to notify that, due to internal resource /// limitations, Lightstreamer Server dropped one or more updates for an item that was @@ -52,7 +54,9 @@ pub trait SubscriptionListener { /// - `Subscription::set_requested_max_frequency()` /// - `Subscription::set_command_second_level_fields()` /// - `Subscription::set_command_second_level_field_schema()` - fn on_command_second_level_item_lost_updates(&mut self, lost_updates: u32, key: &str); + fn on_command_second_level_item_lost_updates(&mut self, lost_updates: u32, key: &str) { + // Default implementation does nothing. + } /// Event handler that is called when the Server notifies an error on a second-level subscription. /// @@ -83,7 +87,9 @@ pub trait SubscriptionListener { /// - `ConnectionDetails::set_adapter_set()` /// - `Subscription::set_command_second_level_fields()` /// - `Subscription::set_command_second_level_field_schema()` - fn on_command_second_level_subscription_error(&mut self, code: i32, message: Option<&str>, key: &str); + fn on_command_second_level_subscription_error(&mut self, code: i32, message: Option<&str>, key: &str) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer to notify that all snapshot events for an item /// in the Subscription have been received, so that real time events are now going to be received. @@ -108,7 +114,9 @@ pub trait SubscriptionListener { /// /// - `Subscription::set_requested_snapshot()` /// - `ItemUpdate::is_snapshot()` - fn on_end_of_snapshot(&mut self, item_name: Option<&str>, item_pos: usize); + fn on_end_of_snapshot(&mut self, item_name: Option<&str>, item_pos: usize) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer to notify that, due to internal resource /// limitations, Lightstreamer Server dropped one or more updates for an item in the Subscription. @@ -131,7 +139,9 @@ pub trait SubscriptionListener { /// # See also /// /// - `Subscription::set_requested_max_frequency()` - fn on_item_lost_updates(&mut self, item_name: Option<&str>, item_pos: usize, lost_updates: u32); + fn on_item_lost_updates(&mut self, item_name: Option<&str>, item_pos: usize, lost_updates: u32) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer each time an update pertaining to an item /// in the Subscription has been received from the Server. @@ -141,17 +151,23 @@ pub trait SubscriptionListener { /// - `update`: a value object containing the updated values for all the fields, together with /// meta-information about the update itself and some helper methods that can be used to /// iterate through all or new values. - fn on_item_update(&mut self, update: ItemUpdate); + fn on_item_update(&mut self, update: ItemUpdate) { + // Default implementation does nothing. + } /// Event handler that receives a notification when the `SubscriptionListener` instance is /// removed from a `Subscription` through `Subscription::remove_listener()`. This is the last /// event to be fired on the listener. - fn on_listen_end(&mut self); + fn on_listen_end(&mut self) { + // Default implementation does nothing. + } /// Event handler that receives a notification when the `SubscriptionListener` instance is /// added to a `Subscription` through `Subscription::add_listener()`. This is the first event /// to be fired on the listener. - fn on_listen_start(&mut self); + fn on_listen_start(&mut self) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer to notify the client with the real maximum /// update frequency of the Subscription. It is called immediately after the Subscription is @@ -173,7 +189,9 @@ pub trait SubscriptionListener { /// - `frequency`: A decimal number, representing the maximum frequency applied by the Server /// (expressed in updates per second), or the string "unlimited". A `None` value is possible in /// rare cases, when the frequency can no longer be determined. - fn on_real_max_frequency(&mut self, frequency: Option<f64>); + fn on_real_max_frequency(&mut self, frequency: Option<f64>) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer to notify that a Subscription has been successfully /// subscribed to through the Server. This can happen multiple times in the life of a Subscription @@ -190,7 +208,9 @@ pub trait SubscriptionListener { /// If the involved Subscription has a two-level behavior enabled /// (see `Subscription::set_command_second_level_fields()` and /// `Subscription::set_command_second_level_field_schema()`), second-level subscriptions are not notified. - fn on_subscription(&mut self); + fn on_subscription(&mut self) { + // Default implementation does nothing. + } /// Event handler that is called when the Server notifies an error on a Subscription. /// By implementing this method it is possible to perform recovery actions. @@ -224,7 +244,9 @@ pub trait SubscriptionListener { /// # See also /// /// - `ConnectionDetails::set_adapter_set()` - fn on_subscription_error(&mut self, code: i32, message: Option<&str>); + fn on_subscription_error(&mut self, code: i32, message: Option<&str>) { + // Default implementation does nothing. + } /// Event handler that is called by Lightstreamer to notify that a Subscription has been successfully /// unsubscribed from. This can happen multiple times in the life of a Subscription instance, in case @@ -240,5 +262,7 @@ pub trait SubscriptionListener { /// If the involved Subscription has a two-level behavior enabled /// (see `Subscription::set_command_second_level_fields()` and /// `Subscription::set_command_second_level_field_schema()`), second-level unsubscriptions are not notified. - fn on_unsubscription(&mut self); + fn on_unsubscription(&mut self) { + // Default implementation does nothing. + } }
\ No newline at end of file |