canlink_hal/monitor/
config.rs1use std::time::Duration;
6
7use serde::Deserialize;
8
9use super::{ConnectionMonitor, ReconnectConfig};
10
11#[derive(Debug, Clone, Deserialize)]
26pub struct MonitorConfig {
27 #[serde(default = "default_heartbeat_ms")]
29 pub heartbeat_interval_ms: u64,
30
31 pub reconnect: Option<ReconnectConfigFile>,
33}
34
35fn default_heartbeat_ms() -> u64 {
36 1000
37}
38
39impl Default for MonitorConfig {
40 fn default() -> Self {
41 Self {
42 heartbeat_interval_ms: default_heartbeat_ms(),
43 reconnect: None,
44 }
45 }
46}
47
48#[derive(Debug, Clone, Deserialize)]
50pub struct ReconnectConfigFile {
51 #[serde(default = "default_max_retries")]
53 pub max_retries: u32,
54
55 #[serde(default = "default_retry_interval_ms")]
57 pub retry_interval_ms: u64,
58
59 #[serde(default = "default_backoff")]
61 pub backoff_multiplier: f32,
62}
63
64fn default_max_retries() -> u32 {
65 3
66}
67
68fn default_retry_interval_ms() -> u64 {
69 1000
70}
71
72fn default_backoff() -> f32 {
73 2.0
74}
75
76impl Default for ReconnectConfigFile {
77 fn default() -> Self {
78 Self {
79 max_retries: default_max_retries(),
80 retry_interval_ms: default_retry_interval_ms(),
81 backoff_multiplier: default_backoff(),
82 }
83 }
84}
85
86impl From<ReconnectConfigFile> for ReconnectConfig {
87 fn from(config: ReconnectConfigFile) -> Self {
88 ReconnectConfig {
89 max_retries: config.max_retries,
90 retry_interval: Duration::from_millis(config.retry_interval_ms),
91 backoff_multiplier: config.backoff_multiplier,
92 }
93 }
94}
95
96impl MonitorConfig {
97 pub fn from_toml(toml_str: &str) -> Result<Self, toml::de::Error> {
104 toml::from_str(toml_str)
105 }
106
107 #[must_use]
109 pub fn into_monitor(self) -> ConnectionMonitor {
110 let heartbeat = Duration::from_millis(self.heartbeat_interval_ms);
111
112 if let Some(reconnect) = self.reconnect {
113 ConnectionMonitor::with_reconnect(heartbeat, reconnect.into())
114 } else {
115 ConnectionMonitor::new(heartbeat)
116 }
117 }
118}
119
120impl ConnectionMonitor {
121 #[must_use]
123 pub fn from_config(config: &MonitorConfig) -> Self {
124 config.clone().into_monitor()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_default_config() {
134 let config = MonitorConfig::default();
135 assert_eq!(config.heartbeat_interval_ms, 1000);
136 assert!(config.reconnect.is_none());
137 }
138
139 #[test]
140 fn test_parse_basic() {
141 let toml = r"
142 heartbeat_interval_ms = 500
143 ";
144
145 let config: MonitorConfig = toml::from_str(toml).unwrap();
146 assert_eq!(config.heartbeat_interval_ms, 500);
147 assert!(config.reconnect.is_none());
148 }
149
150 #[test]
151 fn test_parse_with_reconnect() {
152 let toml = r"
153 heartbeat_interval_ms = 500
154
155 [reconnect]
156 max_retries = 5
157 retry_interval_ms = 2000
158 backoff_multiplier = 1.5
159 ";
160
161 let config: MonitorConfig = toml::from_str(toml).unwrap();
162 assert_eq!(config.heartbeat_interval_ms, 500);
163
164 let reconnect = config.reconnect.unwrap();
165 assert_eq!(reconnect.max_retries, 5);
166 assert_eq!(reconnect.retry_interval_ms, 2000);
167 assert!((reconnect.backoff_multiplier - 1.5).abs() < f32::EPSILON);
168 }
169
170 #[test]
171 fn test_into_monitor() {
172 let config = MonitorConfig {
173 heartbeat_interval_ms: 500,
174 reconnect: Some(ReconnectConfigFile::default()),
175 };
176
177 let monitor = config.into_monitor();
178 assert_eq!(monitor.heartbeat_interval(), Duration::from_millis(500));
179 assert!(monitor.auto_reconnect_enabled());
180 }
181
182 #[test]
183 fn test_into_monitor_no_reconnect() {
184 let config = MonitorConfig::default();
185 let monitor = config.into_monitor();
186 assert!(!monitor.auto_reconnect_enabled());
187 }
188}