Skip to main content

ironfix_session/
config.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 27/1/26
5******************************************************************************/
6
7//! Session configuration.
8//!
9//! This module provides configuration options for FIX sessions.
10
11use ironfix_core::types::CompId;
12use std::time::Duration;
13
14/// Configuration for a FIX session.
15#[derive(Debug, Clone)]
16pub struct SessionConfig {
17    /// Sender CompID (tag 49).
18    pub sender_comp_id: CompId,
19    /// Target CompID (tag 56).
20    pub target_comp_id: CompId,
21    /// FIX version BeginString (e.g., "FIX.4.4").
22    pub begin_string: String,
23    /// Heartbeat interval in seconds.
24    pub heartbeat_interval: Duration,
25    /// Whether to reset sequence numbers on logon.
26    pub reset_on_logon: bool,
27    /// Whether to reset sequence numbers on logout.
28    pub reset_on_logout: bool,
29    /// Whether to reset sequence numbers on disconnect.
30    pub reset_on_disconnect: bool,
31    /// Maximum message size in bytes.
32    pub max_message_size: usize,
33    /// Logon timeout duration.
34    pub logon_timeout: Duration,
35    /// Logout timeout duration.
36    pub logout_timeout: Duration,
37    /// Whether to validate incoming message checksums.
38    pub validate_checksum: bool,
39    /// Whether to validate incoming message length.
40    pub validate_length: bool,
41    /// Optional sender sub ID (tag 50).
42    pub sender_sub_id: Option<String>,
43    /// Optional target sub ID (tag 57).
44    pub target_sub_id: Option<String>,
45    /// Optional sender location ID (tag 142).
46    pub sender_location_id: Option<String>,
47    /// Optional target location ID (tag 143).
48    pub target_location_id: Option<String>,
49}
50
51impl SessionConfig {
52    /// Creates a new session configuration with required fields.
53    ///
54    /// # Arguments
55    /// * `sender_comp_id` - The sender CompID
56    /// * `target_comp_id` - The target CompID
57    /// * `begin_string` - The FIX version string
58    #[must_use]
59    pub fn new(
60        sender_comp_id: CompId,
61        target_comp_id: CompId,
62        begin_string: impl Into<String>,
63    ) -> Self {
64        Self {
65            sender_comp_id,
66            target_comp_id,
67            begin_string: begin_string.into(),
68            heartbeat_interval: Duration::from_secs(30),
69            reset_on_logon: false,
70            reset_on_logout: false,
71            reset_on_disconnect: false,
72            max_message_size: 1024 * 1024, // 1MB
73            logon_timeout: Duration::from_secs(10),
74            logout_timeout: Duration::from_secs(10),
75            validate_checksum: true,
76            validate_length: true,
77            sender_sub_id: None,
78            target_sub_id: None,
79            sender_location_id: None,
80            target_location_id: None,
81        }
82    }
83
84    /// Sets the heartbeat interval.
85    #[must_use]
86    pub fn with_heartbeat_interval(mut self, interval: Duration) -> Self {
87        self.heartbeat_interval = interval;
88        self
89    }
90
91    /// Sets whether to reset sequence numbers on logon.
92    #[must_use]
93    pub const fn with_reset_on_logon(mut self, reset: bool) -> Self {
94        self.reset_on_logon = reset;
95        self
96    }
97
98    /// Sets the maximum message size.
99    #[must_use]
100    pub const fn with_max_message_size(mut self, size: usize) -> Self {
101        self.max_message_size = size;
102        self
103    }
104
105    /// Sets the logon timeout.
106    #[must_use]
107    pub fn with_logon_timeout(mut self, timeout: Duration) -> Self {
108        self.logon_timeout = timeout;
109        self
110    }
111
112    /// Sets the sender sub ID.
113    #[must_use]
114    pub fn with_sender_sub_id(mut self, sub_id: impl Into<String>) -> Self {
115        self.sender_sub_id = Some(sub_id.into());
116        self
117    }
118
119    /// Sets the target sub ID.
120    #[must_use]
121    pub fn with_target_sub_id(mut self, sub_id: impl Into<String>) -> Self {
122        self.target_sub_id = Some(sub_id.into());
123        self
124    }
125
126    /// Returns the heartbeat interval in seconds.
127    #[must_use]
128    pub fn heartbeat_interval_secs(&self) -> u64 {
129        self.heartbeat_interval.as_secs()
130    }
131}
132
133/// Builder for session configuration.
134#[derive(Debug, Default)]
135pub struct SessionConfigBuilder {
136    sender_comp_id: Option<CompId>,
137    target_comp_id: Option<CompId>,
138    begin_string: Option<String>,
139    heartbeat_interval: Option<Duration>,
140    reset_on_logon: bool,
141    max_message_size: Option<usize>,
142}
143
144impl SessionConfigBuilder {
145    /// Creates a new builder.
146    #[must_use]
147    pub fn new() -> Self {
148        Self::default()
149    }
150
151    /// Sets the sender CompID.
152    #[must_use]
153    pub fn sender_comp_id(mut self, id: CompId) -> Self {
154        self.sender_comp_id = Some(id);
155        self
156    }
157
158    /// Sets the target CompID.
159    #[must_use]
160    pub fn target_comp_id(mut self, id: CompId) -> Self {
161        self.target_comp_id = Some(id);
162        self
163    }
164
165    /// Sets the FIX version.
166    #[must_use]
167    pub fn begin_string(mut self, version: impl Into<String>) -> Self {
168        self.begin_string = Some(version.into());
169        self
170    }
171
172    /// Sets the heartbeat interval.
173    #[must_use]
174    pub fn heartbeat_interval(mut self, interval: Duration) -> Self {
175        self.heartbeat_interval = Some(interval);
176        self
177    }
178
179    /// Sets whether to reset on logon.
180    #[must_use]
181    pub const fn reset_on_logon(mut self, reset: bool) -> Self {
182        self.reset_on_logon = reset;
183        self
184    }
185
186    /// Builds the configuration.
187    ///
188    /// # Panics
189    /// Panics if required fields are not set.
190    #[must_use]
191    pub fn build(self) -> SessionConfig {
192        let sender = self.sender_comp_id.expect("sender_comp_id is required");
193        let target = self.target_comp_id.expect("target_comp_id is required");
194        let begin_string = self.begin_string.unwrap_or_else(|| "FIX.4.4".to_string());
195
196        let mut config = SessionConfig::new(sender, target, begin_string);
197
198        if let Some(interval) = self.heartbeat_interval {
199            config.heartbeat_interval = interval;
200        }
201        config.reset_on_logon = self.reset_on_logon;
202        if let Some(size) = self.max_message_size {
203            config.max_message_size = size;
204        }
205
206        config
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn test_session_config_new() {
216        let sender = CompId::new("SENDER").unwrap();
217        let target = CompId::new("TARGET").unwrap();
218        let config = SessionConfig::new(sender, target, "FIX.4.4");
219
220        assert_eq!(config.sender_comp_id.as_str(), "SENDER");
221        assert_eq!(config.target_comp_id.as_str(), "TARGET");
222        assert_eq!(config.begin_string, "FIX.4.4");
223        assert_eq!(config.heartbeat_interval, Duration::from_secs(30));
224    }
225
226    #[test]
227    fn test_session_config_builder() {
228        let config = SessionConfigBuilder::new()
229            .sender_comp_id(CompId::new("SENDER").unwrap())
230            .target_comp_id(CompId::new("TARGET").unwrap())
231            .begin_string("FIX.4.2")
232            .heartbeat_interval(Duration::from_secs(60))
233            .reset_on_logon(true)
234            .build();
235
236        assert_eq!(config.begin_string, "FIX.4.2");
237        assert_eq!(config.heartbeat_interval, Duration::from_secs(60));
238        assert!(config.reset_on_logon);
239    }
240}