rusty_feeder/exchange/bithumb/data/
subscription.rs1use serde::{Deserialize, Serialize};
7use smartstring::alias::String;
8use std::fmt;
9use std::time::Duration;
10use uuid::Uuid;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum SubscriptionType {
16 #[serde(rename = "trade")]
18 Trade,
19
20 #[serde(rename = "orderbook")]
22 Orderbook,
23
24 #[serde(rename = "ticker")]
26 Ticker,
27}
28
29impl fmt::Display for SubscriptionType {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::Trade => write!(f, "trade"),
33 Self::Orderbook => write!(f, "orderbook"),
34 Self::Ticker => write!(f, "ticker"),
35 }
36 }
37}
38
39#[derive(Debug, Clone, Serialize)]
41pub struct BithumbSubscription {
42 pub ticket: String,
44
45 #[serde(rename = "type")]
47 pub data_type: String,
48
49 pub codes: Vec<String>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub level: Option<f64>,
55
56 #[serde(skip_serializing_if = "Option::is_none", rename = "isOnlySnapshot")]
58 pub is_only_snapshot: Option<bool>,
59
60 #[serde(skip_serializing_if = "Option::is_none", rename = "isOnlyRealtime")]
62 pub is_only_realtime: Option<bool>,
63}
64
65impl BithumbSubscription {
66 #[must_use]
68 pub fn new(
69 subscription_type: SubscriptionType,
70 codes: Vec<String>,
71 level: Option<f64>,
72 snapshot_only: bool,
73 realtime_only: bool,
74 ) -> Self {
75 let ticket = String::from(Uuid::new_v4().to_string());
77
78 let is_only_snapshot = if snapshot_only { Some(true) } else { None };
80 let is_only_realtime = if realtime_only { Some(true) } else { None };
81
82 Self {
83 ticket,
84 data_type: subscription_type.to_string().into(),
85 codes,
86 level,
87 is_only_snapshot,
88 is_only_realtime,
89 }
90 }
91
92 #[must_use]
94 pub fn trade(codes: Vec<String>) -> Self {
95 Self::new(SubscriptionType::Trade, codes, None, false, true)
96 }
97
98 #[must_use]
100 pub fn orderbook(codes: Vec<String>, level: Option<f64>) -> Self {
101 Self::new(SubscriptionType::Orderbook, codes, level, false, true)
102 }
103
104 #[must_use]
106 pub fn ticker(codes: Vec<String>) -> Self {
107 Self::new(SubscriptionType::Ticker, codes, None, false, true)
108 }
109}
110
111#[derive(Debug, Clone, Serialize)]
113pub struct ResponseFormat {
114 pub format: String,
116}
117
118impl ResponseFormat {
119 #[must_use]
121 pub fn new(simple: bool) -> Self {
122 let format = if simple { "SIMPLE" } else { "DEFAULT" };
123 Self {
124 format: format.into(),
125 }
126 }
127
128 #[must_use]
130 pub fn simple() -> Self {
131 Self::new(true)
132 }
133}
134
135impl Default for ResponseFormat {
136 fn default() -> Self {
137 Self::new(false)
138 }
139}
140
141#[derive(Debug, Clone, Deserialize)]
143pub struct StatusMessage {
144 pub status: String,
146}
147
148#[derive(Debug, Clone)]
150pub struct ReconnectionParams {
151 pub initial_delay: Duration,
153
154 pub max_delay: Duration,
156
157 pub backoff_factor: f64,
159
160 pub max_attempts: usize,
162
163 pub attempt: usize,
165}
166
167impl Default for ReconnectionParams {
168 fn default() -> Self {
169 Self {
170 initial_delay: Duration::from_millis(100),
171 max_delay: Duration::from_secs(30),
172 backoff_factor: 1.5,
173 max_attempts: 20,
174 attempt: 0,
175 }
176 }
177}
178
179impl ReconnectionParams {
180 pub fn next_delay(&self) -> Duration {
182 let delay_millis =
183 (self.initial_delay.as_millis() as f64) * self.backoff_factor.powf(self.attempt as f64);
184
185 let delay_millis = delay_millis.min(self.max_delay.as_millis() as f64) as u64;
186 Duration::from_millis(delay_millis)
187 }
188
189 pub const fn increment(&mut self) {
191 self.attempt += 1;
192 }
193
194 pub const fn reset(&mut self) {
196 self.attempt = 0;
197 }
198
199 pub const fn is_max_attempts_reached(&self) -> bool {
201 self.attempt >= self.max_attempts
202 }
203}