crypto_botters/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use generic_api_client::{http::{self, *}, websocket::*};
4use serde::Serialize;
5use traits::*;
6
7pub use generic_api_client;
8pub use exchanges::*;
9
10mod exchanges;
11pub mod traits;
12
13// very long type, make it a macro
14macro_rules! request_return_type {
15    ($lt:lifetime, $Response:ty, $Options:ty,  $Body:ty) => {
16        Result<
17            <<$Options as HttpOption<$lt, $Response, $Body>>::RequestHandler as RequestHandler<$Body>>::Successful,
18            RequestError<
19                <<$Options as HttpOption<$lt, $Response, $Body>>::RequestHandler as RequestHandler<$Body>>::BuildError,
20                <<$Options as HttpOption<$lt, $Response, $Body>>::RequestHandler as RequestHandler<$Body>>::Unsuccessful,
21            >,
22        >
23    };
24}
25
26#[derive(Default, Debug, Clone)]
27pub struct Client {
28    client: http::Client,
29    #[cfg(feature = "binance")]
30    binance: binance::BinanceOptions,
31    #[cfg(feature = "bitflyer")]
32    bitflyer: bitflyer::BitFlyerOptions,
33    #[cfg(feature = "bybit")]
34    bybit: bybit::BybitOptions,
35    #[cfg(feature = "coincheck")]
36    coincheck: coincheck::CoincheckOptions,
37}
38
39impl Client {
40    /// Creates a new [Client].
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    /// Update the default options for this [Client]
46    #[inline(always)]
47    pub fn update_default_option<O>(&mut self, option: O)
48    where
49        O: HandlerOption,
50        Self: GetOptions<O::Options>,
51    {
52        self.default_options_mut().update(option);
53    }
54
55    #[inline]
56    fn merged_options<O>(&self, options: impl IntoIterator<Item=O>) -> O::Options
57    where
58        O: HandlerOption,
59        Self:GetOptions<O::Options>,
60    {
61        let mut default_options = self.default_options().clone();
62        for option in options {
63            default_options.update(option);
64        }
65        default_options
66    }
67
68    /// see [http::Client::request()]
69    #[inline(always)]
70    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>)
71        -> request_return_type!('a, R, O, B)
72    where
73        O: HttpOption<'a, R, B>,
74        O::RequestHandler: RequestHandler<B>,
75        Self: GetOptions<O::Options>,
76        Q: Serialize + ?Sized,
77    {
78        self.client.request(method, url, query, body, &O::request_handler(self.merged_options(options))).await
79    }
80
81    /// see [http::Client::get()]
82    #[inline(always)]
83    pub async fn get<'a, R, O, Q>(&self, url: &str, query: Option<&Q>, options: impl IntoIterator<Item=O>) -> request_return_type!('a, R, O, ())
84    where
85        O: HttpOption<'a, R, ()>,
86        O::RequestHandler: RequestHandler<()>,
87        Self: GetOptions<O::Options>,
88        Q: Serialize + ?Sized,
89    {
90        self.client.get(url, query, &O::request_handler(self.merged_options(options))).await
91    }
92
93    /// see [http::Client::get_no_query()]
94    #[inline(always)]
95    pub async fn get_no_query<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item=O>) -> request_return_type!('a, R, O, ())
96    where
97        O: HttpOption<'a, R, ()>,
98        O::RequestHandler: RequestHandler<()>,
99        Self: GetOptions<O::Options>,
100    {
101        self.client.get_no_query(url, &O::request_handler(self.merged_options(options))).await
102    }
103
104    /// see [http::Client::post()]
105    #[inline(always)]
106    pub async fn post<'a, R, O, B>(&self, url: &str, body: Option<B>, options: impl IntoIterator<Item=O>)
107        -> request_return_type!('a, R, O, B)
108    where
109        O: HttpOption<'a, R, B>,
110        O::RequestHandler: RequestHandler<B>,
111        Self: GetOptions<O::Options>,
112    {
113        self.client.post(url, body, &O::request_handler(self.merged_options(options))).await
114    }
115
116    /// see [http::Client::post_no_body()]
117    #[inline(always)]
118    pub async fn post_no_body<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item=O>)
119        -> request_return_type!('a, R, O, ())
120    where
121        O: HttpOption<'a, R, ()>,
122        O::RequestHandler: RequestHandler<()>,
123        Self: GetOptions<O::Options>,
124    {
125        self.client.post_no_body(url, &O::request_handler(self.merged_options(options))).await
126    }
127
128    /// see [http::Client::put()]
129    #[inline(always)]
130    pub async fn put<'a, R, O, B>(&self, url: &str, body: Option<B>, options: impl IntoIterator<Item=O>)
131        -> request_return_type!('a, R, O, B)
132    where
133        O: HttpOption<'a, R, B>,
134        O::RequestHandler: RequestHandler<B>,
135        Self: GetOptions<O::Options>,
136    {
137        self.client.put(url, body, &O::request_handler(self.merged_options(options))).await
138    }
139
140    /// see [http::Client::put_no_body()]
141    #[inline(always)]
142    pub async fn put_no_body<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item=O>)
143        -> request_return_type!('a, R, O, ())
144    where
145        O: HttpOption<'a, R, ()>,
146        O::RequestHandler: RequestHandler<()>,
147        Self: GetOptions<O::Options>,
148    {
149        self.client.put_no_body(url, &O::request_handler(self.merged_options(options))).await
150    }
151
152    /// see [http::Client::delete()]
153    #[inline(always)]
154    pub async fn delete<'a, R, O, Q>(&self, url: &str, query: Option<&Q>, options: impl IntoIterator<Item=O>) -> request_return_type!('a, R, O, ())
155    where
156        O: HttpOption<'a, R, ()>,
157        O::RequestHandler: RequestHandler<()>,
158        Self: GetOptions<O::Options>,
159        Q: Serialize + ?Sized,
160    {
161        self.client.delete(url, query, &O::request_handler(self.merged_options(options))).await
162    }
163
164    /// see [http::Client::delete_no_query()]
165    #[inline(always)]
166    pub async fn delete_no_query<'a, R, O>(&self, url: &str, options: impl IntoIterator<Item=O>) -> request_return_type!('a, R, O, ())
167    where
168        O: HttpOption<'a, R, ()>,
169        O::RequestHandler: RequestHandler<()>,
170        Self: GetOptions<O::Options>,
171    {
172        self.client.delete_no_query(url, &O::request_handler(self.merged_options(options))).await
173    }
174
175    #[inline(always)]
176    pub async fn websocket<O, H>(&self, url: &str, handler: H, options: impl IntoIterator<Item=O>) -> Result<WebSocketConnection<O::WebSocketHandler>, TungsteniteError>
177    where
178        O: WebSocketOption<H>,
179        O::WebSocketHandler: WebSocketHandler,
180        Self: GetOptions<O::Options>,
181    {
182        WebSocketConnection::new(url, O::websocket_handler(handler, self.merged_options(options))).await
183    }
184}
185
186pub trait GetOptions<O: HandlerOptions> {
187    fn default_options(&self) -> &O;
188    fn default_options_mut(&mut self) -> &mut O;
189}
190
191#[cfg(feature = "binance")]
192#[cfg_attr(docsrs, doc(cfg(feature = "binance")))]
193impl GetOptions<binance::BinanceOptions> for Client {
194    #[inline(always)]
195    fn default_options(&self) -> &binance::BinanceOptions {
196        &self.binance
197    }
198
199    #[inline(always)]
200    fn default_options_mut(&mut self) -> &mut binance::BinanceOptions {
201        &mut self.binance
202    }
203}
204
205#[cfg(feature = "bitflyer")]
206#[cfg_attr(docsrs, doc(cfg(feature = "bitflyer")))]
207impl GetOptions<bitflyer::BitFlyerOptions> for Client {
208    #[inline(always)]
209    fn default_options(&self) -> &bitflyer::BitFlyerOptions {
210        &self.bitflyer
211    }
212
213    #[inline(always)]
214    fn default_options_mut(&mut self) -> &mut bitflyer::BitFlyerOptions {
215        &mut self.bitflyer
216    }
217}
218
219#[cfg(feature = "bybit")]
220#[cfg_attr(docsrs, doc(cfg(feature = "bybit")))]
221impl GetOptions<bybit::BybitOptions> for Client {
222    #[inline(always)]
223    fn default_options(&self) -> &bybit::BybitOptions {
224        &self.bybit
225    }
226
227    #[inline(always)]
228    fn default_options_mut(&mut self) -> &mut bybit::BybitOptions {
229        &mut self.bybit
230    }
231}
232
233#[cfg(feature = "coincheck")]
234#[cfg_attr(docsrs, doc(cfg(feature = "coincheck")))]
235impl GetOptions<coincheck::CoincheckOptions> for Client {
236    #[inline(always)]
237    fn default_options(&self) -> &coincheck::CoincheckOptions {
238        &self.coincheck
239    }
240
241    #[inline(always)]
242    fn default_options_mut(&mut self) -> &mut coincheck::CoincheckOptions {
243        &mut self.coincheck
244    }
245}