Skip to main content

mabi_modbus/runtime/
config.rs

1//! Runtime configuration types and utilities.
2
3use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7/// Connection limits configuration.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ConnectionLimits {
10    /// Maximum concurrent connections.
11    pub max_connections: usize,
12
13    /// Maximum connections per IP address.
14    pub max_per_ip: Option<usize>,
15
16    /// Connection rate limit (connections per second).
17    pub rate_limit: Option<f64>,
18
19    /// Burst allowance for rate limiting.
20    pub burst_size: Option<usize>,
21}
22
23impl Default for ConnectionLimits {
24    fn default() -> Self {
25        Self {
26            max_connections: 100,
27            max_per_ip: Some(10),
28            rate_limit: None,
29            burst_size: None,
30        }
31    }
32}
33
34/// Timeout configuration.
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct TimeoutConfig {
37    /// Connection idle timeout.
38    pub idle: Duration,
39
40    /// Request processing timeout.
41    pub request: Duration,
42
43    /// Connection handshake timeout.
44    pub handshake: Duration,
45}
46
47impl Default for TimeoutConfig {
48    fn default() -> Self {
49        Self {
50            idle: Duration::from_secs(300),
51            request: Duration::from_secs(30),
52            handshake: Duration::from_secs(10),
53        }
54    }
55}
56
57/// Access control configuration for registers.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct AccessControl {
60    /// Whether read access is enabled by default.
61    pub default_read: bool,
62
63    /// Whether write access is enabled by default.
64    pub default_write: bool,
65
66    /// Specific read-only ranges.
67    pub read_only_ranges: Vec<AddressRange>,
68
69    /// Specific write-only ranges.
70    pub write_only_ranges: Vec<AddressRange>,
71
72    /// Blocked address ranges.
73    pub blocked_ranges: Vec<AddressRange>,
74}
75
76impl Default for AccessControl {
77    fn default() -> Self {
78        Self {
79            default_read: true,
80            default_write: true,
81            read_only_ranges: Vec::new(),
82            write_only_ranges: Vec::new(),
83            blocked_ranges: Vec::new(),
84        }
85    }
86}
87
88impl AccessControl {
89    /// Check if an address can be read.
90    pub fn can_read(&self, address: u16) -> bool {
91        // Check blocked first
92        for range in &self.blocked_ranges {
93            if range.contains(address) {
94                return false;
95            }
96        }
97
98        // Check write-only
99        for range in &self.write_only_ranges {
100            if range.contains(address) {
101                return false;
102            }
103        }
104
105        // Check read-only (always readable) or default
106        for range in &self.read_only_ranges {
107            if range.contains(address) {
108                return true;
109            }
110        }
111
112        self.default_read
113    }
114
115    /// Check if an address can be written.
116    pub fn can_write(&self, address: u16) -> bool {
117        // Check blocked first
118        for range in &self.blocked_ranges {
119            if range.contains(address) {
120                return false;
121            }
122        }
123
124        // Check read-only
125        for range in &self.read_only_ranges {
126            if range.contains(address) {
127                return false;
128            }
129        }
130
131        // Check write-only (always writable) or default
132        for range in &self.write_only_ranges {
133            if range.contains(address) {
134                return true;
135            }
136        }
137
138        self.default_write
139    }
140}
141
142/// Address range.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct AddressRange {
145    /// Start address (inclusive).
146    pub start: u16,
147
148    /// End address (inclusive).
149    pub end: u16,
150}
151
152impl AddressRange {
153    /// Create a new address range.
154    pub fn new(start: u16, end: u16) -> Self {
155        Self {
156            start: start.min(end),
157            end: start.max(end),
158        }
159    }
160
161    /// Create a single address range.
162    pub fn single(address: u16) -> Self {
163        Self::new(address, address)
164    }
165
166    /// Check if an address is in this range.
167    pub fn contains(&self, address: u16) -> bool {
168        address >= self.start && address <= self.end
169    }
170
171    /// Get the number of addresses in this range.
172    pub fn len(&self) -> usize {
173        (self.end - self.start + 1) as usize
174    }
175
176    /// Check if the range is empty.
177    pub fn is_empty(&self) -> bool {
178        false // A range always has at least one address
179    }
180}
181
182/// Feature flags for runtime feature toggling.
183#[derive(Debug, Clone, Default, Serialize, Deserialize)]
184pub struct FeatureFlags {
185    /// Enable detailed metrics.
186    pub detailed_metrics: bool,
187
188    /// Enable request logging.
189    pub request_logging: bool,
190
191    /// Enable connection logging.
192    pub connection_logging: bool,
193
194    /// Enable slow request warnings.
195    pub slow_request_warnings: bool,
196
197    /// Slow request threshold.
198    pub slow_request_threshold_ms: u64,
199
200    /// Enable broadcast (unit ID 0) support.
201    pub broadcast_enabled: bool,
202
203    /// Enable write operations.
204    pub writes_enabled: bool,
205
206    /// Enable diagnostic function codes.
207    pub diagnostics_enabled: bool,
208}
209
210impl FeatureFlags {
211    /// Production-ready defaults.
212    pub fn production() -> Self {
213        Self {
214            detailed_metrics: false,
215            request_logging: false,
216            connection_logging: false,
217            slow_request_warnings: true,
218            slow_request_threshold_ms: 100,
219            broadcast_enabled: false,
220            writes_enabled: true,
221            diagnostics_enabled: false,
222        }
223    }
224
225    /// Development/debug defaults.
226    pub fn development() -> Self {
227        Self {
228            detailed_metrics: true,
229            request_logging: true,
230            connection_logging: true,
231            slow_request_warnings: true,
232            slow_request_threshold_ms: 50,
233            broadcast_enabled: true,
234            writes_enabled: true,
235            diagnostics_enabled: true,
236        }
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    #[test]
245    fn test_address_range() {
246        let range = AddressRange::new(100, 200);
247        assert!(range.contains(100));
248        assert!(range.contains(150));
249        assert!(range.contains(200));
250        assert!(!range.contains(99));
251        assert!(!range.contains(201));
252        assert_eq!(range.len(), 101);
253    }
254
255    #[test]
256    fn test_address_range_reversed() {
257        // Should auto-correct reversed ranges
258        let range = AddressRange::new(200, 100);
259        assert_eq!(range.start, 100);
260        assert_eq!(range.end, 200);
261    }
262
263    #[test]
264    fn test_access_control_default() {
265        let access = AccessControl::default();
266        assert!(access.can_read(0));
267        assert!(access.can_read(65535));
268        assert!(access.can_write(0));
269        assert!(access.can_write(65535));
270    }
271
272    #[test]
273    fn test_access_control_read_only() {
274        let mut access = AccessControl::default();
275        access.read_only_ranges.push(AddressRange::new(100, 200));
276
277        assert!(access.can_read(150));
278        assert!(!access.can_write(150)); // Read-only means no write
279        assert!(access.can_write(50)); // Outside range, default applies
280    }
281
282    #[test]
283    fn test_access_control_blocked() {
284        let mut access = AccessControl::default();
285        access.blocked_ranges.push(AddressRange::new(500, 600));
286
287        assert!(!access.can_read(550));
288        assert!(!access.can_write(550));
289        assert!(access.can_read(499));
290        assert!(access.can_read(601));
291    }
292
293    #[test]
294    fn test_feature_flags_production() {
295        let flags = FeatureFlags::production();
296        assert!(!flags.detailed_metrics);
297        assert!(!flags.request_logging);
298        assert!(flags.writes_enabled);
299    }
300
301    #[test]
302    fn test_feature_flags_development() {
303        let flags = FeatureFlags::development();
304        assert!(flags.detailed_metrics);
305        assert!(flags.request_logging);
306        assert!(flags.diagnostics_enabled);
307    }
308
309    #[test]
310    fn test_timeout_config_default() {
311        let config = TimeoutConfig::default();
312        assert_eq!(config.idle, Duration::from_secs(300));
313        assert_eq!(config.request, Duration::from_secs(30));
314    }
315
316    #[test]
317    fn test_connection_limits_default() {
318        let limits = ConnectionLimits::default();
319        assert_eq!(limits.max_connections, 100);
320        assert_eq!(limits.max_per_ip, Some(10));
321    }
322}