Skip to content

Commit

Permalink
Refactor requests to match API and extend tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-pease committed Sep 9, 2024
1 parent 95c12a0 commit 6e4f01b
Show file tree
Hide file tree
Showing 6 changed files with 509 additions and 361 deletions.
10 changes: 3 additions & 7 deletions stellar_rust_sdk/src/horizon_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::{
details_request::{BuyingAsset, DetailsRequest, SellingAsset},
response::DetailsResponse,
},
paths::{prelude::*, DestinationAmount, DestinationAsset, SourceAccount},
paths::prelude::*,
payments::prelude::*,
trade_aggregations::prelude::*,
trades::prelude::*,
Expand Down Expand Up @@ -2030,11 +2030,7 @@ impl HorizonClient {
/// filters or parameters.
pub async fn get_list_strict_receive_payment_paths(
&self,
request: &ListStrictReceivePaymentPathsRequest<
DestinationAsset,
DestinationAmount,
SourceAccount,
>,
request: &ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, Source>,
) -> Result<PathsResponse, String> {
self.get::<PathsResponse>(request).await
}
Expand All @@ -2059,7 +2055,7 @@ impl HorizonClient {
/// filters or parameters.
pub async fn get_list_strict_send_payment_paths(
&self,
request: &ListStrictSendPaymentPathsRequest<DestinationAsset, DestinationAmount>,
request: &ListStrictSendPaymentPathsRequest<SourceAsset, SourceAmount, Destination>,
) -> Result<PathsResponse, String> {
self.get::<PathsResponse>(request).await
}
Expand Down
22 changes: 16 additions & 6 deletions stellar_rust_sdk/src/paths/find_payment_paths_request.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::models::Request;
use crate::models::{is_public_key, Request};
use crate::paths::*;
use crate::BuildQueryParametersExt;

Expand Down Expand Up @@ -87,11 +87,11 @@ impl<DAs, DAm, S> FindPaymentsPathRequest<DAs, DAm, S> {
///
pub fn set_destination_amount(
self,
destination_amount: String,
destination_amount: impl Into<String>,
) -> Result<FindPaymentsPathRequest<DAs, DestinationAmount, S>, String> {
Ok(FindPaymentsPathRequest {
destination_asset: self.destination_asset,
destination_amount: DestinationAmount(destination_amount),
destination_amount: DestinationAmount(destination_amount.into()),
destination_account: self.destination_account,
source_account: self.source_account,
})
Expand All @@ -107,8 +107,13 @@ impl<DAs, DAm, S> FindPaymentsPathRequest<DAs, DAm, S> {
///
pub fn set_source_account(
self,
source_account: String,
source_account: impl Into<String>,
) -> Result<FindPaymentsPathRequest<DAs, DAm, SourceAccount>, 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,
Expand All @@ -129,9 +134,14 @@ impl FindPaymentsPathRequest<DestinationAsset, DestinationAmount, SourceAccount>
///
pub fn set_destination_account(
self,
destination_account: String,
destination_account: impl Into<String>,
) -> Result<FindPaymentsPathRequest<DestinationAsset, DestinationAmount, SourceAccount>, 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,
Expand All @@ -149,7 +159,7 @@ impl Request for FindPaymentsPathRequest<DestinationAsset, DestinationAmount, So

// Construct parameters for destination asset.
let parameters = match &self.destination_asset {
DestinationAsset(AssetType::Native) => format!("{}=native", asset_type_prefix),
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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,77 @@
use crate::models::Request;
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<IssuedOrNative>),
/// 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 discovering payment paths that allow
/// 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, amount,
/// source account, and source assets 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 strict receive payment paths.
/// 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, SourceAsset, IssuedOrNative};
/// 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_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account.
/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap() // Sets the destination account.
/// .set_source_assets(vec![SourceAsset(IssuedOrNative::Native)]).unwrap(); // Sets the source assets.
/// .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 = NoSourceAccount,
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<String>,
/// Identifies the source account from which the payment path originates.
source_account: S,
/// Optionally contains a list of source assets to consider when finding payment paths.
source_assets: Option<Vec<SourceAsset>>,
/// Represents the source which can be either a vector of assets, or an account.
source: S,
}

impl
ListStrictReceivePaymentPathsRequest<NoDestinationAsset, NoDestinationAmount, NoSourceAccount>
{
impl ListStrictReceivePaymentPathsRequest<NoDestinationAsset, NoDestinationAmount, NoSource> {
/// Creates a new `ListStrictReceivePaymentPathsRequest` with default parameters.
pub fn new() -> Self {
ListStrictReceivePaymentPathsRequest {
destination_asset: NoDestinationAsset,
destination_amount: NoDestinationAmount,
destination_account: None,
source_account: NoSourceAccount,
source_assets: None,
source: NoSource,
}
}
}
Expand All @@ -79,47 +93,65 @@ impl<DAs, DAm, S> ListStrictReceivePaymentPathsRequest<DAs, DAm, S> {
destination_asset: DestinationAsset(destination_asset_type),
destination_amount: self.destination_amount,
destination_account: self.destination_account,
source_account: self.source_account,
source_assets: self.source_assets,
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: String,
destination_amount: impl Into<String>,
) -> Result<ListStrictReceivePaymentPathsRequest<DAs, DestinationAmount, S>, String> {
Ok(ListStrictReceivePaymentPathsRequest {
destination_asset: self.destination_asset,
destination_amount: DestinationAmount(destination_amount),
destination_amount: DestinationAmount(destination_amount.into()),
destination_account: self.destination_account,
source_account: self.source_account,
source_assets: self.source_assets,
source: self.source,
})
}

/// Sets the destination amount for the payment path request.
/// Sets the source for the payment path request.
///
/// # Arguments
/// * `destination_amount` - The amount of the asset to be received by the destination account.
/// * `source` - One of the `Source` enum types.
///
/// # Returns
/// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination amount set.
/// A new instance of `ListStrictReceivePaymentPathsRequest` with the source set.
///
pub fn set_source_account(
pub fn set_source(
self,
source_account: String,
) -> Result<ListStrictReceivePaymentPathsRequest<DAs, DAm, SourceAccount>, String> {
source: Source,
) -> Result<ListStrictReceivePaymentPathsRequest<DAs, DAm, Source>, 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_account: SourceAccount(source_account),
source_assets: self.source_assets,
source: source,
})
}
}

impl ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, SourceAccount> {
impl ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, Source> {
/// Sets the destination account for the payment path request.
///
/// # Arguments
Expand All @@ -130,55 +162,32 @@ impl ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, S
///
pub fn set_destination_account(
self,
destination_account: String,
) -> Result<
ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, SourceAccount>,
String,
> {
Ok(ListStrictReceivePaymentPathsRequest {
destination_asset: self.destination_asset,
destination_amount: self.destination_amount,
destination_account: Some(destination_account),
source_account: self.source_account,
source_assets: self.source_assets,
})
}
destination_account: impl Into<String>,
) -> Result<ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount>, String>
{
let destination_account = destination_account.into();
if let Err(e) = is_public_key(&destination_account) {
return Err(e.to_string());
}

/// Sets the source assets for the payment path request.
///
/// # Arguments
/// * `source_assets` - A list of source assets to consider when finding payment paths.
///
/// # Returns
/// A new instance of `ListStrictReceivePaymentPathsRequest` with the source assets set.
pub fn set_source_assets(
self,
source_assets: Vec<SourceAsset>,
) -> Result<
ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, SourceAccount>,
String,
> {
Ok(ListStrictReceivePaymentPathsRequest {
destination_asset: self.destination_asset,
destination_amount: self.destination_amount,
destination_account: self.destination_account,
source_account: self.source_account,
source_assets: Some(source_assets),
destination_account: Some(destination_account.into()),
source: self.source,
})
}
}

impl Request
for ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, SourceAccount>
{
impl Request for ListStrictReceivePaymentPathsRequest<DestinationAsset, DestinationAmount, Source> {
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::Native) => format!("{}native", asset_type_prefix),
DestinationAsset(AssetType::CreditAlphanum4(asset_data))
| DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => {
let asset_type = match self.destination_asset {
Expand All @@ -199,18 +208,18 @@ impl Request
}
};

// Construct source asset parameters, if any.
// If no source asset parameters are set, return an empty vector which will later be ignored.
let source_assets_parameters =
self.source_assets.as_ref().map_or(String::new(), |assets| {
assets
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 {
SourceAsset(IssuedOrNative::Native) => format!("{}native", prefix),
SourceAsset(IssuedOrNative::Issued(asset_data)) => {
IssuedOrNative::Native => format!("{}native", prefix),
IssuedOrNative::Issued(asset_data) => {
format!(
"{}{}%3A{}",
prefix, asset_data.asset_code, asset_data.issuer_account_id
Expand All @@ -220,23 +229,22 @@ impl Request
})
.collect::<Vec<_>>()
.join("")
});
}
Source::SourceAccount(account) => {
format!("source_account={}", account)
}
};

// Create query parameters vector.
let mut query_parameters = vec![
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(format!("source_account={}", self.source_account.0)),
Some(source),
];

// Only add source assets parameters if the vector is not empty, to prevent a trailing `&`.
if !source_assets_parameters.is_empty() {
query_parameters.push(Some(source_assets_parameters));
}

query_parameters.build_query_parameters()
}

Expand Down
Loading

0 comments on commit 6e4f01b

Please sign in to comment.