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}