lastid_sdk/config/
polling.rs1#[cfg(feature = "json-schema")]
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::error::LastIDError;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
12#[serde(default)]
13pub struct PollingConfig {
14 #[serde(default = "default_initial_interval")]
16 pub initial_interval_ms: u64,
17
18 #[serde(default = "default_max_interval")]
20 pub max_interval_ms: u64,
21
22 #[serde(default = "default_backoff")]
24 pub backoff_multiplier: f64,
25
26 #[serde(default = "default_max_duration")]
28 pub max_duration_seconds: u64,
29}
30
31const fn default_initial_interval() -> u64 {
32 2000
33}
34const fn default_max_interval() -> u64 {
35 30000
36}
37const fn default_backoff() -> f64 {
38 1.5
39}
40const fn default_max_duration() -> u64 {
41 300
42}
43
44impl Default for PollingConfig {
45 fn default() -> Self {
46 Self {
47 initial_interval_ms: default_initial_interval(),
48 max_interval_ms: default_max_interval(),
49 backoff_multiplier: default_backoff(),
50 max_duration_seconds: default_max_duration(),
51 }
52 }
53}
54
55impl PollingConfig {
56 pub fn validate(&self) -> Result<(), LastIDError> {
58 if self.initial_interval_ms == 0 {
59 return Err(LastIDError::config("initial_interval_ms must be > 0"));
60 }
61
62 if self.max_interval_ms < self.initial_interval_ms {
63 return Err(LastIDError::config(
64 "max_interval_ms must be >= initial_interval_ms",
65 ));
66 }
67
68 if self.backoff_multiplier < 1.0 {
69 return Err(LastIDError::config("backoff_multiplier must be >= 1.0"));
70 }
71
72 if self.max_duration_seconds == 0 {
73 return Err(LastIDError::config("max_duration_seconds must be > 0"));
74 }
75
76 Ok(())
77 }
78
79 #[must_use]
81 #[allow(
82 clippy::cast_precision_loss,
83 clippy::cast_possible_truncation,
84 clippy::cast_sign_loss
85 )]
86 pub fn next_interval(&self, current_ms: u64) -> u64 {
87 let next = (current_ms as f64 * self.backoff_multiplier) as u64;
91 next.min(self.max_interval_ms)
92 }
93
94 pub fn merge(&mut self, other: &Self) {
96 if other.initial_interval_ms != default_initial_interval() {
97 self.initial_interval_ms = other.initial_interval_ms;
98 }
99 if other.max_interval_ms != default_max_interval() {
100 self.max_interval_ms = other.max_interval_ms;
101 }
102 if (other.backoff_multiplier - default_backoff()).abs() > f64::EPSILON {
103 self.backoff_multiplier = other.backoff_multiplier;
104 }
105 if other.max_duration_seconds != default_max_duration() {
106 self.max_duration_seconds = other.max_duration_seconds;
107 }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_default_values() {
117 let config = PollingConfig::default();
118 assert_eq!(config.initial_interval_ms, 2000);
119 assert_eq!(config.max_interval_ms, 30000);
120 assert!((config.backoff_multiplier - 1.5).abs() < f64::EPSILON);
121 assert_eq!(config.max_duration_seconds, 300);
122 }
123
124 #[test]
125 fn test_next_interval() {
126 let config = PollingConfig::default();
127
128 assert_eq!(config.next_interval(2000), 3000);
130
131 assert_eq!(config.next_interval(3000), 4500);
133
134 assert_eq!(config.next_interval(30000), 30000);
136 }
137
138 #[test]
139 fn test_validate_invalid() {
140 let mut config = PollingConfig::default();
141
142 config.initial_interval_ms = 0;
143 assert!(config.validate().is_err());
144
145 config.initial_interval_ms = 2000;
146 config.max_interval_ms = 1000; assert!(config.validate().is_err());
148
149 config.max_interval_ms = 30000;
150 config.backoff_multiplier = 0.5; assert!(config.validate().is_err());
152
153 config.backoff_multiplier = 1.5;
154 config.max_duration_seconds = 0;
155 assert!(config.validate().is_err());
156 }
157
158 #[test]
159 fn test_validate_valid() {
160 let config = PollingConfig::default();
161 assert!(config.validate().is_ok());
162 }
163}