diff --git a/stellar_rust_sdk/src/horizon_client.rs b/stellar_rust_sdk/src/horizon_client.rs index 7f465d2..cbadfc4 100644 --- a/stellar_rust_sdk/src/horizon_client.rs +++ b/stellar_rust_sdk/src/horizon_client.rs @@ -33,6 +33,7 @@ use crate::{ details_request::{BuyingAsset, DetailsRequest, SellingAsset}, response::DetailsResponse, }, + paths::prelude::*, payments::prelude::*, trade_aggregations::prelude::*, trades::prelude::*, @@ -1985,6 +1986,80 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves payment paths from the Horizon server. + /// + /// This asynchronous method fetches a list of payment paths from + /// the Horizon server. It requires an [`FindPaymentsPathRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`FindPaymentsPathRequest`] instance, containing the + /// parameters for the paths request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`PathsResponse`], which includes + /// the list of the payment paths obtained from the Horizon server. If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`FindPaymentsPathRequest`] and set any desired + /// filters or parameters. + pub async fn get_find_payment_paths( + &self, + request: &FindPaymentsPathRequest, + ) -> Result { + self.get::(request).await + } + + /// Retrieves a list of strict receive payment paths from the Horizon server. + /// + /// This asynchronous method fetches a list of strict receive payment paths from + /// the Horizon server. It requires an [`ListStrictReceivePaymentPathsRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`ListStrictReceivePaymentPathsRequest`] instance, containing the + /// parameters for the paths request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`PathsResponse`], which includes + /// the list of the strict receive payment paths obtained from the Horizon server. + /// If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`ListStrictReceivePaymentPathsRequest`] and set any desired + /// filters or parameters. + pub async fn get_list_strict_receive_payment_paths( + &self, + request: &ListStrictReceivePaymentPathsRequest, + ) -> Result { + self.get::(request).await + } + + /// Retrieves a list of strict send payment paths from the Horizon server. + /// + /// This asynchronous method fetches a list of strict send payment paths from + /// the Horizon server. It requires an [`ListStrictSendPaymentPathsRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`ListStrictSendPaymentPathsRequest`] instance, containing the + /// parameters for the paths request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`PathsResponse`], which includes + /// the list of the strict send payment paths obtained from the Horizon server. + /// If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`ListStrictSendPaymentPathsRequest`] and set any desired + /// filters or parameters. + pub async fn get_list_strict_send_payment_paths( + &self, + request: &ListStrictSendPaymentPathsRequest, + ) -> Result { + self.get::(request).await + } + /// Retrieves a list of all payments from the Horizon server. /// /// This asynchronous method fetches a list of all payments from the Horizon server. diff --git a/stellar_rust_sdk/src/lib.rs b/stellar_rust_sdk/src/lib.rs index f2a65db..4d808cf 100644 --- a/stellar_rust_sdk/src/lib.rs +++ b/stellar_rust_sdk/src/lib.rs @@ -15,7 +15,7 @@ //! stabilization. //! //! #### Supported endpoints: -//! ![93%](https://progress-bar.dev/93/?width=200) +//! ![100%](https://progress-bar.dev/100/?width=200) //! * Accounts //! * Assets //! * Claimable balance @@ -26,13 +26,13 @@ //! * Operations //! * Offers //! * Orderbook +//! * Paths //! * Payments //! * Trades //! * Trade aggregations //! * Transactions //! //! #### Endpoints on the roadmap: -//! * Paths //! //! ## Example Usage @@ -625,6 +625,50 @@ pub mod transactions; /// pub mod trades; +/// Provides `Request` and `Response` structs for retrieving payment paths. +/// +/// This module provides a set of specialized request and response structures designed for +/// interacting with the payment path-related endpoints of the Horizon server. These structures +/// facilitate the construction of requests to query payment paths and the interpretation of +/// the corresponding responses. +/// +/// # Usage +/// +/// This module is intended to be used in conjunction with the [`HorizonClient`](crate::horizon_client::HorizonClient) +/// for making specific payment path-related API calls to the Horizon server. The request +/// structures are designed to be passed to the client's methods, which handle the +/// communication with the server and return the corresponding response structures. +/// +/// # Example +/// +/// To use this module, you can create an instance of a request struct, such as +/// `FindPaymentPathsRequest`, `ListStrictReceivePaymentPathsRequest`, or `ListStrictSendPaymentPathsRequest`, +/// set any desired query parameters, and pass the request to the `HorizonClient`. The client +/// will then execute the request and return the corresponding response struct, like `PathsResponse`. +/// +/// ```rust +/// use stellar_rs::horizon_client::HorizonClient; +/// use stellar_rs::paths::{prelude::*, AssetType}; +/// use stellar_rs::models::Request; +/// +/// # async fn example() -> Result<(), Box> { +/// let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string())?; +/// +/// // Example: Fetching payment paths +/// let request = FindPaymentsPathRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_source_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap(); // Sets the destination account. +/// +/// let paths_response = horizon_client.get_find_payment_paths(&request).await?; +/// +/// // Process the responses... +/// # Ok(()) +/// # } +/// ``` +pub mod paths; + /// Provides `Request` and `Response` structs for retrieving payments. /// /// This module provides a set of specialized request and response structures designed for diff --git a/stellar_rust_sdk/src/offers/mod.rs b/stellar_rust_sdk/src/offers/mod.rs index 7e611d1..3da3ccb 100644 --- a/stellar_rust_sdk/src/offers/mod.rs +++ b/stellar_rust_sdk/src/offers/mod.rs @@ -229,12 +229,12 @@ pub mod test { const BUYING_ASSET_CODE: &str = "EURCAllow"; const BUYING_ASSET_ISSUER: &str = "GA6HVGLFUF3BHHGR5CMYXIVZ3RYVUH5EUYAOAY4T3OKI5OQVIWVRK24R"; - const AMOUNT: &str = "922192119415.1975807"; + const AMOUNT: &str = "922192119411.8475807"; const PRICE_R_N: &u32 = &1; const PRICE_R_D: &u32 = &1; const PRICE: &str = "1.0000000"; - const LAST_MODIFIED_LEDGER: &u32 = &923809; - const LAST_MODIFIED_TIME: &str = "2024-08-07T02:21:13Z"; + const LAST_MODIFIED_LEDGER: &u32 = &1280060; + const LAST_MODIFIED_TIME: &str = "2024-08-28T18:20:37Z"; let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); diff --git a/stellar_rust_sdk/src/paths/find_payment_paths_request.rs b/stellar_rust_sdk/src/paths/find_payment_paths_request.rs new file mode 100644 index 0000000..f2f54c6 --- /dev/null +++ b/stellar_rust_sdk/src/paths/find_payment_paths_request.rs @@ -0,0 +1,203 @@ +use crate::models::{is_public_key, Request}; +use crate::paths::*; +use crate::BuildQueryParametersExt; + +/// Represents a request to find payment paths on the Stellar Horizon API. +/// +/// This struct is designed to construct a query for discovering viable payment paths from a +/// source account to a destination account, given specific destination assets and amounts. +/// It adheres to the structure and parameters required by the Horizon API for retrieving +/// payment paths. +/// +/// # Usage +/// +/// Create an instance using the `new` method, and then specify the destination asset, amount, +/// and source account using the provided setters. Once the required parameters are set, you +/// can pass this request object to the appropriate method in the Horizon client to fetch +/// the available payment paths. +/// +/// # Example +/// ``` +/// use stellar_rs::paths::prelude::*; +/// use stellar_rs::paths::{AssetType}; +/// +/// let request = FindPaymentsPathRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_source_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap(); // Sets the destination account. +/// ``` +/// +#[derive(Default)] +pub struct FindPaymentsPathRequest< + DAs = NoDestinationAsset, + DAm = NoDestinationAmount, + S = NoSourceAccount, +> { + /// Represents the asset type being sent to the destination account. + pub destination_asset: DAs, + /// Specifies the amount of the destination asset to be received. + pub destination_amount: DAm, + /// Optionally contains the public key of the destination account. + pub destination_account: Option, + /// Identifies the source account from which the payment path originates. + pub source_account: S, +} + +impl FindPaymentsPathRequest { + /// Creates a new `FindPaymentsPathRequest` with default parameters. + pub fn new() -> Self { + FindPaymentsPathRequest { + destination_asset: NoDestinationAsset, + destination_amount: NoDestinationAmount, + destination_account: None, + source_account: NoSourceAccount, + } + } +} + +impl FindPaymentsPathRequest { + /// Sets the destination asset for the payment path request. + /// + /// # Arguments + /// * `destination_asset_type` - The type of asset being sent to the destination account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the destination asset set. + /// + pub fn set_destination_asset( + self, + destination_asset_type: AssetType, + ) -> Result, String> { + Ok(FindPaymentsPathRequest { + destination_asset: DestinationAsset(destination_asset_type), + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: self.source_account, + }) + } + + /// Sets the destination amount for the payment path request. + /// + /// # Arguments + /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the destination amount set. + /// + pub fn set_destination_amount( + self, + destination_amount: impl Into, + ) -> Result, String> { + Ok(FindPaymentsPathRequest { + destination_asset: self.destination_asset, + destination_amount: DestinationAmount(destination_amount.into()), + destination_account: self.destination_account, + source_account: self.source_account, + }) + } + + /// Sets the source account for the payment path request. + /// + /// # Arguments + /// * `source_account` - The Stellar public key of the source account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the source account set + /// + pub fn set_source_account( + self, + source_account: impl Into, + ) -> Result, String> { + let source_account = source_account.into(); + if let Err(e) = is_public_key(&source_account) { + return Err(e.to_string()); + } + + Ok(FindPaymentsPathRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: SourceAccount(source_account), + }) + } +} + +impl FindPaymentsPathRequest { + /// Sets the destination account for the payment path request. + /// + /// # Arguments + /// * `destination_account` - The Stellar public key of the destination account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the destination account set. + /// + pub fn set_destination_account( + self, + destination_account: impl Into, + ) -> Result, String> + { + let destination_account = destination_account.into(); + if let Err(e) = is_public_key(&destination_account) { + return Err(e.to_string()); + } + + Ok(FindPaymentsPathRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: Some(destination_account), + source_account: self.source_account, + }) + } +} + +impl Request for FindPaymentsPathRequest { + fn get_query_parameters(&self) -> String { + let asset_type_prefix = "destination_asset_type="; + let asset_code_prefix = "&destination_asset_code="; + let asset_issuer_prefix = "&destination_asset_issuer="; + + // Construct parameters for destination asset. + let parameters = match &self.destination_asset { + DestinationAsset(AssetType::Native) => format!("{}native", asset_type_prefix), + DestinationAsset(AssetType::CreditAlphanum4(asset_data)) + | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.destination_asset { + DestinationAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + DestinationAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + _ => "", // should not be reached + }; + + format!( + "{}{}{}{}{}{}", + asset_type_prefix, + asset_type, + asset_code_prefix, + asset_data.asset_code, + asset_issuer_prefix, + asset_data.issuer_account_id + ) + } + }; + + // Construct and return the query parameters. + vec![ + Some(parameters), + Some(format!("destination_amount={}", self.destination_amount.0)), + self.destination_account + .as_ref() + .map(|d| format!("destination_account={}", d)), + Some(format!("source_account={}", self.source_account.0)), + ] + .build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}{}", + base_url, + super::PATHS_PATH, + self.get_query_parameters() + ) + } +} diff --git a/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs b/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs new file mode 100644 index 0000000..85de18f --- /dev/null +++ b/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs @@ -0,0 +1,260 @@ +use crate::models::{is_public_key, Request}; +use crate::paths::*; +use crate::BuildQueryParametersExt; + +/// Represents the absence of either a source account or source asset(s). +#[derive(Default, Clone, Debug)] +pub struct NoSource; + +/// Represents the source which can be either a vector of assets, or an account. +/// Exactly one of these must be set, in order to make a valid request. +#[derive(Clone, Debug)] +pub enum Source { + /// A vector of assets available to the sender. Any returned path must start with an asset in this list. + SourceAssets(Vec), + /// The Stellar address of the sender. Any returned path must start with an asset that the sender holds. + SourceAccount(String), +} + +impl Default for Source { + fn default() -> Self { + Source::SourceAssets(Vec::new()) + } +} + +/// Represents a request to list strict receive payment paths on the Stellar Horizon API. +/// +/// This struct is designed to construct a query for listing payment paths that allow +/// a specified destination amount of a specified asset to be received, considering one or more +/// source assets and accounts. It adheres to the structure and parameters required by the Horizon API +/// for retrieving such payment paths. +/// +/// # Usage +/// +/// Create an instance using the `new` method, and then specify the destination asset, the destination +/// amount, and the source using the provided setters. Once the required parameters are set, optional +/// parameters can be set, and the request object can then be passed to the appropriate method +/// in the Horizon client to fetch the available strict receive payment paths. +/// +/// # Example +/// ``` +/// use stellar_rs::paths::prelude::*; +/// use stellar_rs::paths::{AssetType, IssuedOrNative}; +/// +/// let request = ListStrictReceivePaymentPathsRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_source(Source::SourceAccount("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string())).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap(); // Sets the destination account. +/// ``` +/// +#[derive(Default, Clone)] +pub struct ListStrictReceivePaymentPathsRequest< + DAs = NoDestinationAsset, + DAm = NoDestinationAmount, + S = Source, +> { + /// Represents the asset type being received by the destination account. + destination_asset: DAs, + /// Specifies the amount of the destination asset to be received. + destination_amount: DAm, + /// Optionally contains the public key of the destination account. + destination_account: Option, + /// Represents the source which can be either a vector of assets, or an account. + source: S, +} + +impl ListStrictReceivePaymentPathsRequest { + /// Creates a new `ListStrictReceivePaymentPathsRequest` with default parameters. + pub fn new() -> Self { + ListStrictReceivePaymentPathsRequest { + destination_asset: NoDestinationAsset, + destination_amount: NoDestinationAmount, + destination_account: None, + source: NoSource, + } + } +} + +impl ListStrictReceivePaymentPathsRequest { + /// Sets the destination asset for the payment path request. + /// + /// # Arguments + /// * `destination_asset_type` - The type of asset being received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination asset set. + /// + pub fn set_destination_asset( + self, + destination_asset_type: AssetType, + ) -> Result, String> { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: DestinationAsset(destination_asset_type), + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source: self.source, + }) + } + + /// Sets the destination amount for the payment path request. + /// + /// # Arguments + /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination amount set. + /// + pub fn set_destination_amount( + self, + destination_amount: impl Into, + ) -> Result, String> { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: DestinationAmount(destination_amount.into()), + destination_account: self.destination_account, + source: self.source, + }) + } + + /// Sets the source for the payment path request. + /// + /// # Arguments + /// * `source` - One of the `Source` enum types. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the source set. + /// + pub fn set_source( + self, + source: Source, + ) -> Result, String> { + match &source { + Source::SourceAssets(assets) => { + if assets.is_empty() { + return Err("SourceAssets cannot be empty".to_string()); + } + } + Source::SourceAccount(account) => { + if let Err(e) = is_public_key(&account) { + return Err(e.to_string()); + } + } + } + + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source: source, + }) + } +} + +impl ListStrictReceivePaymentPathsRequest { + /// Sets the destination account for the payment path request. + /// + /// # Arguments + /// * `destination_account` - The Stellar public key of the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination account set. + /// + pub fn set_destination_account( + self, + destination_account: impl Into, + ) -> Result, String> + { + let destination_account = destination_account.into(); + if let Err(e) = is_public_key(&destination_account) { + return Err(e.to_string()); + } + + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: Some(destination_account.into()), + source: self.source, + }) + } +} + +impl Request for ListStrictReceivePaymentPathsRequest { + fn get_query_parameters(&self) -> String { + let asset_type_prefix = "destination_asset_type="; + let asset_code_prefix = "&destination_asset_code="; + let asset_issuer_prefix = "&destination_asset_issuer="; + + // Construct parameters for destination asset. + let destination_asset_parameters = match &self.destination_asset { + DestinationAsset(AssetType::Native) => format!("{}native", asset_type_prefix), + DestinationAsset(AssetType::CreditAlphanum4(asset_data)) + | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.destination_asset { + DestinationAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + DestinationAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + _ => "", // should not be reached + }; + + format!( + "{}{}{}{}{}{}", + asset_type_prefix, + asset_type, + asset_issuer_prefix, + asset_data.issuer_account_id, + asset_code_prefix, + asset_data.asset_code, + ) + } + }; + + let source = match &self.source { + Source::SourceAssets(source_assets) => { + // Construct source asset parameters, if any. + // If no source asset parameters are set, return an empty vector which will later be ignored. + source_assets + .iter() + .enumerate() + .map(|(i, asset)| { + let prefix = if i == 0 { "source_assets=" } else { "%2C" }; + match asset { + IssuedOrNative::Native => format!("{}native", prefix), + IssuedOrNative::Issued(asset_data) => { + format!( + "{}{}%3A{}", + prefix, asset_data.asset_code, asset_data.issuer_account_id + ) + } + } + }) + .collect::>() + .join("") + } + Source::SourceAccount(account) => { + format!("source_account={}", account) + } + }; + + // Create query parameters vector. + let query_parameters = vec![ + Some(destination_asset_parameters), + Some(format!("destination_amount={}", self.destination_amount.0)), + self.destination_account + .as_ref() + .map(|d| format!("destination_account={}", d)), + Some(source), + ]; + + query_parameters.build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}/{}{}", + base_url, + super::PATHS_PATH, + super::PATHS_STRICT_RECEIVE_PATH, + self.get_query_parameters() + ) + } +} diff --git a/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs b/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs new file mode 100644 index 0000000..9c690ef --- /dev/null +++ b/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs @@ -0,0 +1,235 @@ +use crate::models::{is_public_key, Request}; +use crate::paths::*; +use crate::BuildQueryParametersExt; + +/// Represents the absence of a source asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoSourceAsset; + +/// Represents the source asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAsset(AssetType); + +/// Represents the absence of a source amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoSourceAmount; + +/// Represents the source amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAmount(String); + +/// Represents the absence of a source amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoDestination; + +/// Represents the destination which can be either a vector of assets, or an account. +/// Exactly one of these must be set, in order to make a valid request. +#[derive(Clone, Debug)] +pub enum Destination { + DestinationAssets(Vec), + DestinationAccount(String), +} + +impl Default for Destination { + fn default() -> Self { + Destination::DestinationAssets(Vec::new()) + } +} + +/// Represents a request to list strict send payment paths on the Stellar Horizon API. +/// +/// This struct is designed to construct a query for listing the paths a payment can take based +/// on the amount of an asset you want the recipient to receive. The destination asset amount +/// stays constant, and the type and amount of an asset sent varies based on offers in the order books. +/// +/// # Usage +/// +/// Create an instance using the `new` method, and then specify the source asset, source amount, +/// and the destination using the provided setters. Once the required parameters are set, optional +/// parameters can be set, and the request object can then be passed to the appropriate method +/// in the Horizon client to fetch the available strict send payment paths. +/// +/// # Example +/// ``` +/// use stellar_rs::paths::prelude::*; +/// use stellar_rs::paths::{AssetType, IssuedOrNative}; +/// +/// let request = ListStrictSendPaymentPathsRequest::new() +/// .set_source_asset(AssetType::Native).unwrap() // Sets the source asset to native XLM. +/// .set_source_amount("100".to_string()).unwrap() // Sets the amount of the source asset. +/// .set_destination(Destination::DestinationAccount("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string())).unwrap(); // Sets an account as destination. +/// ``` +/// +#[derive(Default, Clone)] +pub struct ListStrictSendPaymentPathsRequest< + SAs = NoSourceAsset, + SAm = NoSourceAmount, + D = Destination, +> { + /// Represents the asset type being received by the source account. + source_asset: SAs, + /// Specifies the amount of the source asset to be received. + source_amount: SAm, + /// Represents the destination which can be either a vector of assets, or an account. + destination: D, +} + +impl ListStrictSendPaymentPathsRequest { + /// Creates a new `ListStrictSendPaymentPathsRequest` with default parameters. + pub fn new() -> Self { + ListStrictSendPaymentPathsRequest { + source_asset: NoSourceAsset, + source_amount: NoSourceAmount, + destination: NoDestination, + } + } +} + +impl ListStrictSendPaymentPathsRequest { + /// Sets the source asset for the payment path request. + /// + /// # Arguments + /// * `source_asset_type` - The type of asset being received by the source account. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the source asset set. + /// + pub fn set_source_asset( + self, + source_asset_type: AssetType, + ) -> Result, String> { + Ok(ListStrictSendPaymentPathsRequest { + source_asset: SourceAsset(source_asset_type), + source_amount: self.source_amount, + destination: self.destination, + }) + } + + /// Sets the source amount for the payment path request. + /// + /// # Arguments + /// * `source_amount` - The amount of the asset to be received by the source account. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the source amount set. + /// + pub fn set_source_amount( + self, + source_amount: impl Into, + ) -> Result, String> { + Ok(ListStrictSendPaymentPathsRequest { + source_asset: self.source_asset, + source_amount: SourceAmount(source_amount.into()), + destination: self.destination, + }) + } + + /// Sets the destination for the payment path request. + /// + /// # Arguments + /// * `destination` - One of the `Destination` enum types. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination set. + /// + pub fn set_destination( + self, + destination: Destination, + ) -> Result, String> { + match &destination { + Destination::DestinationAssets(assets) => { + if assets.is_empty() { + return Err("DestinationAssets cannot be empty".to_string()); + } + } + Destination::DestinationAccount(account) => { + if let Err(e) = is_public_key(&account) { + return Err(e.to_string()); + } + } + } + + Ok(ListStrictSendPaymentPathsRequest { + source_asset: self.source_asset, + source_amount: self.source_amount, + destination: destination, + }) + } +} + +impl Request for ListStrictSendPaymentPathsRequest { + fn get_query_parameters(&self) -> String { + let asset_type_prefix = "source_asset_type="; + let asset_code_prefix = "&source_asset_code="; + let asset_issuer_prefix = "&source_asset_issuer="; + + // Construct parameters for source asset. + let source_asset_parameters = match &self.source_asset { + SourceAsset(AssetType::Native) => format!("{}native", asset_type_prefix), + SourceAsset(AssetType::CreditAlphanum4(asset_data)) + | SourceAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.source_asset { + SourceAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + SourceAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + _ => "", // should not be reached + }; + + format!( + "{}{}{}{}{}{}", + asset_type_prefix, + asset_type, + asset_issuer_prefix, + asset_data.issuer_account_id, + asset_code_prefix, + asset_data.asset_code, + ) + } + }; + + let destination = match &self.destination { + Destination::DestinationAssets(destination_assets) => { + // Construct destination asset parameters, if any. + // If no destination asset parameters are set, return an empty vector which will later be ignored. + destination_assets + .iter() + .enumerate() + .map(|(i, asset)| { + let prefix = if i == 0 { "destination_assets=" } else { "%2C" }; + match asset { + IssuedOrNative::Native => format!("{}native", prefix), + IssuedOrNative::Issued(asset_data) => { + format!( + "{}{}%3A{}", + prefix, asset_data.asset_code, asset_data.issuer_account_id + ) + } + } + }) + .collect::>() + .join("") + } + Destination::DestinationAccount(account) => { + format!("destination_account={}", account) + } + }; + + // Create query parameters vector. + let query_parameters = vec![ + Some(format!("source_amount={}", self.source_amount.0)), + Some(destination), + Some(source_asset_parameters), + ]; + + query_parameters.build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}/{}{}", + base_url, + super::PATHS_PATH, + super::PATHS_STRICT_SEND_PATH, + self.get_query_parameters() + ) + } +} diff --git a/stellar_rust_sdk/src/paths/mod.rs b/stellar_rust_sdk/src/paths/mod.rs new file mode 100644 index 0000000..93e5939 --- /dev/null +++ b/stellar_rust_sdk/src/paths/mod.rs @@ -0,0 +1,484 @@ +/// Provides the `FindPaymentPathsRequest`. +/// +/// # Usage +/// This module provides the `FindPaymentPathsRequest` struct, specifically designed for +/// constructing requests to find payment paths based on certain criteria. It is tailored for +/// use with the [`HorizonClient::get_find_payment_paths`](crate::horizon_client::HorizonClient::get_find_payment_paths) +/// method. +/// +pub mod find_payment_paths_request; + +/// Provides the `ListStrictReceivePaymentPathsRequest`. +/// +/// # Usage +/// This module provides the `ListStrictReceivePaymentPathsRequest` struct, specifically designed for +/// constructing requests to list strict receive payment paths. It is tailored for +/// use with the [`HorizonClient::get_list_strict_receive_payment_paths`](crate::horizon_client::HorizonClient::get_list_strict_receive_payment_paths) +/// method. +/// +pub mod list_strict_receive_payment_paths_request; + +/// Provides the `ListStrictSendPaymentPathsRequest`. +/// +/// # Usage +/// This module provides the `ListStrictSendPaymentPathsRequest` struct, specifically designed for +/// constructing requests to list strict send payment paths. It is tailored for +/// use with the [`HorizonClient::get_list_strict_send_payment_paths`](crate::horizon_client::HorizonClient::get_list_strict_send_payment_paths) +/// method. +/// +pub mod list_strict_send_payment_paths_request; + +/// Provides the response structures. +/// +/// This module defines structures representing the responses from the payment path API. +/// The structures are designed to deserialize the JSON response into Rust objects, enabling +/// straightforward access to various details of payment paths. +/// +/// # Usage +/// These structures are equipped with serialization capabilities to handle the JSON data from the +/// payment path server and with getter methods for easy field access. +/// +pub mod response; + +/// The base paths for path-related endpoints in the Horizon API. +/// +/// # Usage +/// This variable is intended to be used internally by the request-building logic +/// to ensure consistent and accurate path construction for offer-related API calls. +/// +pub(crate) static PATHS_PATH: &str = "paths"; // the base API path +pub(crate) static PATHS_STRICT_RECEIVE_PATH: &str = "strict-receive"; +pub(crate) static PATHS_STRICT_SEND_PATH: &str = "strict-send"; + +/// Represents the absence of a destination asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoDestinationAsset; + +/// Represents a source asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct DestinationAsset(AssetType); + +/// Represents the absence of a destination amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoDestinationAmount; + +/// Represents the destination amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct DestinationAmount(String); + +/// Represents the absence of a source account for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoSourceAccount; + +/// Represents the source account for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAccount(String); + +/// Represents structure of the required asset. +#[derive(Default, Clone, Debug)] +pub enum AssetType { + #[default] + Native, + CreditAlphanum4(Asset), + CreditAlphanum12(Asset), +} + +/// Represents an asset containing an asset code and issuer account ID. +#[derive(Clone, Debug)] +pub struct Asset { + pub asset_code: String, + pub issuer_account_id: String, +} + +/// Represents structure of an asset used in the vector of optional assets. +#[derive(Default, Clone, Debug)] +pub enum IssuedOrNative { + #[default] + Native, + Issued(Asset), +} + +/// The `prelude` module of the `paths` module. +/// +/// # Usage +/// This module serves as a convenience for users of the payment path Rust SDK, allowing for easy and +/// ergonomic import of the most commonly used items across various modules. It re-exports +/// key structs and traits from the sibling modules, simplifying access to these components +/// when using the library. +/// +/// By importing the contents of `prelude`, users can conveniently access the primary +/// functionalities of the payment path-related modules without needing to import each item +/// individually. +/// +/// # Contents +/// +/// The `prelude` includes the following re-exports: +/// +/// * From `find_payment_paths_request`: All items (e.g. `FindPaymentPathsRequest`). +/// * From `list_strict_receive_payment_paths_request`: All items (e.g. `ListStrictReceivePaymentPathsRequest`). +/// * From `list_strict_send_payment_paths_request`: All items (e.g. `ListStrictSendPaymentPathsRequest`). +/// * From `response`: All items (e.g. `PaymentPathResponse`, etc.). +/// +pub mod prelude { + pub use super::find_payment_paths_request::*; + pub use super::list_strict_receive_payment_paths_request::*; + pub use super::list_strict_send_payment_paths_request::*; + pub use super::response::*; + pub use super::{ + DestinationAmount, DestinationAsset, NoDestinationAmount, NoDestinationAsset, + NoSourceAccount, SourceAccount, + }; +} + +#[cfg(test)] +mod tests { + use super::prelude::*; + use super::{AssetType, IssuedOrNative}; + use crate::{horizon_client::HorizonClient, models::*}; + + const SOURCE_ASSET_TYPE: &str = "native"; + const SOURCE_AMOUNT: &str = "100.0000000"; + const DESTINATION_ASSET_TYPE: &str = "native"; + const DESTINATION_AMOUNT: &str = "100.0000000"; + + #[tokio::test] + async fn test_find_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH}; + + // Test creating and sending a request with source assets. Only the response status will be checked, as the request will not yield comparable data. + let request = FindPaymentsPathRequest::new() + .set_destination_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source_account( + "GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H".to_string(), + ) + .unwrap() + .set_destination_account( + "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + ) + .unwrap(); + + let expected_parameters = + "?destination_asset_type=credit_alphanum4&destination_asset_code=USDC&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H"; + + assert_eq!(expected_parameters, request.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!("{}/{}{}", url, PATHS_PATH, request.get_query_parameters()), + request.build_url(url) + ); + + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + let response = horizon_client.get_find_payment_paths(&request).await; + + assert!(response.clone().is_ok()); + + // Test creating and sending a request with source account. + let request = FindPaymentsPathRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("100".to_string()) + .unwrap() + .set_source_account( + "GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS".to_string(), + ) + .unwrap(); + + let expected_parameters: &str = + "?destination_asset_type=native&destination_amount=100&source_account=GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS"; + assert_eq!(request.get_query_parameters(), expected_parameters); + + let url = "base_url"; + assert_eq!( + format!("{}/{}{}", url, PATHS_PATH, request.get_query_parameters()), + request.build_url(url) + ); + + let response = horizon_client.get_find_payment_paths(&request).await; + + + assert!(response.clone().is_ok()); + let binding = response.unwrap(); + let response = &binding.embedded().records()[0]; + assert_eq!(response.source_asset_type(), SOURCE_ASSET_TYPE); + assert_eq!(response.source_amount(), SOURCE_AMOUNT); + assert_eq!(response.destination_asset_type(), DESTINATION_ASSET_TYPE); + assert_eq!(response.destination_amount(), DESTINATION_AMOUNT); + + // Test creating a request with an invalid source account ID. + let request = FindPaymentsPathRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source_account("invalid_account_id".to_string()); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + } + + #[tokio::test] + async fn test_list_strict_receive_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_RECEIVE_PATH}; + + // Test creating and sending a request with source assets. Only the response status will be checked, as the request will not yield comparable data. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAssets(vec![ + IssuedOrNative::Native, + IssuedOrNative::Native, + IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + }), + ])) + .unwrap(); + + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&source_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; + + assert_eq!(request.get_query_parameters(), expected_parameters); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_RECEIVE_PATH, + request.get_query_parameters() + ), + request.build_url(url) + ); + + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + let response = horizon_client + .get_list_strict_receive_payment_paths(&request) + .await; + + assert!(response.clone().is_ok()); + + // Test creating and sending a request with destination account. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("100".to_string()) + .unwrap() + .set_source(Source::SourceAccount( + "GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS".to_string(), + )) + .unwrap(); + + let expected_parameters: &str = + "?destination_asset_type=native&destination_amount=100&source_account=GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS"; + assert_eq!(request.get_query_parameters(), expected_parameters); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_RECEIVE_PATH, + request.get_query_parameters() + ), + request.build_url(url) + ); + + let response = horizon_client + .get_list_strict_receive_payment_paths(&request) + .await; + + assert!(response.clone().is_ok()); + let binding = response.unwrap(); + let response = &binding.embedded().records()[0]; + assert_eq!(response.source_asset_type(), SOURCE_ASSET_TYPE); + assert_eq!(response.source_amount(), SOURCE_AMOUNT); + assert_eq!(response.destination_asset_type(), DESTINATION_ASSET_TYPE); + assert_eq!(response.destination_amount(), DESTINATION_AMOUNT); + + // Test creating a request with an empty source assets vector. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAssets(Vec::new())); + assert_eq!(request.err().unwrap(), "SourceAssets cannot be empty"); + + // Test creating a request with an invalid asset source account ID. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAccount("invalid_account_id".to_string())); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + + // Test creating a request with an invalid source account ID. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAssets(vec![IssuedOrNative::Native])) + .unwrap() + .set_destination_account("invalid_account_id"); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + } + + #[tokio::test] + async fn test_list_strict_send_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_SEND_PATH}; + + // Test creating and sending a request with destination assets. Only the response status will be checked, as the request will not yield comparable data. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAssets(vec![ + IssuedOrNative::Native, + IssuedOrNative::Native, + IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + }), + ])) + .unwrap(); + + let expected_parameters: &str = + "?source_amount=42&destination_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_asset_type=credit_alphanum4&source_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&source_asset_code=USDC"; + + assert_eq!(request.get_query_parameters(), expected_parameters); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_SEND_PATH, + request.get_query_parameters() + ), + request.build_url(url) + ); + + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + let response = horizon_client + .get_list_strict_send_payment_paths(&request) + .await; + + assert!(response.clone().is_ok()); + + // Test creating and sending a request with destination account. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("100".to_string()) + .unwrap() + .set_destination(Destination::DestinationAccount( + "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + )) + .unwrap(); + + let expected_parameters: &str = + "?source_amount=100&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_asset_type=native"; + + assert_eq!(request.get_query_parameters(), expected_parameters); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_SEND_PATH, + request.get_query_parameters() + ), + request.build_url(url) + ); + + let response = horizon_client + .get_list_strict_send_payment_paths(&request) + .await; + + assert!(response.clone().is_ok()); + let binding = response.unwrap(); + let response = &binding.embedded().records()[0]; + assert_eq!(response.source_asset_type(), SOURCE_ASSET_TYPE); + assert_eq!(response.source_amount(), SOURCE_AMOUNT); + assert_eq!(response.destination_asset_type(), DESTINATION_ASSET_TYPE); + assert_eq!(response.destination_amount(), DESTINATION_AMOUNT); + + // Test creating a request with an empty destination assets vector. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAssets(Vec::new())); + assert_eq!(request.err().unwrap(), "DestinationAssets cannot be empty"); + + // Test creating a request with an invalid destination asset account ID. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAccount( + "invalid_account_id".to_string(), + )); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + + // Test creating a request with an invalid destination account ID. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAccount( + "invalid_account_id".to_string(), + )); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + } +} diff --git a/stellar_rust_sdk/src/paths/response.rs b/stellar_rust_sdk/src/paths/response.rs new file mode 100644 index 0000000..8ab8d96 --- /dev/null +++ b/stellar_rust_sdk/src/paths/response.rs @@ -0,0 +1,62 @@ +use crate::models::prelude::Embedded; +use crate::models::Response; +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; + +/// Represents the response for a payment paths query. +/// +/// This struct defines the overall structure of the response for a query +/// that retrieves payment paths. It includes the embedded results which +/// consist of the details of each payment path. +/// +#[derive(Debug, Clone, Serialize, Deserialize, Getters)] +pub struct PathsResponse { + #[serde(rename = "_embedded")] + embedded: Embedded, +} + +/// Represents a single payment path. +/// +/// This struct details a specific payment path including information about +/// the source and destination assets, their amounts, and the sequence of assets +/// that form the path. +/// +#[derive(Debug, Clone, Serialize, Deserialize, Getters)] +pub struct Path { + /// The type of the source asset. + source_asset_type: String, + /// The code of the source asset. Optional. + source_asset_code: Option, + /// The issuer of the source asset. Optional. + source_asset_issuer: Option, + /// The amount of the source asset. + source_amount: String, + /// The type of the destination asset. + destination_asset_type: String, + /// The code of the destination asset. Optional. + destination_asset_code: Option, + /// The issuer of the destination asset. Optional. + destination_asset_issuer: Option, + /// The amount of the destination asset. + destination_amount: String, + /// A vector of assets forming the path. + path: Vec, +} + +/// Represents a single asset used in the payment path. +/// +/// This struct details the information about an asset including its type, +/// code, and issuer. +/// +#[derive(Debug, Clone, Serialize, Deserialize, Getters)] +pub struct Asset { + asset_type: String, + asset_code: Option, + asset_issuer: Option, +} + +impl Response for PathsResponse { + fn from_json(json: String) -> Result { + serde_json::from_str(&json).map_err(|e| e.to_string()) + } +}