1use crate::error::TimerError;
7use std::num::NonZeroUsize;
8use std::time::Duration;
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.is_multiple_of(l0_ms) {
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, Default)]
381pub struct TimerConfig {
382 pub wheel: WheelConfig,
384 pub service: ServiceConfig,
386 pub batch: BatchConfig,
388}
389
390impl TimerConfig {
391 pub fn builder() -> TimerConfigBuilder {
393 TimerConfigBuilder::default()
394 }
395}
396
397#[derive(Debug, Default)]
401pub struct TimerConfigBuilder {
402 wheel_builder: WheelConfigBuilder,
403 service_builder: ServiceConfigBuilder,
404 batch_config: BatchConfig,
405}
406
407impl TimerConfigBuilder {
408 pub fn command_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
410 self.service_builder = self.service_builder.command_channel_capacity(capacity);
411 self
412 }
413
414 pub fn timeout_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
416 self.service_builder = self.service_builder.timeout_channel_capacity(capacity);
417 self
418 }
419
420 pub fn small_batch_threshold(mut self, threshold: usize) -> Self {
422 self.batch_config.small_batch_threshold = threshold;
423 self
424 }
425
426 pub fn build(self) -> Result<TimerConfig, TimerError> {
439 Ok(TimerConfig {
440 wheel: self.wheel_builder.build()?,
441 service: self.service_builder.build(),
442 batch: self.batch_config,
443 })
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 #[test]
452 fn test_wheel_config_default() {
453 let config = WheelConfig::default();
454 assert_eq!(config.l0_tick_duration, Duration::from_millis(10));
455 assert_eq!(config.l0_slot_count, 512);
456 assert_eq!(config.l1_tick_duration, Duration::from_secs(1));
457 assert_eq!(config.l1_slot_count, 64);
458 }
459
460 #[test]
461 fn test_wheel_config_builder() {
462 let config = WheelConfig::builder()
463 .l0_tick_duration(Duration::from_millis(20))
464 .l0_slot_count(1024)
465 .l1_tick_duration(Duration::from_secs(2))
466 .l1_slot_count(128)
467 .build()
468 .unwrap();
469
470 assert_eq!(config.l0_tick_duration, Duration::from_millis(20));
471 assert_eq!(config.l0_slot_count, 1024);
472 assert_eq!(config.l1_tick_duration, Duration::from_secs(2));
473 assert_eq!(config.l1_slot_count, 128);
474 }
475
476 #[test]
477 fn test_wheel_config_validation_zero_tick() {
478 let result = WheelConfig::builder()
479 .l0_tick_duration(Duration::ZERO)
480 .build();
481
482 assert!(result.is_err());
483 }
484
485 #[test]
486 fn test_wheel_config_validation_invalid_slot_count() {
487 let result = WheelConfig::builder().l0_slot_count(100).build();
488
489 assert!(result.is_err());
490 }
491
492 #[test]
493 fn test_service_config_builder() {
494 let config = ServiceConfig::builder()
495 .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
496 .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
497 .build();
498
499 assert_eq!(
500 config.command_channel_capacity,
501 NonZeroUsize::new(1024).unwrap()
502 );
503 assert_eq!(
504 config.timeout_channel_capacity,
505 NonZeroUsize::new(2000).unwrap()
506 );
507 }
508
509 #[test]
510 fn test_batch_config_default() {
511 let config = BatchConfig::default();
512 assert_eq!(config.small_batch_threshold, 10);
513 }
514
515 #[test]
516 fn test_timer_config_default() {
517 let config = TimerConfig::default();
518 assert_eq!(config.wheel.l0_slot_count, 512);
519 assert_eq!(
520 config.service.command_channel_capacity,
521 NonZeroUsize::new(512).unwrap()
522 );
523 assert_eq!(config.batch.small_batch_threshold, 10);
524 }
525
526 #[test]
527 fn test_timer_config_builder() {
528 let config = TimerConfig::builder()
529 .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
530 .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
531 .small_batch_threshold(20)
532 .build()
533 .unwrap();
534
535 assert_eq!(
536 config.service.command_channel_capacity,
537 NonZeroUsize::new(1024).unwrap()
538 );
539 assert_eq!(
540 config.service.timeout_channel_capacity,
541 NonZeroUsize::new(2000).unwrap()
542 );
543 assert_eq!(config.batch.small_batch_threshold, 20);
544 }
545}