1use crate::config::utils::{get_env_optional, get_env_or_default};
8use crate::constants::{
9 DEFAULT_CONNECTION_TIMEOUT_SECS, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_LOG_LEVEL,
10 DEFAULT_PROD_HOST, DEFAULT_PROD_PORT, DEFAULT_RECONNECT_ATTEMPTS, DEFAULT_RECONNECT_DELAY_SECS,
11 DEFAULT_SENDER_COMP_ID, DEFAULT_SSL_PORT, DEFAULT_TARGET_COMP_ID, DEFAULT_TEST_HOST,
12 DEFAULT_TEST_PORT,
13};
14use crate::error::{DeribitFixError, Result};
15use dotenv::dotenv;
16use serde::{Deserialize, Serialize};
17use std::{fmt::Debug, time::Duration};
18use tracing::debug;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct DeribitFixConfig {
23 pub username: String,
25 pub password: String,
27 pub host: String,
29 pub port: u16,
31 pub use_ssl: bool,
33 pub test_mode: bool,
35 pub heartbeat_interval: u32,
37 pub connection_timeout: Duration,
39 pub reconnect_attempts: u32,
41 pub reconnect_delay: Duration,
43 pub enable_logging: bool,
45 pub log_level: String,
47 pub sender_comp_id: String,
49 pub target_comp_id: String,
51 pub cancel_on_disconnect: bool,
53 pub app_id: Option<String>,
55 pub app_secret: Option<String>,
57}
58
59impl DeribitFixConfig {
60 pub fn new() -> Self {
62 match dotenv() {
64 Ok(_) => debug!("Successfully loaded .env file"),
65 Err(e) => debug!("Failed to load .env file: {}", e),
66 }
67
68 let test_mode = get_env_or_default("DERIBIT_TEST_MODE", true);
69 let use_ssl = get_env_or_default("DERIBIT_USE_SSL", false);
70
71 let (default_host, default_port) = if test_mode {
72 if use_ssl {
73 (DEFAULT_TEST_HOST, DEFAULT_SSL_PORT)
74 } else {
75 (DEFAULT_TEST_HOST, DEFAULT_TEST_PORT)
76 }
77 } else if use_ssl {
78 (DEFAULT_PROD_HOST, DEFAULT_SSL_PORT)
79 } else {
80 (DEFAULT_PROD_HOST, DEFAULT_PROD_PORT)
81 };
82
83 Self {
84 username: get_env_or_default("DERIBIT_USERNAME", String::new()),
85 password: get_env_or_default("DERIBIT_PASSWORD", String::new()),
86 host: get_env_or_default("DERIBIT_HOST", default_host.to_string()),
87 port: get_env_or_default("DERIBIT_PORT", default_port),
88 use_ssl,
89 test_mode,
90 heartbeat_interval: get_env_or_default(
91 "DERIBIT_HEARTBEAT_INTERVAL",
92 DEFAULT_HEARTBEAT_INTERVAL,
93 ),
94 connection_timeout: Duration::from_secs(get_env_or_default(
95 "DERIBIT_CONNECTION_TIMEOUT",
96 DEFAULT_CONNECTION_TIMEOUT_SECS,
97 )),
98 reconnect_attempts: get_env_or_default(
99 "DERIBIT_RECONNECT_ATTEMPTS",
100 DEFAULT_RECONNECT_ATTEMPTS,
101 ),
102 reconnect_delay: Duration::from_secs(get_env_or_default(
103 "DERIBIT_RECONNECT_DELAY",
104 DEFAULT_RECONNECT_DELAY_SECS,
105 )),
106 enable_logging: get_env_or_default("DERIBIT_ENABLE_LOGGING", true),
107 log_level: get_env_or_default("DERIBIT_LOG_LEVEL", DEFAULT_LOG_LEVEL.to_string()),
108 sender_comp_id: get_env_or_default(
109 "DERIBIT_SENDER_COMP_ID",
110 DEFAULT_SENDER_COMP_ID.to_string(),
111 ),
112 target_comp_id: get_env_or_default(
113 "DERIBIT_TARGET_COMP_ID",
114 DEFAULT_TARGET_COMP_ID.to_string(),
115 ),
116 cancel_on_disconnect: get_env_or_default("DERIBIT_CANCEL_ON_DISCONNECT", false),
117 app_id: get_env_optional("DERIBIT_APP_ID"),
118 app_secret: get_env_optional("DERIBIT_APP_SECRET"),
119 }
120 }
121
122 pub fn with_credentials(mut self, username: String, password: String) -> Self {
124 self.username = username;
125 self.password = password;
126 self
127 }
128
129 pub fn production() -> Self {
131 let mut config = Self::new();
132 config.test_mode = false;
133 config.host = get_env_or_default("DERIBIT_HOST", DEFAULT_PROD_HOST.to_string());
134 config.port = if config.use_ssl {
135 get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT)
136 } else {
137 get_env_or_default("DERIBIT_PORT", DEFAULT_PROD_PORT)
138 };
139 config
140 }
141
142 pub fn production_with_credentials(username: String, password: String) -> Self {
144 let mut config = Self::production();
145 config.username = username;
146 config.password = password;
147 config
148 }
149
150 pub fn production_ssl() -> Self {
152 let mut config = Self::production();
153 config.use_ssl = true;
154 config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
155 config
156 }
157
158 pub fn test_ssl() -> Self {
160 let mut config = Self::new();
161 config.use_ssl = true;
162 config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
163 config
164 }
165
166 pub fn with_endpoint(mut self, host: String, port: u16) -> Self {
168 self.host = host;
169 self.port = port;
170 self
171 }
172
173 pub fn with_ssl(mut self, use_ssl: bool) -> Self {
175 self.use_ssl = use_ssl;
176 self
177 }
178
179 pub fn with_heartbeat_interval(mut self, interval: u32) -> Self {
181 self.heartbeat_interval = interval;
182 self
183 }
184
185 pub fn with_connection_timeout(mut self, timeout: Duration) -> Self {
187 self.connection_timeout = timeout;
188 self
189 }
190
191 pub fn with_reconnection(mut self, attempts: u32, delay: Duration) -> Self {
193 self.reconnect_attempts = attempts;
194 self.reconnect_delay = delay;
195 self
196 }
197
198 pub fn with_logging(mut self, enabled: bool, level: String) -> Self {
200 self.enable_logging = enabled;
201 self.log_level = level;
202 self
203 }
204
205 pub fn with_session_ids(mut self, sender_comp_id: String, target_comp_id: String) -> Self {
207 self.sender_comp_id = sender_comp_id;
208 self.target_comp_id = target_comp_id;
209 self
210 }
211
212 pub fn with_cancel_on_disconnect(mut self, cancel_on_disconnect: bool) -> Self {
214 self.cancel_on_disconnect = cancel_on_disconnect;
215 self
216 }
217
218 pub fn with_app_credentials(mut self, app_id: String, app_secret: String) -> Self {
220 self.app_id = Some(app_id);
221 self.app_secret = Some(app_secret);
222 self
223 }
224
225 pub fn connection_url(&self) -> String {
227 format!("{}:{}", self.host, self.port)
228 }
229
230 pub fn validate(&self) -> Result<()> {
232 if self.username.is_empty() {
233 return Err(DeribitFixError::Config(
234 "Username cannot be empty".to_string(),
235 ));
236 }
237
238 if self.password.is_empty() {
239 return Err(DeribitFixError::Config(
240 "Password cannot be empty".to_string(),
241 ));
242 }
243
244 if self.host.is_empty() {
245 return Err(DeribitFixError::Config("Host cannot be empty".to_string()));
246 }
247
248 if self.port == 0 {
249 return Err(DeribitFixError::Config(
250 "Port must be greater than 0".to_string(),
251 ));
252 }
253
254 if self.heartbeat_interval == 0 {
255 return Err(DeribitFixError::Config(
256 "Heartbeat interval must be greater than 0".to_string(),
257 ));
258 }
259
260 if self.sender_comp_id.is_empty() {
261 return Err(DeribitFixError::Config(
262 "Sender company ID cannot be empty".to_string(),
263 ));
264 }
265
266 if self.target_comp_id.is_empty() {
267 return Err(DeribitFixError::Config(
268 "Target company ID cannot be empty".to_string(),
269 ));
270 }
271
272 if self.app_id.is_some() && self.app_secret.is_none() {
274 return Err(DeribitFixError::Config(
275 "Application secret is required when app ID is provided".to_string(),
276 ));
277 }
278
279 if self.app_secret.is_some() && self.app_id.is_none() {
280 return Err(DeribitFixError::Config(
281 "Application ID is required when app secret is provided".to_string(),
282 ));
283 }
284
285 Ok(())
286 }
287}
288
289impl Default for DeribitFixConfig {
290 fn default() -> Self {
291 Self::new()
292 }
293}