stellar_rs/paths/list_strict_send_payment_paths_request.rs
1use crate::models::{is_public_key, Request};
2use crate::paths::*;
3use crate::BuildQueryParametersExt;
4
5/// Represents the absence of a source asset for a payment path request.
6#[derive(Default, Clone, Debug)]
7pub struct NoSourceAsset;
8
9/// Represents the source asset for a payment path request.
10#[derive(Default, Clone, Debug)]
11pub struct SourceAsset(AssetType);
12
13/// Represents the absence of a source amount for a payment path request.
14#[derive(Default, Clone, Debug)]
15pub struct NoSourceAmount;
16
17/// Represents the source amount for a payment path request.
18#[derive(Default, Clone, Debug)]
19pub struct SourceAmount(String);
20
21/// Represents the absence of a source amount for a payment path request.
22#[derive(Default, Clone, Debug)]
23pub struct NoDestination;
24
25/// Represents the destination which can be either a vector of assets, or an account.
26/// Exactly one of these must be set, in order to make a valid request.
27#[derive(Clone, Debug)]
28pub enum Destination {
29 DestinationAssets(Vec<IssuedOrNative>),
30 DestinationAccount(String),
31}
32
33impl Default for Destination {
34 fn default() -> Self {
35 Destination::DestinationAssets(Vec::new())
36 }
37}
38
39/// Represents a request to list strict send payment paths on the Stellar Horizon API.
40///
41/// This struct is designed to construct a query for listing the paths a payment can take based
42/// on the amount of an asset you want the recipient to receive. The destination asset amount
43/// stays constant, and the type and amount of an asset sent varies based on offers in the order books.
44///
45/// # Usage
46///
47/// Create an instance using the `new` method, and then specify the source asset, source amount,
48/// and the destination using the provided setters. Once the required parameters are set, optional
49/// parameters can be set, and the request object can then be passed to the appropriate method
50/// in the Horizon client to fetch the available strict send payment paths.
51///
52/// # Example
53/// ```
54/// use stellar_rs::paths::prelude::*;
55/// use stellar_rs::models::prelude::*;
56///
57/// let request = ListStrictSendPaymentPathsRequest::new()
58/// .set_source_asset(AssetType::Native).unwrap() // Sets the source asset to native XLM.
59/// .set_source_amount("100".to_string()).unwrap() // Sets the amount of the source asset.
60/// .set_destination(Destination::DestinationAccount("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string())).unwrap(); // Sets an account as destination.
61/// ```
62///
63#[derive(Default, Clone)]
64pub struct ListStrictSendPaymentPathsRequest<
65 SAs = NoSourceAsset,
66 SAm = NoSourceAmount,
67 D = Destination,
68> {
69 /// Represents the asset type being received by the source account.
70 source_asset: SAs,
71 /// Specifies the amount of the source asset to be received.
72 source_amount: SAm,
73 /// Represents the destination which can be either a vector of assets, or an account.
74 destination: D,
75}
76
77impl ListStrictSendPaymentPathsRequest<NoSourceAsset, NoSourceAmount, NoDestination> {
78 /// Creates a new `ListStrictSendPaymentPathsRequest` with default parameters.
79 pub fn new() -> Self {
80 ListStrictSendPaymentPathsRequest {
81 source_asset: NoSourceAsset,
82 source_amount: NoSourceAmount,
83 destination: NoDestination,
84 }
85 }
86}
87
88impl<SAs, SAm, D> ListStrictSendPaymentPathsRequest<SAs, SAm, D> {
89 /// Sets the source asset for the payment path request.
90 ///
91 /// # Arguments
92 /// * `source_asset_type` - The type of asset being received by the source account.
93 ///
94 /// # Returns
95 /// A new instance of `ListStrictSendPaymentPathsRequest` with the source asset set.
96 ///
97 pub fn set_source_asset(
98 self,
99 source_asset_type: AssetType,
100 ) -> Result<ListStrictSendPaymentPathsRequest<SourceAsset, SAm, D>, String> {
101 Ok(ListStrictSendPaymentPathsRequest {
102 source_asset: SourceAsset(source_asset_type),
103 source_amount: self.source_amount,
104 destination: self.destination,
105 })
106 }
107
108 /// Sets the source amount for the payment path request.
109 ///
110 /// # Arguments
111 /// * `source_amount` - The amount of the asset to be received by the source account.
112 ///
113 /// # Returns
114 /// A new instance of `ListStrictSendPaymentPathsRequest` with the source amount set.
115 ///
116 pub fn set_source_amount(
117 self,
118 source_amount: impl Into<String>,
119 ) -> Result<ListStrictSendPaymentPathsRequest<SAs, SourceAmount, D>, String> {
120 Ok(ListStrictSendPaymentPathsRequest {
121 source_asset: self.source_asset,
122 source_amount: SourceAmount(source_amount.into()),
123 destination: self.destination,
124 })
125 }
126
127 /// Sets the destination for the payment path request.
128 ///
129 /// # Arguments
130 /// * `destination` - One of the `Destination` enum types.
131 ///
132 /// # Returns
133 /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination set.
134 ///
135 pub fn set_destination(
136 self,
137 destination: Destination,
138 ) -> Result<ListStrictSendPaymentPathsRequest<SAs, SAm, Destination>, String> {
139 match &destination {
140 Destination::DestinationAssets(assets) => {
141 if assets.is_empty() {
142 return Err("DestinationAssets cannot be empty".to_string());
143 }
144 }
145 Destination::DestinationAccount(account) => {
146 if let Err(e) = is_public_key(&account) {
147 return Err(e.to_string());
148 }
149 }
150 }
151
152 Ok(ListStrictSendPaymentPathsRequest {
153 source_asset: self.source_asset,
154 source_amount: self.source_amount,
155 destination: destination,
156 })
157 }
158}
159
160impl Request for ListStrictSendPaymentPathsRequest<SourceAsset, SourceAmount, Destination> {
161 fn get_query_parameters(&self) -> String {
162 let asset_type_prefix = "source_asset_type=";
163 let asset_code_prefix = "&source_asset_code=";
164 let asset_issuer_prefix = "&source_asset_issuer=";
165
166 // Construct parameters for source asset.
167 let source_asset_parameters = match &self.source_asset {
168 SourceAsset(AssetType::Native) => format!("{}native", asset_type_prefix),
169 SourceAsset(AssetType::Alphanumeric4(asset_data))
170 | SourceAsset(AssetType::Alphanumeric12(asset_data)) => {
171 let asset_type = match self.source_asset {
172 SourceAsset(AssetType::Alphanumeric4(_)) => "credit_alphanum4",
173 SourceAsset(AssetType::Alphanumeric12(_)) => "credit_alphanum12",
174 _ => "", // should not be reached
175 };
176
177 format!(
178 "{}{}{}{}{}{}",
179 asset_type_prefix,
180 asset_type,
181 asset_issuer_prefix,
182 asset_data.asset_issuer,
183 asset_code_prefix,
184 asset_data.asset_code,
185 )
186 }
187 };
188
189 let destination = match &self.destination {
190 Destination::DestinationAssets(destination_assets) => {
191 // Construct destination asset parameters, if any.
192 // If no destination asset parameters are set, return an empty vector which will later be ignored.
193 destination_assets
194 .iter()
195 .enumerate()
196 .map(|(i, asset)| {
197 let prefix = if i == 0 { "destination_assets=" } else { "%2C" };
198 match asset {
199 IssuedOrNative::Native => format!("{}native", prefix),
200 IssuedOrNative::Issued(asset_data) => {
201 format!(
202 "{}{}%3A{}",
203 prefix, asset_data.asset_code, asset_data.asset_issuer
204 )
205 }
206 }
207 })
208 .collect::<Vec<_>>()
209 .join("")
210 }
211 Destination::DestinationAccount(account) => {
212 format!("destination_account={}", account)
213 }
214 };
215
216 // Create query parameters vector.
217 let query_parameters = vec![
218 Some(format!("source_amount={}", self.source_amount.0)),
219 Some(destination),
220 Some(source_asset_parameters),
221 ];
222
223 query_parameters.build_query_parameters()
224 }
225
226 fn build_url(&self, base_url: &str) -> String {
227 format!(
228 "{}/{}/{}{}",
229 base_url,
230 super::PATHS_PATH,
231 super::PATHS_STRICT_SEND_PATH,
232 self.get_query_parameters()
233 )
234 }
235}