Skip to main content

binance_sdk/common/
config.rs

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
20#[derive(Clone)]
21pub struct HttpAgent(pub Arc<dyn Fn(ClientBuilder) -> ClientBuilder + Send + Sync>);
22
23impl fmt::Debug for HttpAgent {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        write!(f, "HttpAgent(<custom agent fn>)")
26    }
27}
28
29#[derive(Clone)]
30pub struct ProxyAuth {
31    pub username: String,
32    pub password: String,
33}
34
35#[derive(Debug, Clone)]
36pub struct ProxyConfig {
37    pub host: String,
38    pub port: u16,
39    pub protocol: Option<String>,
40    pub auth: Option<ProxyAuth>,
41}
42
43impl fmt::Debug for ProxyAuth {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        f.debug_struct("ProxyAuth")
46            .field("username", &self.username)
47            .field("password", &"[REDACTED]")
48            .finish()
49    }
50}
51
52#[derive(Clone)]
53pub enum PrivateKey {
54    File(String),
55    Raw(Vec<u8>),
56}
57
58impl fmt::Debug for PrivateKey {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            PrivateKey::File(_) => write!(f, "PrivateKey::File([REDACTED])"),
62            PrivateKey::Raw(_) => write!(f, "PrivateKey::Raw([REDACTED])"),
63        }
64    }
65}
66
67#[derive(Clone, Builder)]
68#[builder(
69    pattern = "owned",
70    build_fn(name = "try_build", error = "ConfigBuildError")
71)]
72pub struct ConfigurationRestApi {
73    #[builder(setter(into, strip_option), default)]
74    pub api_key: Option<String>,
75
76    #[builder(setter(into, strip_option), default)]
77    pub api_secret: Option<String>,
78
79    #[builder(setter(into, strip_option), default)]
80    pub base_path: Option<String>,
81
82    #[builder(default = "1000")]
83    pub timeout: u64,
84
85    #[builder(default = "true")]
86    pub keep_alive: bool,
87
88    #[builder(default = "true")]
89    pub compression: bool,
90
91    #[builder(default = "3")]
92    pub retries: u32,
93
94    #[builder(default = "1000")]
95    pub backoff: u64,
96
97    #[builder(setter(strip_option), default)]
98    pub proxy: Option<ProxyConfig>,
99
100    #[builder(setter(strip_option, into), default)]
101    pub custom_headers: Option<HashMap<String, String>>,
102
103    #[builder(setter(strip_option), default)]
104    pub agent: Option<HttpAgent>,
105
106    #[builder(setter(strip_option), default)]
107    pub private_key: Option<PrivateKey>,
108
109    #[builder(setter(strip_option), default)]
110    pub private_key_passphrase: Option<String>,
111
112    #[builder(setter(strip_option), default)]
113    pub time_unit: Option<TimeUnit>,
114
115    #[builder(setter(skip))]
116    pub(crate) client: Client,
117
118    #[builder(setter(skip))]
119    pub(crate) user_agent: String,
120
121    #[builder(setter(skip))]
122    pub(crate) signature_gen: SignatureGenerator,
123}
124
125impl fmt::Debug for ConfigurationRestApi {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.debug_struct("ConfigurationRestApi")
128            .field("api_key", &self.api_key.as_ref().map(|_| "[REDACTED]"))
129            .field(
130                "api_secret",
131                &self.api_secret.as_ref().map(|_| "[REDACTED]"),
132            )
133            .field("base_path", &self.base_path)
134            .field("timeout", &self.timeout)
135            .field("keep_alive", &self.keep_alive)
136            .field("compression", &self.compression)
137            .field("retries", &self.retries)
138            .field("backoff", &self.backoff)
139            .field("proxy", &self.proxy)
140            .field("custom_headers", &self.custom_headers)
141            .field("agent", &self.agent)
142            .field(
143                "private_key",
144                &self.private_key.as_ref().map(|_| "[REDACTED]"),
145            )
146            .field(
147                "private_key_passphrase",
148                &self.private_key_passphrase.as_ref().map(|_| "[REDACTED]"),
149            )
150            .field("time_unit", &self.time_unit)
151            .field("client", &"<reqwest::Client>")
152            .field("user_agent", &self.user_agent)
153            .field("signature_gen", &self.signature_gen)
154            .finish()
155    }
156}
157
158impl ConfigurationRestApi {
159    #[must_use]
160    pub fn builder() -> ConfigurationRestApiBuilder {
161        ConfigurationRestApiBuilder::default()
162    }
163}
164
165impl ConfigurationRestApiBuilder {
166    /// Builds a `ConfigurationRestApi` instance with configured HTTP client and signature generator.
167    ///
168    /// # Returns
169    ///
170    /// A `Result` containing the fully configured `ConfigurationRestApi` or a `ConfigBuildError` if configuration fails.
171    ///
172    /// # Errors
173    ///
174    /// Returns a `ConfigBuildError` if the initial configuration build fails or if client setup encounters issues.
175    pub fn build(self) -> Result<ConfigurationRestApi, ConfigBuildError> {
176        let mut cfg = self.try_build()?;
177        cfg.client = build_client(
178            cfg.timeout,
179            cfg.keep_alive,
180            cfg.proxy.as_ref(),
181            cfg.agent.clone(),
182        );
183        cfg.signature_gen = SignatureGenerator::new(
184            cfg.api_secret.clone(),
185            cfg.private_key.clone(),
186            cfg.private_key_passphrase.clone(),
187        );
188
189        Ok(cfg)
190    }
191}
192
193#[derive(Clone, Builder)]
194#[builder(
195    pattern = "owned",
196    build_fn(name = "try_build", error = "ConfigBuildError")
197)]
198pub struct ConfigurationWebsocketApi {
199    #[builder(setter(into, strip_option), default)]
200    pub api_key: Option<String>,
201
202    #[builder(setter(into, strip_option), default)]
203    pub api_secret: Option<String>,
204
205    #[builder(setter(into, strip_option), default)]
206    pub ws_url: Option<String>,
207
208    #[builder(default = "5000")]
209    pub timeout: u64,
210
211    #[builder(default = "5000")]
212    pub reconnect_delay: u64,
213
214    #[builder(default = "WebsocketMode::Single")]
215    pub mode: WebsocketMode,
216
217    #[builder(setter(strip_option), default)]
218    pub agent: Option<AgentConnector>,
219
220    #[builder(setter(strip_option), default)]
221    pub private_key: Option<PrivateKey>,
222
223    #[builder(setter(strip_option), default)]
224    pub private_key_passphrase: Option<String>,
225
226    #[builder(setter(strip_option), default)]
227    pub time_unit: Option<TimeUnit>,
228
229    #[builder(default = "true")]
230    pub auto_session_relogon: bool,
231
232    #[builder(setter(skip))]
233    pub(crate) user_agent: String,
234
235    #[builder(setter(skip))]
236    pub(crate) signature_gen: SignatureGenerator,
237}
238
239impl fmt::Debug for ConfigurationWebsocketApi {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        f.debug_struct("ConfigurationWebsocketApi")
242            .field("api_key", &self.api_key.as_ref().map(|_| "[REDACTED]"))
243            .field(
244                "api_secret",
245                &self.api_secret.as_ref().map(|_| "[REDACTED]"),
246            )
247            .field("ws_url", &self.ws_url)
248            .field("timeout", &self.timeout)
249            .field("reconnect_delay", &self.reconnect_delay)
250            .field("mode", &self.mode)
251            .field("agent", &self.agent)
252            .field(
253                "private_key",
254                &self.private_key.as_ref().map(|_| "[REDACTED]"),
255            )
256            .field(
257                "private_key_passphrase",
258                &self.private_key_passphrase.as_ref().map(|_| "[REDACTED]"),
259            )
260            .field("time_unit", &self.time_unit)
261            .field("auto_session_relogon", &self.auto_session_relogon)
262            .field("user_agent", &self.user_agent)
263            .field("signature_gen", &self.signature_gen)
264            .finish()
265    }
266}
267
268impl ConfigurationWebsocketApi {
269    /// Creates a builder for `ConfigurationWebsocketApi` with the specified API key.
270    ///
271    /// # Arguments
272    ///
273    /// * `api_key` - The API key to be used for the WebSocket API configuration
274    ///
275    /// # Returns
276    ///
277    /// A `ConfigurationWebsocketApiBuilder` initialized with the provided API key
278    #[must_use]
279    pub fn builder() -> ConfigurationWebsocketApiBuilder {
280        ConfigurationWebsocketApiBuilder::default()
281    }
282}
283
284impl ConfigurationWebsocketApiBuilder {
285    /// Builds the `ConfigurationWebsocketApi` with a generated signature generator.
286    ///
287    /// This method attempts to build the configuration using the builder's settings
288    /// and then initializes the signature generator with the API secret, private key,
289    /// and private key passphrase.
290    ///
291    /// # Returns
292    ///
293    /// A `Result` containing the fully configured `ConfigurationWebsocketApi` or a
294    /// `ConfigBuildError` if the build process fails.
295    ///
296    /// # Errors
297    ///
298    /// Returns a `ConfigBuildError` if the initial configuration build fails or if signature generation fails.
299    ///
300    pub fn build(self) -> Result<ConfigurationWebsocketApi, ConfigBuildError> {
301        let mut cfg = self.try_build()?;
302        cfg.signature_gen = SignatureGenerator::new(
303            cfg.api_secret.clone(),
304            cfg.private_key.clone(),
305            cfg.private_key_passphrase.clone(),
306        );
307
308        Ok(cfg)
309    }
310}
311
312#[derive(Debug, Clone, Builder)]
313#[builder(pattern = "owned", build_fn(error = "ConfigBuildError"))]
314pub struct ConfigurationWebsocketStreams {
315    #[builder(setter(into, strip_option), default)]
316    pub ws_url: Option<String>,
317
318    #[builder(default = "5000")]
319    pub reconnect_delay: u64,
320
321    #[builder(default = "WebsocketMode::Single")]
322    pub mode: WebsocketMode,
323
324    #[builder(setter(strip_option), default)]
325    pub agent: Option<AgentConnector>,
326
327    #[builder(setter(strip_option), default)]
328    pub time_unit: Option<TimeUnit>,
329
330    #[builder(setter(skip))]
331    pub(crate) user_agent: String,
332}
333
334impl ConfigurationWebsocketStreams {
335    #[must_use]
336    /// Creates a builder for `ConfigurationWebsocketStreams` with default settings.
337    ///
338    /// # Returns
339    ///
340    /// A `ConfigurationWebsocketStreamsBuilder` initialized with default values
341    pub fn builder() -> ConfigurationWebsocketStreamsBuilder {
342        ConfigurationWebsocketStreamsBuilder::default()
343    }
344}