1use crate::error::TimerError;
7use std::time::Duration;
8use std::num::NonZeroUsize;
9
10#[derive(Debug, Clone)]
36pub struct WheelConfig {
37 pub l0_tick_duration: Duration,
41
42 pub l0_slot_count: usize,
46
47 pub l1_tick_duration: Duration,
51
52 pub l1_slot_count: usize,
56}
57
58impl Default for WheelConfig {
59 fn default() -> Self {
60 Self {
61 l0_tick_duration: Duration::from_millis(10),
62 l0_slot_count: 512,
63 l1_tick_duration: Duration::from_secs(1),
64 l1_slot_count: 64,
65 }
66 }
67}
68
69impl WheelConfig {
70 pub fn builder() -> WheelConfigBuilder {
72 WheelConfigBuilder::default()
73 }
74}
75
76#[derive(Debug, Clone)]
78pub struct WheelConfigBuilder {
79 l0_tick_duration: Duration,
80 l0_slot_count: usize,
81 l1_tick_duration: Duration,
82 l1_slot_count: usize,
83}
84
85impl Default for WheelConfigBuilder {
86 fn default() -> Self {
87 Self {
88 l0_tick_duration: Duration::from_millis(10),
89 l0_slot_count: 512,
90 l1_tick_duration: Duration::from_secs(1),
91 l1_slot_count: 64,
92 }
93 }
94}
95
96impl WheelConfigBuilder {
97 pub fn l0_tick_duration(mut self, duration: Duration) -> Self {
99 self.l0_tick_duration = duration;
100 self
101 }
102
103 pub fn l0_slot_count(mut self, count: usize) -> Self {
105 self.l0_slot_count = count;
106 self
107 }
108
109 pub fn l1_tick_duration(mut self, duration: Duration) -> Self {
111 self.l1_tick_duration = duration;
112 self
113 }
114
115 pub fn l1_slot_count(mut self, count: usize) -> Self {
117 self.l1_slot_count = count;
118 self
119 }
120
121 pub fn build(self) -> Result<WheelConfig, TimerError> {
134 if self.l0_tick_duration.is_zero() {
136 return Err(TimerError::InvalidConfiguration {
137 field: "l0_tick_duration".to_string(),
138 reason: "L0 layer tick duration must be greater than 0".to_string(),
139 });
140 }
141
142 if self.l0_slot_count == 0 {
143 return Err(TimerError::InvalidSlotCount {
144 slot_count: self.l0_slot_count,
145 reason: "L0 layer slot count must be greater than 0",
146 });
147 }
148
149 if !self.l0_slot_count.is_power_of_two() {
150 return Err(TimerError::InvalidSlotCount {
151 slot_count: self.l0_slot_count,
152 reason: "L0 layer slot count must be power of 2",
153 });
154 }
155
156 if self.l1_tick_duration.is_zero() {
158 return Err(TimerError::InvalidConfiguration {
159 field: "l1_tick_duration".to_string(),
160 reason: "L1 layer tick duration must be greater than 0".to_string(),
161 });
162 }
163
164 if self.l1_slot_count == 0 {
165 return Err(TimerError::InvalidSlotCount {
166 slot_count: self.l1_slot_count,
167 reason: "L1 layer slot count must be greater than 0",
168 });
169 }
170
171 if !self.l1_slot_count.is_power_of_two() {
172 return Err(TimerError::InvalidSlotCount {
173 slot_count: self.l1_slot_count,
174 reason: "L1 layer slot count must be power of 2",
175 });
176 }
177
178 let l0_ms = self.l0_tick_duration.as_millis() as u64;
180 let l1_ms = self.l1_tick_duration.as_millis() as u64;
181 if l1_ms % l0_ms != 0 {
182 return Err(TimerError::InvalidConfiguration {
183 field: "l1_tick_duration".to_string(),
184 reason: format!(
185 "L1 tick duration ({} ms) must be an integer multiple of L0 tick duration ({} ms)",
186 l1_ms, l0_ms
187 ),
188 });
189 }
190
191 Ok(WheelConfig {
192 l0_tick_duration: self.l0_tick_duration,
193 l0_slot_count: self.l0_slot_count,
194 l1_tick_duration: self.l1_tick_duration,
195 l1_slot_count: self.l1_slot_count,
196 })
197 }
198}
199
200#[derive(Debug, Clone)]
223pub struct ServiceConfig {
224 pub command_channel_capacity: NonZeroUsize,
228
229 pub timeout_channel_capacity: NonZeroUsize,
233}
234
235impl Default for ServiceConfig {
236 fn default() -> Self {
237 Self {
238 command_channel_capacity: NonZeroUsize::new(512).unwrap(),
239 timeout_channel_capacity: NonZeroUsize::new(1000).unwrap(),
240 }
241 }
242}
243
244impl ServiceConfig {
245 pub fn builder() -> ServiceConfigBuilder {
247 ServiceConfigBuilder::default()
248 }
249}
250
251#[derive(Debug, Clone)]
255pub struct ServiceConfigBuilder {
256 pub command_channel_capacity: NonZeroUsize,
260
261 pub timeout_channel_capacity: NonZeroUsize,
265}
266
267impl Default for ServiceConfigBuilder {
268 fn default() -> Self {
269 let config = ServiceConfig::default();
270 Self {
271 command_channel_capacity: config.command_channel_capacity,
272 timeout_channel_capacity: config.timeout_channel_capacity,
273 }
274 }
275}
276
277impl ServiceConfigBuilder {
278 pub fn command_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
280 self.command_channel_capacity = capacity;
281 self
282 }
283
284 pub fn timeout_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
286 self.timeout_channel_capacity = capacity;
287 self
288 }
289
290 pub fn build(self) -> ServiceConfig {
309 ServiceConfig {
310 command_channel_capacity: self.command_channel_capacity,
311 timeout_channel_capacity: self.timeout_channel_capacity,
312 }
313 }
314}
315
316#[derive(Debug, Clone)]
339pub struct BatchConfig {
340 pub small_batch_threshold: usize,
347}
348
349impl Default for BatchConfig {
350 fn default() -> Self {
351 Self {
352 small_batch_threshold: 10,
353 }
354 }
355}
356
357#[derive(Debug, Clone)]
381pub struct TimerConfig {
382 pub wheel: WheelConfig,
384 pub service: ServiceConfig,
386 pub batch: BatchConfig,
388}
389
390impl Default for TimerConfig {
391 fn default() -> Self {
392 Self {
393 wheel: WheelConfig::default(),
394 service: ServiceConfig::default(),
395 batch: BatchConfig::default(),
396 }
397 }
398}
399
400impl TimerConfig {
401 pub fn builder() -> TimerConfigBuilder {
403 TimerConfigBuilder::default()
404 }
405}
406
407#[derive(Debug)]
411pub struct TimerConfigBuilder {
412 wheel_builder: WheelConfigBuilder,
413 service_builder: ServiceConfigBuilder,
414 batch_config: BatchConfig,
415}
416
417impl Default for TimerConfigBuilder {
418 fn default() -> Self {
419 Self {
420 wheel_builder: WheelConfigBuilder::default(),
421 service_builder: ServiceConfigBuilder::default(),
422 batch_config: BatchConfig::default(),
423 }
424 }
425}
426
427impl TimerConfigBuilder {
428 pub fn command_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
430 self.service_builder = self.service_builder.command_channel_capacity(capacity);
431 self
432 }
433
434 pub fn timeout_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
436 self.service_builder = self.service_builder.timeout_channel_capacity(capacity);
437 self
438 }
439
440 pub fn small_batch_threshold(mut self, threshold: usize) -> Self {
442 self.batch_config.small_batch_threshold = threshold;
443 self
444 }
445
446 pub fn build(self) -> Result<TimerConfig, TimerError> {
459 Ok(TimerConfig {
460 wheel: self.wheel_builder.build()?,
461 service: self.service_builder.build(),
462 batch: self.batch_config,
463 })
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470
471 #[test]
472 fn test_wheel_config_default() {
473 let config = WheelConfig::default();
474 assert_eq!(config.l0_tick_duration, Duration::from_millis(10));
475 assert_eq!(config.l0_slot_count, 512);
476 assert_eq!(config.l1_tick_duration, Duration::from_secs(1));
477 assert_eq!(config.l1_slot_count, 64);
478 }
479
480 #[test]
481 fn test_wheel_config_builder() {
482 let config = WheelConfig::builder()
483 .l0_tick_duration(Duration::from_millis(20))
484 .l0_slot_count(1024)
485 .l1_tick_duration(Duration::from_secs(2))
486 .l1_slot_count(128)
487 .build()
488 .unwrap();
489
490 assert_eq!(config.l0_tick_duration, Duration::from_millis(20));
491 assert_eq!(config.l0_slot_count, 1024);
492 assert_eq!(config.l1_tick_duration, Duration::from_secs(2));
493 assert_eq!(config.l1_slot_count, 128);
494 }
495
496 #[test]
497 fn test_wheel_config_validation_zero_tick() {
498 let result = WheelConfig::builder()
499 .l0_tick_duration(Duration::ZERO)
500 .build();
501
502 assert!(result.is_err());
503 }
504
505 #[test]
506 fn test_wheel_config_validation_invalid_slot_count() {
507 let result = WheelConfig::builder()
508 .l0_slot_count(100)
509 .build();
510
511 assert!(result.is_err());
512 }
513
514 #[test]
515 fn test_service_config_builder() {
516 let config = ServiceConfig::builder()
517 .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
518 .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
519 .build();
520
521 assert_eq!(config.command_channel_capacity, NonZeroUsize::new(1024).unwrap());
522 assert_eq!(config.timeout_channel_capacity, NonZeroUsize::new(2000).unwrap());
523 }
524
525 #[test]
526 fn test_batch_config_default() {
527 let config = BatchConfig::default();
528 assert_eq!(config.small_batch_threshold, 10);
529 }
530
531 #[test]
532 fn test_timer_config_default() {
533 let config = TimerConfig::default();
534 assert_eq!(config.wheel.l0_slot_count, 512);
535 assert_eq!(config.service.command_channel_capacity, NonZeroUsize::new(512).unwrap());
536 assert_eq!(config.batch.small_batch_threshold, 10);
537 }
538
539 #[test]
540 fn test_timer_config_builder() {
541 let config = TimerConfig::builder()
542 .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
543 .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
544 .small_batch_threshold(20)
545 .build()
546 .unwrap();
547
548 assert_eq!(config.service.command_channel_capacity, NonZeroUsize::new(1024).unwrap());
549 assert_eq!(config.service.timeout_channel_capacity, NonZeroUsize::new(2000).unwrap());
550 assert_eq!(config.batch.small_batch_threshold, 20);
551 }
552}
553