1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![feature(default_field_values)]
3#![feature(duration_constructors)]
4#![feature(try_blocks)]
5pub extern crate v_exchanges_api_generics as generics;
6use std::sync::Arc;
7
8pub use exchanges::*;
9use generics::UrlError;
10use serde::Serialize;
11use tokio::sync::Semaphore;
12use traits::*;
13use v_exchanges_api_generics::{
14 http::{self, *},
15 ws::*,
16};
17
18mod exchanges;
19pub mod traits;
20
21macro_rules! request_ret {
23 ($lt:lifetime, $Response:ty, $Options:ty, $Body:ty) => {
24 Result<
25 <<$Options as HttpOption<$lt, $Response, $Body>>::RequestHandler as RequestHandler<$Body>>::Successful,
26 RequestError,
27 >
28 };
29}
30
31pub const DEFAULT_MAX_SIMULTANEOUS_REQUESTS: usize = 100;
33
34#[derive(Clone, Debug)]
35pub struct Client {
36 pub client: http::Client,
37 pub request_semaphore: Arc<Semaphore>,
40 #[cfg(feature = "binance")]
41 binance: binance::BinanceOptions,
42 #[cfg(feature = "bitflyer")]
43 bitflyer: bitflyer::BitFlyerOptions,
44 #[cfg(feature = "bybit")]
45 bybit: bybit::BybitOptions,
46 #[cfg(feature = "coincheck")]
47 coincheck: coincheck::CoincheckOptions,
48 #[cfg(feature = "kucoin")]
49 kucoin: kucoin::KucoinOptions,
50 #[cfg(feature = "mexc")]
51 mexc: mexc::MexcOptions,
52}
53
54impl Default for Client {
55 fn default() -> Self {
56 Self {
57 client: http::Client::default(),
58 request_semaphore: Arc::new(Semaphore::new(DEFAULT_MAX_SIMULTANEOUS_REQUESTS)),
59 #[cfg(feature = "binance")]
60 binance: binance::BinanceOptions::default(),
61 #[cfg(feature = "bitflyer")]
62 bitflyer: bitflyer::BitFlyerOptions::default(),
63 #[cfg(feature = "bybit")]
64 bybit: bybit::BybitOptions::default(),
65 #[cfg(feature = "coincheck")]
66 coincheck: coincheck::CoincheckOptions::default(),
67 #[cfg(feature = "kucoin")]
68 kucoin: kucoin::KucoinOptions::default(),
69 #[cfg(feature = "mexc")]
70 mexc: mexc::MexcOptions::default(),
71 }
72 }
73}
74
75impl Client {
76 pub fn set_max_simultaneous_requests(&mut self, max: usize) {
82 self.request_semaphore = Arc::new(Semaphore::new(max));
83 }
84
85 pub fn update_default_option<O>(&mut self, option: O)
87 where
88 O: HandlerOption,
89 Self: GetOptions<O::Options>, {
90 self.default_options_mut().update(option);
91 }
92
93 pub fn is_authenticated<O>(&self) -> bool
94 where
95 O: HandlerOption,
96 Self: GetOptions<O::Options>, {
97 self.default_options().is_authenticated()
98 }
99
100 #[inline]
101 fn merged_options<O>(&self, options: impl IntoIterator<Item = O>) -> O::Options
102 where
103 O: HandlerOption,
104 Self: GetOptions<O::Options>, {
105 let mut default_options = self.default_options().clone();
106 for option in options {
107 default_options.update(option);
108 }
109 default_options
110 }
111
112 pub async fn request<'a, R, O, Q, B>(&self, method: Method, url: &str, query: Option<&Q>, body: Option<B>, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, B)
114 where
115 O: HttpOption<'a, R, B>,
116 O::RequestHandler: RequestHandler<B>,
117 Self: GetOptions<O::Options>,
118 Q: Serialize + ?Sized + std::fmt::Debug, {
119 self.client.request(method, url, query, body, &O::request_handler(self.merged_options(options))).await
120 }
121
122 pub async fn get<'a, R, O, Q>(&self, url: &str, query: &Q, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, ())
124 where
125 O: HttpOption<'a, R, ()>,
126 O::RequestHandler: RequestHandler<()>,
127 Self: GetOptions<O::Options>,
128 Q: Serialize + ?Sized + std::fmt::Debug, {
129 self.client.get(url, query, &O::request_handler(self.merged_options(options))).await
130 }
131
132 pub async fn get_no_query<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, ())
134 where
135 O: HttpOption<'a, R, ()>,
136 O::RequestHandler: RequestHandler<()>,
137 Self: GetOptions<O::Options>, {
138 self.client.get_no_query(url, &O::request_handler(self.merged_options(options))).await
139 }
140
141 pub async fn post<'a, R, O, B>(&self, url: &str, body: B, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, B)
143 where
144 O: HttpOption<'a, R, B>,
145 O::RequestHandler: RequestHandler<B>,
146 Self: GetOptions<O::Options>, {
147 self.client.post(url, body, &O::request_handler(self.merged_options(options))).await
148 }
149
150 pub async fn post_no_body<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, ())
152 where
153 O: HttpOption<'a, R, ()>,
154 O::RequestHandler: RequestHandler<()>,
155 Self: GetOptions<O::Options>, {
156 self.client.post_no_body(url, &O::request_handler(self.merged_options(options))).await
157 }
158
159 pub async fn put<'a, R, O, B>(&self, url: &str, body: B, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, B)
161 where
162 O: HttpOption<'a, R, B>,
163 O::RequestHandler: RequestHandler<B>,
164 Self: GetOptions<O::Options>, {
165 self.client.put(url, body, &O::request_handler(self.merged_options(options))).await
166 }
167
168 pub async fn put_no_body<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, ())
170 where
171 O: HttpOption<'a, R, ()>,
172 O::RequestHandler: RequestHandler<()>,
173 Self: GetOptions<O::Options>, {
174 self.client.put_no_body(url, &O::request_handler(self.merged_options(options))).await
175 }
176
177 pub async fn delete<'a, R, O, Q>(&self, url: &str, query: &Q, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, ())
179 where
180 O: HttpOption<'a, R, ()>,
181 O::RequestHandler: RequestHandler<()>,
182 Self: GetOptions<O::Options>,
183 Q: Serialize + ?Sized + std::fmt::Debug, {
184 self.client.delete(url, query, &O::request_handler(self.merged_options(options))).await
185 }
186
187 pub async fn delete_no_query<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item = O>) -> request_ret!('a, R, O, ())
189 where
190 O: HttpOption<'a, R, ()>,
191 O::RequestHandler: RequestHandler<()>,
192 Self: GetOptions<O::Options>, {
193 self.client.delete_no_query(url, &O::request_handler(self.merged_options(options))).await
194 }
195
196 pub fn ws_connection<O>(&self, url: &str, options: impl IntoIterator<Item = O>) -> Result<WsConnection<O::WsHandler>, UrlError>
197 where
198 O: WsOption,
199 O::WsHandler: WsHandler,
200 Self: GetOptions<O::Options>, {
201 WsConnection::try_new(url, O::ws_handler(self.merged_options(options)))
202 }
203}
204
205pub trait GetOptions<O: HandlerOptions> {
206 fn default_options(&self) -> &O;
207 fn default_options_mut(&mut self) -> &mut O;
208 fn is_authenticated(&self) -> bool {
209 self.default_options().is_authenticated()
210 }
211}
212
213#[cfg(feature = "binance")]
214#[cfg_attr(docsrs, doc(cfg(feature = "binance")))]
215impl GetOptions<binance::BinanceOptions> for Client {
216 fn default_options(&self) -> &binance::BinanceOptions {
217 &self.binance
218 }
219
220 fn default_options_mut(&mut self) -> &mut binance::BinanceOptions {
221 &mut self.binance
222 }
223}
224
225#[cfg(feature = "bitflyer")]
226#[cfg_attr(docsrs, doc(cfg(feature = "bitflyer")))]
227impl GetOptions<bitflyer::BitFlyerOptions> for Client {
228 fn default_options(&self) -> &bitflyer::BitFlyerOptions {
229 &self.bitflyer
230 }
231
232 fn default_options_mut(&mut self) -> &mut bitflyer::BitFlyerOptions {
233 &mut self.bitflyer
234 }
235}
236
237#[cfg(feature = "bybit")]
238#[cfg_attr(docsrs, doc(cfg(feature = "bybit")))]
239impl GetOptions<bybit::BybitOptions> for Client {
240 fn default_options(&self) -> &bybit::BybitOptions {
241 &self.bybit
242 }
243
244 fn default_options_mut(&mut self) -> &mut bybit::BybitOptions {
245 &mut self.bybit
246 }
247}
248
249#[cfg(feature = "coincheck")]
250#[cfg_attr(docsrs, doc(cfg(feature = "coincheck")))]
251impl GetOptions<coincheck::CoincheckOptions> for Client {
252 fn default_options(&self) -> &coincheck::CoincheckOptions {
253 &self.coincheck
254 }
255
256 fn default_options_mut(&mut self) -> &mut coincheck::CoincheckOptions {
257 &mut self.coincheck
258 }
259}
260#[cfg(feature = "kucoin")]
261#[cfg_attr(docsrs, doc(cfg(feature = "kucoin")))]
262impl GetOptions<kucoin::KucoinOptions> for Client {
263 fn default_options(&self) -> &kucoin::KucoinOptions {
264 &self.kucoin
265 }
266
267 fn default_options_mut(&mut self) -> &mut kucoin::KucoinOptions {
268 &mut self.kucoin
269 }
270}
271#[cfg(feature = "mexc")]
272#[cfg_attr(docsrs, doc(cfg(feature = "mexc")))]
273impl GetOptions<mexc::MexcOptions> for Client {
274 fn default_options(&self) -> &mexc::MexcOptions {
275 &self.mexc
276 }
277
278 fn default_options_mut(&mut self) -> &mut mexc::MexcOptions {
279 &mut self.mexc
280 }
281}