### Basic Usage of Pyth Lazer Client in Rust Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Demonstrates the fundamental steps to initialize and use the Pyth Lazer client. It covers building the client with an access token, starting the data stream, subscribing to price feeds, and then continuously processing the received messages. This example requires the `tokio` runtime and assumes a valid access token and subscription configuration. ```rust use pyth_lazer_client::PythLazerStreamClientBuilder; use pyth_lazer_protocol::subscription::SubscribeRequest; #[tokio::main] async fn main() -> anyhow::Result<()> { let mut client = PythLazerStreamClientBuilder::new("your_access_token".to_string()) .with_num_connections(2) .build()?; let mut receiver = client.start().await?; // Subscribe to price feeds let subscribe_request = SubscribeRequest { // ... configure subscription }; client.subscribe(subscribe_request).await?; // Process incoming messages while let Some(response) = receiver.recv().await { println!("Received: {:?}", response); } Ok(()) } ``` -------------------------------- ### Start Pyth Lazer Client Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Starts the client and begins establishing WebSocket connections. Initializes all WebSocket connections and starts the message processing loop, returning a receiver channel for deduplicated messages. ```APIDOC ## POST /websites/rs_pyth-lazer-client_8_1_1/start ### Description Starts the client and begins establishing WebSocket connections. It initializes all WebSocket connections and starts the message processing loop, returning a receiver channel that will yield deduplicated messages from all connections. ### Method POST ### Endpoint /websites/rs_pyth-lazer-client_8_1_1/start ### Parameters #### Query Parameters - **channel_capacity** (integer) - Required - The capacity of the channel for receiving messages. - **num_connections** (integer) - Required - The number of WebSocket connections to establish. - **endpoints** (array of strings) - Required - A list of WebSocket endpoint URLs. - **access_token** (string) - Required - The access token for authentication. - **backoff** (object) - Required - Configuration for the backoff strategy for reconnections. - **timeout** (integer) - Required - The timeout duration for WebSocket connections in seconds. ### Request Body This endpoint does not accept a request body. ### Response #### Success Response (200) - **receiver** (mpsc::Receiver) - A receiver channel that yields deduplicated messages from all WebSocket connections. #### Response Example ```json { "receiver": "" } ``` ### Errors This method itself does not return errors. Individual connection failures are handled internally with automatic reconnection using the configured backoff strategy. Message deduplication is handled using a TTL cache with a 10-second window. ``` -------------------------------- ### Start Pyth Lazer WebSocket Connection Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/struct Establishes the WebSocket connection to the Lazer service and begins receiving price feed updates. Returns a stream of responses. ```rust pub async fn start(&mut self) -> Result>> ``` -------------------------------- ### PythLazerWSConnection - Start Connection Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/struct Establishes the WebSocket connection to the Lazer service and returns a stream of responses. ```APIDOC ## POST /start ### Description Starts the WebSocket connection and returns a stream of responses from the server. ### Method POST ### Endpoint `/start` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```json {} ``` ### Response #### Success Response (200) - **stream** (Stream>) - A stream of responses received from the server. #### Response Example ```json { "stream": "" } ``` ``` -------------------------------- ### Start PythLazerStreamClient and Begin WebSocket Connections (Rust) Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Initializes WebSocket connections and starts a message processing loop. Returns a receiver channel for deduplicated messages. Handles internal connection failures with automatic retries. Uses a TTL cache for message deduplication over a 10-second window. ```rust pub async fn start(&mut self) -> Result> { let (sender, receiver) = mpsc::channel::(self.channel_capacity); let (ws_connection_sender, mut ws_connection_receiver) = mpsc::channel::(CHANNEL_CAPACITY); for i in 0..self.num_connections { let endpoint = self.endpoints[i % self.endpoints.len()].clone(); let connection = PythLazerResilientWSConnection::new( endpoint, self.access_token.clone(), self.backoff.clone(), self.timeout, ws_connection_sender.clone(), ); self.ws_connections.push(connection); } let mut seen_updates = TtlCache::new(DEDUP_CACHE_SIZE); tokio::spawn(async move { while let Some(response) = ws_connection_receiver.recv().await { let cache_key = response.cache_key(); if seen_updates.contains_key(&cache_key) { continue; } seen_updates.insert(cache_key, true, DEDUP_TTL); match sender.try_send(response) { Ok(_) => (), Err(TrySendError::Full(r)) => { warn!("Sender channel is full, responses will be delayed"); if sender.send(r).await.is_err() { error!("Sender channel is closed, stopping client"); } } Err(TrySendError::Closed(_)) => { error!("Sender channel is closed, stopping client"); } } } }); Ok(receiver) } ``` -------------------------------- ### Rust: Handle WebSocket Connection Initialization Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/ws_connection This Rust code snippet demonstrates how to handle the initialization of a WebSocket sender within a struct. It checks if the connection has been started and either returns Ok(()) or a formatted error using `anyhow::bail!` if the connection is not initiated. ```rust self.ws_sender = None; Ok(()) } else { anyhow::bail!("WebSocket connection not started") } } ``` -------------------------------- ### Start WebSocket Connection and Stream Processing Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/resilient_ws_connection Establishes a new WebSocket connection using `PythLazerWSConnection` and pins the resulting stream for processing. It then iterates over existing subscriptions to re-establish them. ```rust pub async fn start( &mut self, sender: mpsc::Sender, request_receiver: &mut mpsc::Receiver, ) -> Result<()> { let mut ws_connection = PythLazerWSConnection::new(self.endpoint.clone(), self.access_token.clone())?; let stream = ws_connection.start().await?; pin!(stream); for subscription in self.subscriptions.clone() { ws_connection ``` -------------------------------- ### Run Resilient WebSocket Connection Task Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/resilient_ws_connection Manages the WebSocket connection lifecycle, including starting the connection, handling errors, and implementing retry logic with exponential backoff. It continuously attempts to run the connection. ```rust pub async fn run( &mut self, response_sender: mpsc::Sender, request_receiver: &mut mpsc::Receiver, ) -> Result<()> { loop { let start_time = Instant::now(); if let Err(e) = self.start(response_sender.clone(), request_receiver).await { // If a connection was working for BACKOFF_RESET_DURATION // and timeout + 1sec, it was considered successful therefore reset the backoff if start_time.elapsed() > BACKOFF_RESET_DURATION && start_time.elapsed() > self.timeout + Duration::from_secs(1) { self.backoff.reset(); } let delay = self.backoff.next_backoff(); match delay { Some(d) => { info!("WebSocket connection failed: {}. Retrying in {:?}", e, d); tokio::time::sleep(d).await; } None => { bail!("Max retries reached for WebSocket connection to {}, this should never happen, please contact developers", self.endpoint); } } } } } ``` -------------------------------- ### Create Auto-Updating Symbols Metadata Handle Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Fetches initial symbol metadata and creates an auto-updating handle. This involves starting a background task that periodically updates the metadata. Returns an error if the initial fetch fails. The handle provides a snapshot of the data via `ArcSwap`. ```rust /// Fetch metadata for all symbols as an auto-updating handle. /// /// Returns an error if the initial fetch failed. /// The returned `SymbolMetadataHandle` will be updated by a background task when the data changes. pub async fn all_symbols_metadata_handle(&self) -> anyhow::Result { let symbols = Arc::new( self.fetch_symbols_initial() .await? .into_iter() .map(|f| (f.pyth_lazer_id, f)) .collect::>(), ); let previous_symbols = symbols.clone(); let handle = Arc::new(ArcSwap::new(symbols)); let client = self.clone(); let weak_handle = Arc::downgrade(&handle); tokio::spawn(async move { client .update_symbols_handle(weak_handle, previous_symbols) .await; }); Ok(SymbolMetadataHandle(handle)) } ``` -------------------------------- ### Create Fault-Tolerant Auto-Updating Symbols Metadata Handle Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Fetches initial symbol metadata and creates an auto-updating handle, but tolerates initial fetch failures. If the initial fetch fails, the handle will start with an empty map. A background task is spawned to update the data. ```rust /// Fetch metadata for all symbols as an auto-updating handle. /// /// The returned `SymbolMetadataHandle` will be updated by a background task when the data changes. /// If the initial fetch failed, the handle will initially contain an empty hashmap. pub async fn all_symbols_metadata_fault_tolerant_handle(&self) -> SymbolMetadataHandle { ``` -------------------------------- ### SymbolMetadataHandle Methods in Rust Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Provides methods to access and update symbol metadata. Includes a method to get the current symbols and a constructor for testing purposes. ```rust impl SymbolMetadataHandle { pub fn symbols(&self) -> arc_swap::Guard>> { self.0.load() } pub fn new_for_test(data: HashMap) -> Self { Self(Arc::new(ArcSwap::new(Arc::new(data)))) } } ``` -------------------------------- ### Get Symbols Cache File Path Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client A helper method to determine the file path for caching symbol metadata. It constructs the path by joining the configured cache directory with a predefined filename `symbols_v1.json`. ```rust fn symbols_cache_file_path(&self) -> Option { self.config .cache_dir .as_ref() .map(|path| path.join("symbols_v1.json")) } ``` -------------------------------- ### Basic Usage of Pyth Lazer Client Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/stream_client/index Demonstrates how to instantiate, connect, subscribe, and process messages using the Pyth Lazer Client. It requires an access token and utilizes Tokio for asynchronous operations. The client supports configuring the number of connections and handles message reception. ```rust use pyth_lazer_client::PythLazerStreamClientBuilder; use pyth_lazer_protocol::subscription::SubscribeRequest; #[tokio::main] async fn main() -> anyhow::Result<()> { let mut client = PythLazerStreamClientBuilder::new("your_access_token".to_string()) .with_num_connections(2) .build()?; let mut receiver = client.start().await?; // Subscribe to price feeds let subscribe_request = SubscribeRequest { // ... configure subscription }; client.subscribe(subscribe_request).await?; // Process incoming messages while let Some(response) = receiver.recv().await { println!("Received: {:?}", response); } Ok(()) } ``` -------------------------------- ### Initialize Pyth Lazer History Client Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Creates a new instance of the Pyth Lazer History Client with the provided configuration. ```APIDOC ## Initialize Pyth Lazer History Client ### Description Initializes a new `PythLazerHistoryClient` with the specified configuration. ### Method `new` ### Parameters #### `config` - **Type**: `PythLazerHistoryClientConfig` - **Description**: The configuration object for the history client. ### Request Example ```rust use pyth_lazer_client::PythLazerHistoryClient; use std::time::Duration; let config = pyth_lazer_client::PythLazerHistoryClientConfig { urls: vec![std::net::Ipv4Addr::new(127, 0, 0, 1).to_string().parse().unwrap()], update_interval: Duration::from_secs(60), request_timeout: Duration::from_secs(30), cache_dir: None, channel_capacity: 1000, }; let client = PythLazerHistoryClient::new(config); ``` ``` -------------------------------- ### Create PythLazerWSConnection Instance Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/struct Initializes a new instance of the Pyth Lazer WebSocket client. Requires the WebSocket endpoint URL and an access token for authentication. ```rust pub fn new(endpoint: Url, access_token: String) -> Result ``` -------------------------------- ### Initialize Lazer Client Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Demonstrates the initialization of the Lazer client with various configuration parameters such as endpoints, access token, connection settings, and timeouts. This is a fundamental step before making any API calls. ```python self.endpoints, self.access_token, self.num_connections, self.backoff, self.timeout, self.channel_capacity, ) } } ``` -------------------------------- ### AnyResponse Conversions Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/enum Demonstrates how to convert between different types and AnyResponse. ```APIDOC ## Type Conversions for AnyResponse ### Description The `AnyResponse` enum implements the `From` trait, allowing for straightforward conversion from `WsResponse` (JSON) and `BinaryWsUpdate` (Binary) types into `AnyResponse`. ### `From` ```rust impl From for AnyResponse ``` This implementation allows creating an `AnyResponse::Json` variant directly from a `WsResponse` object. ### `From` ```rust impl From for AnyResponse ``` This implementation allows creating an `AnyResponse::Binary` variant directly from a `BinaryWsUpdate` object. ``` -------------------------------- ### Rust: Initialize PythLazerStreamClientBuilder Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/stream_client/struct Creates a new PythLazerStreamClientBuilder with default configurations. This method requires an access token for authentication and sets up default production endpoints, 4 WebSocket connections, and a 5-second timeout. ```rust pub fn new(access_token: String) -> Self ``` -------------------------------- ### Rust: Build PythLazerStreamClient Instance Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/stream_client/struct Builds and returns a configured PythLazerStreamClient instance using the settings provided in the builder. This method consumes the builder. Connections are established only when PythLazerStreamClient::start is called. Returns an error if no endpoints are configured or if any parameter is invalid. ```rust pub fn build(self) -> Result ``` -------------------------------- ### Initialize Pyth Lazer History Client Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Provides a constructor for `PythLazerHistoryClient`. It takes a configuration, clones it into an `Arc`, and initializes a `reqwest::Client` with a default timeout. The `reqwest::Client` is used for making HTTP requests to the history service. ```rust /// Client to the history service API. #[derive(Debug, Clone)] pub struct PythLazerHistoryClient { config: Arc, client: reqwest::Client, } impl PythLazerHistoryClient { pub fn new(config: PythLazerHistoryClientConfig) -> Self { Self { config: Arc::new(config), client: reqwest::Client::builder() .timeout(DEFAULT_REQUEST_TIMEOUT) .build() .expect("failed to initialize reqwest"), } } ``` -------------------------------- ### PythLazerStreamClientBuilder API Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/stream_client/struct Documentation for the PythLazerStreamClientBuilder struct and its associated methods for configuring and building a Pyth Lazer stream client. ```APIDOC ## Struct PythLazerStreamClientBuilder A builder for creating `PythLazerStreamClient` instances with customizable configuration. The builder provides a convenient way to configure a Pyth Lazer client with sensible defaults while allowing customization of all parameters. It follows the builder pattern for a fluent API. ### Default Configuration - **Endpoints**: Uses Pyth Lazer’s default production endpoints - **Connections**: 4 concurrent WebSocket connections - **Timeout**: 5 seconds for WebSocket operations - **Backoff**: Exponential backoff with default settings - **Channel Capacity**: Uses the default 1000 ## Methods ### `fn new(access_token: String) -> Self` Creates a new builder with default configuration. This initializes the builder with sensible defaults for production use: - Default Pyth Lazer endpoints - 4 WebSocket connections - 5-second timeout #### Arguments - **access_token** (String) - The authentication token for accessing Pyth Lazer services ### `fn with_endpoints(self, endpoints: Vec) -> Self` Sets custom WebSocket endpoints for the client. By default, the client uses Pyth Lazer’s production endpoints. Use this method to connect to different environments (staging, local development) or to use custom endpoint configurations. #### Arguments - **endpoints** (Vec) - A vector of WebSocket endpoint URLs. Must not be empty. ### `fn with_num_connections(self, num_connections: usize) -> Self` Sets the number of concurrent WebSocket connections to maintain. More connections provide better redundancy and can improve throughput, but also consume more resources. #### Arguments - **num_connections** (usize) - The number of WebSocket connections (must be > 0) ### `fn with_backoff(self, backoff: PythLazerExponentialBackoff) -> Self` Sets the exponential backoff configuration for connection retries. The backoff strategy determines how the client handles connection failures and retries. #### Arguments - **backoff** (PythLazerExponentialBackoff) - The exponential backoff configuration ### `fn with_timeout(self, timeout: Duration) -> Self` Sets the timeout duration for WebSocket operations. This timeout applies to each WebSocket connection; if no response is received within this duration, the connection will be considered failed and retried. #### Arguments - **timeout** (Duration) - The timeout duration for each WebSocket ### `fn with_channel_capacity(self, channel_capacity: usize) -> Self` Sets the capacity of the internal message channel. This determines how many messages can be buffered internally before the client starts applying backpressure. #### Arguments - **channel_capacity** (usize) - The channel capacity (number of messages) ### `fn build(self) -> Result` Builds the configured `PythLazerStreamClient` instance. This consumes the builder and creates a new client with the specified configuration. The client is ready to use, but connections are not established until `PythLazerStreamClient::start` is called. #### Returns Returns `Ok(PythLazerStreamClient)` on success, or an error if the configuration is invalid. #### Errors Returns an error if: - No endpoints are configured - Any configuration parameter is invalid ``` -------------------------------- ### Implement `symbols` and `new_for_test` for SymbolMetadataHandle Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/history_client/struct Details the implementation of methods for SymbolMetadataHandle, including `symbols` to retrieve metadata and `new_for_test` for testing scenarios. ```rust impl SymbolMetadataHandle Source #### pub fn symbols(&self) -> Guard>> Source #### pub fn new_for_test(data: HashMap) -> Self ``` -------------------------------- ### Initialize PythLazerStreamClientBuilder Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Creates a new PythLazerStreamClientBuilder with default settings. This constructor takes an access token and initializes the builder with default production endpoints, a specified number of connections, a default backoff strategy, and a default timeout. ```rust impl PythLazerStreamClientBuilder { /// Creates a new builder with default configuration. /// /// This initializes the builder with sensible defaults for production use: /// - Default Pyth Lazer endpoints /// - 4 WebSocket connections /// - 5-second timeout /// /// # Arguments /// /// * `access_token` - The authentication token for accessing Pyth Lazer services /// pub fn new(access_token: String) -> Self { Self { endpoints: DEFAULT_ENDPOINTS .iter() .map(|&s| s.parse().unwrap()) .collect(), access_token, num_connections: DEFAULT_NUM_CONNECTIONS, backoff: PythLazerExponentialBackoffBuilder::default().build(), timeout: DEFAULT_TIMEOUT, channel_capacity: CHANNEL_CAPACITY, } } // ... other methods ``` -------------------------------- ### Pyth Lazer Stream Client Initialization in Rust Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Shows the low-level constructor for creating a `PythLazerStreamClient`. This method requires explicit configuration of endpoints, access token, number of connections, backoff strategy, timeouts, and channel capacity. It includes validation to ensure at least one endpoint is provided. ```rust use std::time::Duration; use crate::backoff::PythLazerExponentialBackoff; use crate::resilient_ws_connection::PythLazerResilientWSConnection; use crate::ws_connection::AnyResponse; use crate::CHANNEL_CAPACITY; use anyhow::{bail, Result}; use backoff::ExponentialBackoff; use pyth_lazer_protocol::api::{SubscribeRequest, SubscriptionId}; use tokio::sync::mpsc::{self, error::TrySendError}; use tracing::{error, warn}; use ttl_cache::TtlCache; use url::Url; pub fn new( endpoints: Vec, access_token: String, num_connections: usize, backoff: PythLazerExponentialBackoff, timeout: Duration, channel_capacity: usize, ) -> Result { if endpoints.is_empty() { bail!("At least one endpoint must be provided"); } Ok(Self { endpoints, access_token, num_connections, ws_connections: Vec::with_capacity(num_connections), backoff: backoff.into(), timeout, channel_capacity, }) } ``` -------------------------------- ### Pyth Lazer History Client Configuration Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Configuration options for the Pyth Lazer History Client, including service URLs, update intervals, request timeouts, cache directory, and channel capacity. ```APIDOC ## Pyth Lazer History Client Configuration ### Description Defines the configuration parameters for the `PythLazerHistoryClient`. ### Struct Fields #### `urls` - **Type**: `Vec` - **Default**: `["https://history.pyth-lazer.dourolabs.app/"]` - **Description**: A list of URLs for the history services to connect to. #### `update_interval` - **Type**: `Duration` - **Default**: `30s` - **Description**: The interval at which to query history services. Note that requests failing will be retried with exponential backoff regardless of this setting. #### `request_timeout` - **Type**: `Duration` - **Default**: `15s` - **Description**: The timeout duration for individual requests to the history services. #### `cache_dir` - **Type**: `Option` - **Default**: `None` - **Description**: An optional path to a directory used for caching the latest data when the history service is unavailable. #### `channel_capacity` - **Type**: `usize` - **Default**: `1000` - **Description**: The capacity for communication channels created by the client. Must be greater than zero. ### Default Configuration ```rust use std::time::Duration; let config = pyth_lazer_client::PythLazerHistoryClientConfig { urls: vec![std::net::Ipv4Addr::new(127, 0, 0, 1).to_string().parse().unwrap()], update_interval: Duration::from_secs(60), request_timeout: Duration::from_secs(30), cache_dir: Some(std::path::PathBuf::from("/tmp/pyth_cache on")) channel_capacity: 2000, }; ``` ``` -------------------------------- ### PythLazerResilientWSConnection Constructor Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/resilient_ws_connection/struct Creates a new instance of PythLazerResilientWSConnection. It requires the endpoint URL, an access token for authentication, an exponential backoff strategy, a timeout duration, and a sender for responses. ```Rust pub fn new( endpoint: Url, access_token: String, backoff: ExponentialBackoff, timeout: Duration, sender: Sender, ) -> Self ``` -------------------------------- ### PythLazerWSConnection - Create New Client Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/struct Creates a new instance of the Pyth Lazer WebSocket client. This does not establish a connection. ```APIDOC ## POST /new ### Description Creates a new Lazer client instance. The client is not yet connected upon creation. ### Method POST ### Endpoint `/new` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **endpoint** (Url) - Required - The WebSocket URL of the Lazer service. - **access_token** (String) - Required - Access token for authentication. ### Request Example ```json { "endpoint": "wss://example.com/ws", "access_token": "your_access_token" } ``` ### Response #### Success Response (200) - **client** (PythLazerWSConnection) - A new client instance. #### Response Example ```json { "client": "" } ``` ``` -------------------------------- ### Generic Blanket Implementations Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/resilient_ws_connection/struct Demonstrates various blanket implementations for generic types, including Any, Borrow, BorrowMut, From, Instrument, Into, IntoEither, PolicyExt, Same, TryFrom, TryInto, VZip, and WithSubscriber. ```Rust impl Any for T where T: 'static + ?Sized, fn type_id(&self) -> TypeId impl Borrow for T where T: ?Sized, fn borrow(&self) -> &T impl BorrowMut for T where T: ?Sized, fn borrow_mut(&mut self) -> &mut T impl From for T fn from(t: T) -> T impl Instrument for T fn instrument(self, span: Span) -> Instrumented fn in_current_span(self) -> Instrumented impl Into for T where U: From, fn into(self) -> U impl IntoEither for T fn into_either(self, into_left: bool) -> Either fn into_either_with(self, into_left: F) -> Either where F: FnOnce(&Self) -> bool, impl PolicyExt for T where T: ?Sized, fn and(self, other: P) -> And where T: Policy, P: Policy, fn or(self, other: P) -> Or where T: Policy, P: Policy, impl Same for T type Output = T impl TryFrom for T where U: Into, type Error = Infallible fn try_from(value: U) -> Result>::Error> impl TryInto for T where U: TryFrom, type Error = >::Error fn try_into(self) -> Result>::Error> impl VZip for T where V: MultiLane, fn vzip(self) -> V impl WithSubscriber for T fn with_subscriber(self, subscriber: S) -> WithDispatch where S: Into, fn with_current_subscriber(self) -> WithDispatch impl ErasedDestructor for T where T: 'static, ``` -------------------------------- ### Blanket Implementations for SymbolMetadataHandle Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/history_client/struct Showcases various blanket implementations for SymbolMetadataHandle, covering traits like `Any`, `Borrow`, `CloneToUninit`, `From`, `Instrument`, `Into`, `IntoEither`, `PolicyExt`, `Same`, `ToOwned`, `TryFrom`, `TryInto`, `VZip`, and `WithSubscriber`. ```rust impl Any for T where T: 'static + ?Sized, Source§ #### fn type_id(&self) -> TypeId Gets the `TypeId` of `self`. Read more Source§ ### impl Borrow for T where T: ?Sized, Source§ #### fn borrow(&self) -> &T Immutably borrows from an owned value. Read more Source§ ### impl BorrowMut for T where T: ?Sized, Source§ #### fn borrow_mut(&mut self) -> &mut T Mutably borrows from an owned value. Read more Source§ ### impl CloneToUninit for T where T: Clone, Source§ #### unsafe fn clone_to_uninit(&self, dest: *mut u8) Performs copy-assignment from `self` to `dest`. Read more Source§ ### impl From for T Source§ #### fn from(t: T) -> T Returns the argument unchanged. Source§ ### impl Instrument for T Source§ #### fn instrument(self, span: Span) -> Instrumented Instruments this type with the provided `Span`, returning an `Instrumented` wrapper. Read more Source§ #### fn in_current_span(self) -> Instrumented Instruments this type with the current `Span`, returning an `Instrumented` wrapper. Read more Source§ ### impl Into for T where U: From, Source§ #### fn into(self) -> U Calls `U::from(self)`. That is, this conversion is whatever the implementation of `From for U` chooses to do. Source§ ### impl IntoEither for T Source§ #### fn into_either(self, into_left: bool) -> Either Converts `self` into a `Left` variant of `Either` if `into_left` is `true`. Converts `self` into a `Right` variant of `Either` otherwise. Read more Source§ #### fn into_either_with(self, into_left: F) -> Either where F: FnOnce(&Self) -> bool, Converts `self` into a `Left` variant of `Either` if `into_left(&self)` returns `true`. Converts `self` into a `Right` variant of `Either` otherwise. Read more Source§ ### impl PolicyExt for T where T: ?Sized, Source§ #### fn and(self, other: P) -> And where T: Policy, P: Policy, Create a new `Policy` that returns `Action::Follow` only if `self` and `other` return `Action::Follow`. Read more Source§ #### fn or(self, other: P) -> Or where T: Policy, P: Policy, Create a new `Policy` that returns `Action::Follow` if either `self` or `other` returns `Action::Follow`. Read more Source§ ### impl Same for T Source§ #### type Output = T Should always be `Self` Source§ ### impl ToOwned for T where T: Clone, Source§ #### type Owned = T The resulting type after obtaining ownership. Source§ #### fn to_owned(&self) -> T Creates owned data from borrowed data, usually by cloning. Read more Source§ #### fn clone_into(&self, target: &mut T) Uses borrowed data to replace owned data, usually by cloning. Read more Source§ ### impl TryFrom for T where U: Into, Source§ #### type Error = Infallible The type returned in the event of a conversion error. Source§ #### fn try_from(value: U) -> Result>::Error> Performs the conversion. Source§ ### impl TryInto for T where U: TryFrom, Source§ #### type Error = >::Error The type returned in the event of a conversion error. Source§ #### fn try_into(self) -> Result>::Error> Performs the conversion. Source§ ### impl VZip for T where V: MultiLane, Source§ #### fn vzip(self) -> V Source§ ### impl WithSubscriber for T Source§ #### fn with_subscriber(self, subscriber: S) -> WithDispatch where S: Into, Attaches the provided `Subscriber` to this type, returning a `WithDispatch` wrapper. Read more Source§ ``` -------------------------------- ### Implement From for AnyResponse Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/enum Enables conversion from `WsResponse` to `AnyResponse` using the `From` trait. This simplifies creating `AnyResponse` instances from JSON responses. ```Rust fn from(value: WsResponse) -> Self ``` -------------------------------- ### Create PythLazerResilientWSConnection Instance Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/resilient_ws_connection Initializes a new resilient WebSocket client. It takes the endpoint URL, access token, backoff strategy, timeout, and a sender for responses. A background task is spawned to manage the connection. ```rust pub fn new( endpoint: Url, access_token: String, backoff: ExponentialBackoff, timeout: Duration, sender: mpsc::Sender, ) -> Self { let (request_sender, mut request_receiver) = mpsc::channel(CHANNEL_CAPACITY); let mut task = PythLazerResilientWSConnectionTask::new(endpoint, access_token, backoff, timeout); tokio::spawn(async move { if let Err(e) = task.run(sender, &mut request_receiver).await { error!("Resilient WebSocket connection task failed: {}", e); } }); Self { request_sender } } ``` -------------------------------- ### Define Pyth Lazer History Client Configuration Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Defines the configuration structure for the history client, including URLs, update intervals, request timeouts, cache directory, and channel capacity. It uses `serde` for serialization/deserialization and provides default values for various settings. ```rust use std::{ collections::HashMap, io::Write, path::{Path, PathBuf}, sync::{Arc, Weak}, time::Duration, }; use anyhow::{bail, Context as _}; use arc_swap::ArcSwap; use atomicwrites::replace_atomic; use backoff::{exponential::ExponentialBackoff, future::retry_notify, SystemClock}; use futures::{stream::FuturesUnordered, StreamExt}; use pyth_lazer_protocol::{jrpc::SymbolMetadata, PriceFeedId}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tokio::{sync::mpsc, time::sleep}; use tracing::{info, warn}; use url::Url; const DEFAULT_URLS: &[&str] = &["https://history.pyth-lazer.dourolabs.app/"]; const DEFAULT_UPDATE_INTERVAL: Duration = Duration::from_secs(30); const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(15); /// Configuration for the history client. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PythLazerHistoryClientConfig { /// URLs of the history services. #[serde(default = "default_urls")] pub urls: Vec, /// Interval of queries to the history services. /// Note: if the request fails, it will be retried using exponential backoff regardless of this setting. #[serde(with = "humantime_serde", default = "default_update_interval")] pub update_interval: Duration, /// Timeout of an individual request. #[serde(with = "humantime_serde", default = "default_request_timeout")] pub request_timeout: Duration, /// Path to the cache directory that can be used to provide latest data if history service is unavailable. pub cache_dir: Option, /// Capacity of communication channels created by this client. It must be above zero. #[serde(default = "default_channel_capacity")] pub channel_capacity: usize, } fn default_urls() -> Vec { DEFAULT_URLS .iter() .map(|url| Url::parse(url).unwrap()) .collect() } fn default_update_interval() -> Duration { Duration::from_secs(30) } fn default_request_timeout() -> Duration { Duration::from_secs(15) } fn default_channel_capacity() -> usize { 1000 } impl Default for PythLazerHistoryClientConfig { fn default() -> Self { Self { urls: default_urls(), update_interval: default_update_interval(), request_timeout: default_request_timeout(), cache_dir: None, channel_capacity: default_channel_capacity(), } } } ``` -------------------------------- ### Implement From for AnyResponse Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/enum Enables conversion from `BinaryWsUpdate` to `AnyResponse` using the `From` trait. This simplifies creating `AnyResponse` instances from binary updates. ```Rust fn from(value: BinaryWsUpdate) -> Self ``` -------------------------------- ### Implement PartialEq for AnyResponse Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/enum Implements the `PartialEq` trait for `AnyResponse`, allowing for equality comparisons between instances. Includes methods for both equality (`eq`) and inequality (`ne`). ```Rust fn eq(&self, other: &AnyResponse) -> bool ``` ```Rust fn ne(&self, other: &Rhs) -> bool ``` -------------------------------- ### PythLazerWSConnection - Subscribe Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/struct Subscribes to price feed updates from the Pyth network. ```APIDOC ## POST /subscribe ### Description Subscribes to price feed updates by providing feed IDs and parameters. ### Method POST ### Endpoint `/subscribe` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **request** (SubscribeRequest) - Required - A subscription request object containing feed IDs and parameters. ### Request Example ```json { "request": { "feeds": [ { "id": "0xabc123", "params": { "realtime": true } } ] } } ``` ### Response #### Success Response (200) - **subscription_id** (String) - The unique identifier for the new subscription. #### Response Example ```json { "subscription_id": "sub_12345" } ``` ``` -------------------------------- ### Implement Blanket CloneToUninit Trait Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/enum Details a nightly-only experimental blanket implementation of `CloneToUninit` for types that implement `Clone`. It allows copying data to uninitialized memory. ```Rust unsafe fn clone_to_uninit(&self, dest: *mut u8) ``` -------------------------------- ### Pyth Lazer WebSocket Client Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/ws_connection This section covers the core functionality of the Pyth Lazer WebSocket client, including connection management and data handling. ```APIDOC ## PythLazerWSConnection ### Description A WebSocket client for consuming Pyth Lazer price feed updates. This client provides a simple interface to connect to a Lazer WebSocket endpoint, subscribe to price feed updates, and receive updates as a stream of messages. ### Methods #### `new(endpoint: Url, access_token: String) -> Result` Creates a new Lazer client instance. * **endpoint** (Url) - The WebSocket URL of the Lazer service. * **access_token** (String) - Access token for authentication. Returns: A new client instance (not yet connected). #### `start(&mut self) -> Result>>` Starts the WebSocket connection. Returns: A stream of responses from the server. #### `send_request(&mut self, request: WsRequest) -> Result<()>` Sends a WebSocket request to the server. * **request** (WsRequest) - The request payload to send. Returns: Ok(()) if the request was sent successfully, or an error if the connection is not started. #### `subscribe(&mut self, request: SubscribeRequest) -> Result<()>` Subscribes to price feed updates. * **request** (SubscribeRequest) - A subscription request containing feed IDs and parameters. #### `unsubscribe(&mut self, request: UnsubscribeRequest) -> Result<()>` Unsubscribes from a previously subscribed feed. * **request** (UnsubscribeRequest) - The subscription ID to cancel. #### `close(&mut self) -> Result<()>` Closes the WebSocket connection. ### Data Types #### `AnyResponse` enum Represents a response from the WebSocket that can be either a JSON `WsResponse` or a binary `BinaryWsUpdate`. * `Json(WsResponse)` * `Binary(BinaryWsUpdate)` ##### `cache_key(&self) -> u64` Calculates a unique hash key for the response, useful for caching. ``` -------------------------------- ### Implement Clone for SymbolMetadataHandle Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/history_client/struct Provides implementations for the Clone trait, allowing SymbolMetadataHandle instances to be duplicated. Includes `clone` and `clone_from` methods. ```rust impl Clone for SymbolMetadataHandle Source§ #### fn clone(&self) -> SymbolMetadataHandle Returns a duplicate of the value. Read more 1.0.0 · Source§ #### fn clone_from(&mut self, source: &Self) Performs copy-assignment from `source`. Read more Source§ ``` -------------------------------- ### Implement Hash for AnyResponse Source: https://docs.rs/pyth-lazer-client/8.1.1/pyth_lazer_client/ws_connection/enum Provides hashing capabilities for `AnyResponse`, allowing it to be used in hash-based collections. Includes methods for hashing individual items and slices. ```Rust fn hash<__H: Hasher>(&self, state: &mut __H) ``` ```Rust fn hash_slice(data: &[Self], state: &mut H) where H: Hasher, Self: Sized, ``` -------------------------------- ### Fetch and Initialize Symbols with ArcSwap Handle Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/history_client Fetches initial symbol data, converts it into a HashMap keyed by `pyth_lazer_id`, and stores it in an `ArcSwap` for shared mutable access. It also spawns a background task to continuously update these symbols. ```rust let initial_result = self.fetch_symbols_initial().await; let symbols = match initial_result { Ok(data) => data .into_iter() .map(|f| (f.pyth_lazer_id, f)) .collect::>(), Err(err) => { warn!( ?err, "failed to fetch symbols, proceeding with empty symbol list" ); HashMap::new() } }; let symbols = Arc::new(symbols); let previous_symbols = symbols.clone(); let handle = Arc::new(ArcSwap::new(symbols)); let weak_handle = Arc::downgrade(&handle); let client = self.clone(); tokio::spawn(async move { client .update_symbols_handle(weak_handle, previous_symbols) .await; }); SymbolMetadataHandle(handle) ``` -------------------------------- ### Build PythLazerStreamClient Instance Source: https://docs.rs/pyth-lazer-client/8.1.1/src/pyth_lazer_client/stream_client Consumes the PythLazerStreamClientBuilder to create a configured [`PythLazerStreamClient`]. This method returns a `Result` which is `Ok(PythLazerStreamClient)` on success or an error if the configuration is invalid, such as missing endpoints. ```rust /// Builds the configured [`PythLazerStreamClient`] instance. /// /// This consumes the builder and creates a new client with the specified /// configuration. The client is ready to use but connections are not /// established until [`PythLazerStreamClient::start`] is called. /// /// # Returns /// /// Returns `Ok(PythLazerStreamClient)` on success, or an error if the configuration /// is invalid. /// /// # Errors /// /// Returns an error if: /// - No endpoints are configured /// - Any configuration parameter is invalid /// pub fn build(self) -> Result { PythLazerStreamClient::new( // ... rest of the implementation ```