Skip to main content

binance_sdk/convert/rest_api/apis/
market_data_api.rs

1/*
2 * Binance Convert REST API
3 *
4 * OpenAPI Specification for the Binance Convert REST API
5 *
6 * The version of the OpenAPI document: 1.0.0
7 *
8 *
9 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
10 * https://openapi-generator.tech
11 * Do not edit the class manually.
12 */
13
14#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24    config::ConfigurationRestApi,
25    models::{ParamBuildError, RestApiResponse},
26    utils::send_request,
27};
28use crate::convert::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait MarketDataApi: Send + Sync {
34    async fn list_all_convert_pairs(
35        &self,
36        params: ListAllConvertPairsParams,
37    ) -> anyhow::Result<RestApiResponse<Vec<models::ListAllConvertPairsResponseInner>>>;
38    async fn query_order_quantity_precision_per_asset(
39        &self,
40        params: QueryOrderQuantityPrecisionPerAssetParams,
41    ) -> anyhow::Result<
42        RestApiResponse<Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>>,
43    >;
44}
45
46#[derive(Debug, Clone)]
47pub struct MarketDataApiClient {
48    configuration: ConfigurationRestApi,
49}
50
51impl MarketDataApiClient {
52    pub fn new(configuration: ConfigurationRestApi) -> Self {
53        Self { configuration }
54    }
55}
56
57/// Request parameters for the [`list_all_convert_pairs`] operation.
58///
59/// This struct holds all of the inputs you can pass when calling
60/// [`list_all_convert_pairs`](#method.list_all_convert_pairs).
61#[derive(Clone, Debug, Builder, Default)]
62#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
63pub struct ListAllConvertPairsParams {
64    /// User spends coin
65    ///
66    /// This field is **optional.
67    #[builder(setter(into), default)]
68    pub from_asset: Option<String>,
69    /// User receives coin
70    ///
71    /// This field is **optional.
72    #[builder(setter(into), default)]
73    pub to_asset: Option<String>,
74}
75
76impl ListAllConvertPairsParams {
77    /// Create a builder for [`list_all_convert_pairs`].
78    ///
79    #[must_use]
80    pub fn builder() -> ListAllConvertPairsParamsBuilder {
81        ListAllConvertPairsParamsBuilder::default()
82    }
83}
84/// Request parameters for the [`query_order_quantity_precision_per_asset`] operation.
85///
86/// This struct holds all of the inputs you can pass when calling
87/// [`query_order_quantity_precision_per_asset`](#method.query_order_quantity_precision_per_asset).
88#[derive(Clone, Debug, Builder, Default)]
89#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
90pub struct QueryOrderQuantityPrecisionPerAssetParams {
91    /// The value cannot be greater than 60000
92    ///
93    /// This field is **optional.
94    #[builder(setter(into), default)]
95    pub recv_window: Option<i64>,
96}
97
98impl QueryOrderQuantityPrecisionPerAssetParams {
99    /// Create a builder for [`query_order_quantity_precision_per_asset`].
100    ///
101    #[must_use]
102    pub fn builder() -> QueryOrderQuantityPrecisionPerAssetParamsBuilder {
103        QueryOrderQuantityPrecisionPerAssetParamsBuilder::default()
104    }
105}
106
107#[async_trait]
108impl MarketDataApi for MarketDataApiClient {
109    async fn list_all_convert_pairs(
110        &self,
111        params: ListAllConvertPairsParams,
112    ) -> anyhow::Result<RestApiResponse<Vec<models::ListAllConvertPairsResponseInner>>> {
113        let ListAllConvertPairsParams {
114            from_asset,
115            to_asset,
116        } = params;
117
118        let mut query_params = BTreeMap::new();
119        let body_params = BTreeMap::new();
120
121        if let Some(rw) = from_asset {
122            query_params.insert("fromAsset".to_string(), json!(rw));
123        }
124
125        if let Some(rw) = to_asset {
126            query_params.insert("toAsset".to_string(), json!(rw));
127        }
128
129        send_request::<Vec<models::ListAllConvertPairsResponseInner>>(
130            &self.configuration,
131            "/sapi/v1/convert/exchangeInfo",
132            reqwest::Method::GET,
133            query_params,
134            body_params,
135            if HAS_TIME_UNIT {
136                self.configuration.time_unit
137            } else {
138                None
139            },
140            false,
141        )
142        .await
143    }
144
145    async fn query_order_quantity_precision_per_asset(
146        &self,
147        params: QueryOrderQuantityPrecisionPerAssetParams,
148    ) -> anyhow::Result<
149        RestApiResponse<Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>>,
150    > {
151        let QueryOrderQuantityPrecisionPerAssetParams { recv_window } = params;
152
153        let mut query_params = BTreeMap::new();
154        let body_params = BTreeMap::new();
155
156        if let Some(rw) = recv_window {
157            query_params.insert("recvWindow".to_string(), json!(rw));
158        }
159
160        send_request::<Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>>(
161            &self.configuration,
162            "/sapi/v1/convert/assetInfo",
163            reqwest::Method::GET,
164            query_params,
165            body_params,
166            if HAS_TIME_UNIT {
167                self.configuration.time_unit
168            } else {
169                None
170            },
171            true,
172        )
173        .await
174    }
175}
176
177#[cfg(all(test, feature = "convert"))]
178mod tests {
179    use super::*;
180    use crate::TOKIO_SHARED_RT;
181    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
182    use async_trait::async_trait;
183    use std::collections::HashMap;
184
185    struct DummyRestApiResponse<T> {
186        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
187        status: u16,
188        headers: HashMap<String, String>,
189        rate_limits: Option<Vec<RestApiRateLimit>>,
190    }
191
192    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
193        fn from(dummy: DummyRestApiResponse<T>) -> Self {
194            Self {
195                data_fn: dummy.inner,
196                status: dummy.status,
197                headers: dummy.headers,
198                rate_limits: dummy.rate_limits,
199            }
200        }
201    }
202
203    struct MockMarketDataApiClient {
204        force_error: bool,
205    }
206
207    #[async_trait]
208    impl MarketDataApi for MockMarketDataApiClient {
209        async fn list_all_convert_pairs(
210            &self,
211            _params: ListAllConvertPairsParams,
212        ) -> anyhow::Result<RestApiResponse<Vec<models::ListAllConvertPairsResponseInner>>>
213        {
214            if self.force_error {
215                return Err(ConnectorError::ConnectorClientError {
216                    msg: "ResponseError".to_string(),
217                    code: None,
218                }
219                .into());
220            }
221
222            let resp_json: Value = serde_json::from_str(r#"[{"fromAsset":"BTC","toAsset":"USDT","fromAssetMinAmount":"0.0004","fromAssetMaxAmount":"50","toAssetMinAmount":"20","toAssetMaxAmount":"9E+24"}]"#).unwrap();
223            let dummy_response: Vec<models::ListAllConvertPairsResponseInner> =
224                serde_json::from_value(resp_json.clone())
225                    .expect("should parse into Vec<models::ListAllConvertPairsResponseInner>");
226
227            let dummy = DummyRestApiResponse {
228                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
229                status: 200,
230                headers: HashMap::new(),
231                rate_limits: None,
232            };
233
234            Ok(dummy.into())
235        }
236
237        async fn query_order_quantity_precision_per_asset(
238            &self,
239            _params: QueryOrderQuantityPrecisionPerAssetParams,
240        ) -> anyhow::Result<
241            RestApiResponse<Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>>,
242        > {
243            if self.force_error {
244                return Err(ConnectorError::ConnectorClientError {
245                    msg: "ResponseError".to_string(),
246                    code: None,
247                }
248                .into());
249            }
250
251            let resp_json: Value = serde_json::from_str(
252                r#"[{"asset":"BTC","fraction":8},{"asset":"SHIB","fraction":2}]"#,
253            )
254            .unwrap();
255            let dummy_response : Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>");
256
257            let dummy = DummyRestApiResponse {
258                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
259                status: 200,
260                headers: HashMap::new(),
261                rate_limits: None,
262            };
263
264            Ok(dummy.into())
265        }
266    }
267
268    #[test]
269    fn list_all_convert_pairs_required_params_success() {
270        TOKIO_SHARED_RT.block_on(async {
271            let client = MockMarketDataApiClient { force_error: false };
272
273            let params = ListAllConvertPairsParams::builder().build().unwrap();
274
275            let resp_json: Value = serde_json::from_str(r#"[{"fromAsset":"BTC","toAsset":"USDT","fromAssetMinAmount":"0.0004","fromAssetMaxAmount":"50","toAssetMinAmount":"20","toAssetMaxAmount":"9E+24"}]"#).unwrap();
276            let expected_response : Vec<models::ListAllConvertPairsResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::ListAllConvertPairsResponseInner>");
277
278            let resp = client.list_all_convert_pairs(params).await.expect("Expected a response");
279            let data_future = resp.data();
280            let actual_response = data_future.await.unwrap();
281            assert_eq!(actual_response, expected_response);
282        });
283    }
284
285    #[test]
286    fn list_all_convert_pairs_optional_params_success() {
287        TOKIO_SHARED_RT.block_on(async {
288            let client = MockMarketDataApiClient { force_error: false };
289
290            let params = ListAllConvertPairsParams::builder().from_asset("from_asset_example".to_string()).to_asset("to_asset_example".to_string()).build().unwrap();
291
292            let resp_json: Value = serde_json::from_str(r#"[{"fromAsset":"BTC","toAsset":"USDT","fromAssetMinAmount":"0.0004","fromAssetMaxAmount":"50","toAssetMinAmount":"20","toAssetMaxAmount":"9E+24"}]"#).unwrap();
293            let expected_response : Vec<models::ListAllConvertPairsResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::ListAllConvertPairsResponseInner>");
294
295            let resp = client.list_all_convert_pairs(params).await.expect("Expected a response");
296            let data_future = resp.data();
297            let actual_response = data_future.await.unwrap();
298            assert_eq!(actual_response, expected_response);
299        });
300    }
301
302    #[test]
303    fn list_all_convert_pairs_response_error() {
304        TOKIO_SHARED_RT.block_on(async {
305            let client = MockMarketDataApiClient { force_error: true };
306
307            let params = ListAllConvertPairsParams::builder().build().unwrap();
308
309            match client.list_all_convert_pairs(params).await {
310                Ok(_) => panic!("Expected an error"),
311                Err(err) => {
312                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
313                }
314            }
315        });
316    }
317
318    #[test]
319    fn query_order_quantity_precision_per_asset_required_params_success() {
320        TOKIO_SHARED_RT.block_on(async {
321            let client = MockMarketDataApiClient { force_error: false };
322
323            let params = QueryOrderQuantityPrecisionPerAssetParams::builder().build().unwrap();
324
325            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","fraction":8},{"asset":"SHIB","fraction":2}]"#).unwrap();
326            let expected_response : Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>");
327
328            let resp = client.query_order_quantity_precision_per_asset(params).await.expect("Expected a response");
329            let data_future = resp.data();
330            let actual_response = data_future.await.unwrap();
331            assert_eq!(actual_response, expected_response);
332        });
333    }
334
335    #[test]
336    fn query_order_quantity_precision_per_asset_optional_params_success() {
337        TOKIO_SHARED_RT.block_on(async {
338            let client = MockMarketDataApiClient { force_error: false };
339
340            let params = QueryOrderQuantityPrecisionPerAssetParams::builder().recv_window(5000).build().unwrap();
341
342            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","fraction":8},{"asset":"SHIB","fraction":2}]"#).unwrap();
343            let expected_response : Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryOrderQuantityPrecisionPerAssetResponseInner>");
344
345            let resp = client.query_order_quantity_precision_per_asset(params).await.expect("Expected a response");
346            let data_future = resp.data();
347            let actual_response = data_future.await.unwrap();
348            assert_eq!(actual_response, expected_response);
349        });
350    }
351
352    #[test]
353    fn query_order_quantity_precision_per_asset_response_error() {
354        TOKIO_SHARED_RT.block_on(async {
355            let client = MockMarketDataApiClient { force_error: true };
356
357            let params = QueryOrderQuantityPrecisionPerAssetParams::builder()
358                .build()
359                .unwrap();
360
361            match client
362                .query_order_quantity_precision_per_asset(params)
363                .await
364            {
365                Ok(_) => panic!("Expected an error"),
366                Err(err) => {
367                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
368                }
369            }
370        });
371    }
372}