1use derive_builder::Builder;
2use reqwest::{Client, ClientBuilder};
3use std::collections::HashMap;
4use std::fmt;
5use std::sync::Arc;
6use tokio_tungstenite::Connector;
7
8use super::models::{ConfigBuildError, TimeUnit, WebsocketMode};
9use super::utils::{SignatureGenerator, build_client};
10
11#[derive(Clone)]
12pub struct AgentConnector(pub Connector);
13
14impl fmt::Debug for AgentConnector {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(f, "Connector(…)")
17 }
18}
19
20pub type WebsocketHandshakeResult = Result<
29 (
30 tokio_tungstenite::WebSocketStream<
31 tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
32 >,
33 tokio_tungstenite::tungstenite::handshake::client::Response,
34 ),
35 tokio_tungstenite::tungstenite::Error,
36>;
37
38pub type WebsocketHandshakeFn = Arc<
72 dyn Fn(
73 tokio_tungstenite::tungstenite::handshake::client::Request,
74 Option<tokio_tungstenite::tungstenite::protocol::WebSocketConfig>,
75 bool,
76 Option<Connector>,
77 ) -> std::pin::Pin<Box<dyn Future<Output = WebsocketHandshakeResult> + Send>>
78 + Send
79 + Sync,
80>;
81
82#[derive(Clone)]
83pub struct HttpAgent(pub Arc<dyn Fn(ClientBuilder) -> ClientBuilder + Send + Sync>);
84
85impl fmt::Debug for HttpAgent {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "HttpAgent(<custom agent fn>)")
88 }
89}
90
91#[derive(Clone)]
92pub struct ProxyAuth {
93 pub username: String,
94 pub password: String,
95}
96
97#[derive(Debug, Clone)]
98pub struct ProxyConfig {
99 pub host: String,
100 pub port: u16,
101 pub protocol: Option<String>,
102 pub auth: Option<ProxyAuth>,
103}
104
105impl fmt::Debug for ProxyAuth {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 f.debug_struct("ProxyAuth")
108 .field("username", &self.username)
109 .field("password", &"[REDACTED]")
110 .finish()
111 }
112}
113
114#[derive(Clone)]
115pub enum PrivateKey {
116 File(String),
117 Raw(Vec<u8>),
118}
119
120impl fmt::Debug for PrivateKey {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 PrivateKey::File(_) => write!(f, "PrivateKey::File([REDACTED])"),
124 PrivateKey::Raw(_) => write!(f, "PrivateKey::Raw([REDACTED])"),
125 }
126 }
127}
128
129#[derive(Clone, Builder)]
130#[builder(
131 pattern = "owned",
132 build_fn(name = "try_build", error = "ConfigBuildError")
133)]
134pub struct ConfigurationRestApi {
135 #[builder(setter(into, strip_option), default)]
136 pub api_key: Option<String>,
137
138 #[builder(setter(into, strip_option), default)]
139 pub api_secret: Option<String>,
140
141 #[builder(setter(into, strip_option), default)]
142 pub base_path: Option<String>,
143
144 #[builder(default = "1000")]
145 pub timeout: u64,
146
147 #[builder(default = "true")]
148 pub keep_alive: bool,
149
150 #[builder(default = "true")]
151 pub compression: bool,
152
153 #[builder(default = "3")]
154 pub retries: u32,
155
156 #[builder(default = "1000")]
157 pub backoff: u64,
158
159 #[builder(setter(strip_option), default)]
160 pub proxy: Option<ProxyConfig>,
161
162 #[builder(setter(strip_option, into), default)]
163 pub custom_headers: Option<HashMap<String, String>>,
164
165 #[builder(setter(strip_option), default)]
166 pub agent: Option<HttpAgent>,
167
168 #[builder(setter(strip_option), default)]
169 pub private_key: Option<PrivateKey>,
170
171 #[builder(setter(strip_option), default)]
172 pub private_key_passphrase: Option<String>,
173
174 #[builder(setter(strip_option), default)]
175 pub time_unit: Option<TimeUnit>,
176
177 #[builder(setter(skip))]
178 pub(crate) client: Client,
179
180 #[builder(setter(skip))]
181 pub(crate) user_agent: String,
182
183 #[builder(setter(skip))]
184 pub(crate) signature_gen: SignatureGenerator,
185}
186
187impl fmt::Debug for ConfigurationRestApi {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 f.debug_struct("ConfigurationRestApi")
190 .field("api_key", &self.api_key.as_ref().map(|_| "[REDACTED]"))
191 .field(
192 "api_secret",
193 &self.api_secret.as_ref().map(|_| "[REDACTED]"),
194 )
195 .field("base_path", &self.base_path)
196 .field("timeout", &self.timeout)
197 .field("keep_alive", &self.keep_alive)
198 .field("compression", &self.compression)
199 .field("retries", &self.retries)
200 .field("backoff", &self.backoff)
201 .field("proxy", &self.proxy)
202 .field("custom_headers", &self.custom_headers)
203 .field("agent", &self.agent)
204 .field(
205 "private_key",
206 &self.private_key.as_ref().map(|_| "[REDACTED]"),
207 )
208 .field(
209 "private_key_passphrase",
210 &self.private_key_passphrase.as_ref().map(|_| "[REDACTED]"),
211 )
212 .field("time_unit", &self.time_unit)
213 .field("client", &"<reqwest::Client>")
214 .field("user_agent", &self.user_agent)
215 .field("signature_gen", &self.signature_gen)
216 .finish()
217 }
218}
219
220impl ConfigurationRestApi {
221 #[must_use]
222 pub fn builder() -> ConfigurationRestApiBuilder {
223 ConfigurationRestApiBuilder::default()
224 }
225}
226
227impl ConfigurationRestApiBuilder {
228 pub fn build(self) -> Result<ConfigurationRestApi, ConfigBuildError> {
238 let mut cfg = self.try_build()?;
239 cfg.client = build_client(
240 cfg.timeout,
241 cfg.keep_alive,
242 cfg.proxy.as_ref(),
243 cfg.agent.clone(),
244 );
245 cfg.signature_gen = SignatureGenerator::new(
246 cfg.api_secret.clone(),
247 cfg.private_key.clone(),
248 cfg.private_key_passphrase.clone(),
249 );
250
251 Ok(cfg)
252 }
253}
254
255#[derive(Clone, Builder)]
256#[builder(
257 pattern = "owned",
258 build_fn(name = "try_build", error = "ConfigBuildError")
259)]
260pub struct ConfigurationWebsocketApi {
261 #[builder(setter(into, strip_option), default)]
262 pub api_key: Option<String>,
263
264 #[builder(setter(into, strip_option), default)]
265 pub api_secret: Option<String>,
266
267 #[builder(setter(into, strip_option), default)]
268 pub ws_url: Option<String>,
269
270 #[builder(default = "5000")]
271 pub timeout: u64,
272
273 #[builder(default = "5000")]
274 pub reconnect_delay: u64,
275
276 #[builder(default = "WebsocketMode::Single")]
277 pub mode: WebsocketMode,
278
279 #[builder(setter(strip_option), default)]
280 pub agent: Option<AgentConnector>,
281
282 #[builder(setter(strip_option), default)]
285 pub handshake: Option<WebsocketHandshakeFn>,
286
287 #[builder(setter(strip_option), default)]
288 pub private_key: Option<PrivateKey>,
289
290 #[builder(setter(strip_option), default)]
291 pub private_key_passphrase: Option<String>,
292
293 #[builder(setter(strip_option), default)]
294 pub time_unit: Option<TimeUnit>,
295
296 #[builder(default = "true")]
297 pub auto_session_relogon: bool,
298
299 #[builder(setter(skip))]
300 pub(crate) user_agent: String,
301
302 #[builder(setter(skip))]
303 pub(crate) signature_gen: SignatureGenerator,
304}
305
306impl fmt::Debug for ConfigurationWebsocketApi {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 f.debug_struct("ConfigurationWebsocketApi")
309 .field("api_key", &self.api_key.as_ref().map(|_| "[REDACTED]"))
310 .field(
311 "api_secret",
312 &self.api_secret.as_ref().map(|_| "[REDACTED]"),
313 )
314 .field("ws_url", &self.ws_url)
315 .field("timeout", &self.timeout)
316 .field("reconnect_delay", &self.reconnect_delay)
317 .field("mode", &self.mode)
318 .field("agent", &self.agent)
319 .field(
320 "handshake",
321 &self.handshake.as_ref().map(|_| "<custom handshake fn>"),
322 )
323 .field(
324 "private_key",
325 &self.private_key.as_ref().map(|_| "[REDACTED]"),
326 )
327 .field(
328 "private_key_passphrase",
329 &self.private_key_passphrase.as_ref().map(|_| "[REDACTED]"),
330 )
331 .field("time_unit", &self.time_unit)
332 .field("auto_session_relogon", &self.auto_session_relogon)
333 .field("user_agent", &self.user_agent)
334 .field("signature_gen", &self.signature_gen)
335 .finish()
336 }
337}
338
339impl ConfigurationWebsocketApi {
340 #[must_use]
350 pub fn builder() -> ConfigurationWebsocketApiBuilder {
351 ConfigurationWebsocketApiBuilder::default()
352 }
353}
354
355impl ConfigurationWebsocketApiBuilder {
356 pub fn build(self) -> Result<ConfigurationWebsocketApi, ConfigBuildError> {
372 let mut cfg = self.try_build()?;
373 cfg.signature_gen = SignatureGenerator::new(
374 cfg.api_secret.clone(),
375 cfg.private_key.clone(),
376 cfg.private_key_passphrase.clone(),
377 );
378
379 Ok(cfg)
380 }
381}
382
383#[derive(Clone, Builder)]
384#[builder(pattern = "owned", build_fn(error = "ConfigBuildError"))]
385pub struct ConfigurationWebsocketStreams {
386 #[builder(setter(into, strip_option), default)]
387 pub ws_url: Option<String>,
388
389 #[builder(default = "5000")]
390 pub reconnect_delay: u64,
391
392 #[builder(default = "WebsocketMode::Single")]
393 pub mode: WebsocketMode,
394
395 #[builder(setter(strip_option), default)]
396 pub agent: Option<AgentConnector>,
397
398 #[builder(setter(strip_option), default)]
401 pub handshake: Option<WebsocketHandshakeFn>,
402
403 #[builder(setter(strip_option), default)]
404 pub time_unit: Option<TimeUnit>,
405
406 #[builder(setter(skip))]
407 pub(crate) user_agent: String,
408}
409
410impl fmt::Debug for ConfigurationWebsocketStreams {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 f.debug_struct("ConfigurationWebsocketStreams")
413 .field("ws_url", &self.ws_url)
414 .field("reconnect_delay", &self.reconnect_delay)
415 .field("mode", &self.mode)
416 .field("agent", &self.agent)
417 .field(
418 "handshake",
419 &self.handshake.as_ref().map(|_| "<custom handshake fn>"),
420 )
421 .field("time_unit", &self.time_unit)
422 .field("user_agent", &self.user_agent)
423 .finish()
424 }
425}
426
427impl ConfigurationWebsocketStreams {
428 #[must_use]
429 pub fn builder() -> ConfigurationWebsocketStreamsBuilder {
435 ConfigurationWebsocketStreamsBuilder::default()
436 }
437}