1use std::fmt::{Display, Formatter};
9use std::ops::{Add, AddAssign, Sub, SubAssign};
10
11pub type TimeTick = usize;
13
14#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
16pub struct SimTime(TimeTick);
17
18impl Display for SimTime {
19 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
20 write!(f, "{}", self.0)
21 }
22}
23
24impl From<TimeTick> for SimTime {
25 fn from(v: TimeTick) -> Self {
26 SimTime(v)
27 }
28}
29
30impl Add<Duration> for SimTime {
31 type Output = SimTime;
32
33 fn add(self, rhs: Duration) -> Self::Output {
34 match self.0.checked_add(rhs.0) {
35 Some(val) => SimTime(val),
36 None => panic!("attempt to add with overflow"),
37 }
38 }
39}
40
41impl AddAssign<Duration> for SimTime {
42 fn add_assign(&mut self, rhs: Duration) {
43 match self.0.checked_add(rhs.0) {
44 Some(val) => self.0 = val,
45 None => panic!("attempt to add with overflow"),
46 }
47 }
48}
49
50impl Sub<SimTime> for SimTime {
51 type Output = Duration;
52
53 fn sub(self, rhs: SimTime) -> Self::Output {
54 match self.0.checked_sub(rhs.0) {
55 Some(val) => Duration(val),
56 None => panic!("attempt to subtract with overflow"),
57 }
58 }
59}
60
61impl Sub<Duration> for SimTime {
62 type Output = SimTime;
63
64 fn sub(self, rhs: Duration) -> Self::Output {
65 match self.0.checked_sub(rhs.0) {
66 Some(val) => SimTime(val),
67 None => panic!("attempt to subtract with overflow"),
68 }
69 }
70}
71
72impl SimTime {
73 pub const fn from_ticks(ticks: TimeTick) -> SimTime {
75 SimTime(ticks)
76 }
77
78 pub const fn zero() -> SimTime {
80 SimTime(0)
81 }
82
83 pub const fn as_time_tick(self) -> TimeTick {
85 self.0
86 }
87
88 pub const fn is_zero(self) -> bool {
90 self.0 == 0
91 }
92
93 pub fn checked_add(self, rhs: Duration) -> Option<SimTime> {
95 self.0.checked_add(rhs.0).map(SimTime)
96 }
97
98 pub fn saturating_add(self, rhs: Duration) -> SimTime {
100 SimTime(self.0.saturating_add(rhs.0))
101 }
102
103 pub fn checked_sub(self, rhs: SimTime) -> Option<Duration> {
105 self.0.checked_sub(rhs.0).map(Duration)
106 }
107
108 pub fn saturating_sub(self, rhs: SimTime) -> Duration {
110 Duration(self.0.saturating_sub(rhs.0))
111 }
112
113 pub fn checked_sub_duration(self, rhs: Duration) -> Option<SimTime> {
115 self.0.checked_sub(rhs.0).map(SimTime)
116 }
117
118 pub fn saturating_sub_duration(self, rhs: Duration) -> SimTime {
120 SimTime(self.0.saturating_sub(rhs.0))
121 }
122}
123
124#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
126pub struct Duration(TimeTick);
127
128impl Display for Duration {
129 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
130 write!(f, "{}", self.0)
131 }
132}
133
134impl From<TimeTick> for Duration {
135 fn from(v: TimeTick) -> Self {
136 Duration(v)
137 }
138}
139
140impl Add<Duration> for Duration {
141 type Output = Duration;
142
143 fn add(self, rhs: Duration) -> Self::Output {
144 match self.0.checked_add(rhs.0) {
145 Some(val) => Duration(val),
146 None => panic!("attempt to add with overflow"),
147 }
148 }
149}
150
151impl AddAssign<Duration> for Duration {
152 fn add_assign(&mut self, rhs: Duration) {
153 match self.0.checked_add(rhs.0) {
154 Some(val) => self.0 = val,
155 None => panic!("attempt to add with overflow"),
156 }
157 }
158}
159
160impl Sub<Duration> for Duration {
161 type Output = Duration;
162
163 fn sub(self, rhs: Duration) -> Self::Output {
164 match self.0.checked_sub(rhs.0) {
165 Some(val) => Duration(val),
166 None => panic!("attempt to subtract with overflow"),
167 }
168 }
169}
170
171impl SubAssign<Duration> for Duration {
172 fn sub_assign(&mut self, rhs: Duration) {
173 match self.0.checked_sub(rhs.0) {
174 Some(val) => self.0 = val,
175 None => panic!("attempt to subtract with overflow"),
176 }
177 }
178}
179
180impl Duration {
181 pub const fn ticks(ticks: TimeTick) -> Duration {
183 Duration(ticks)
184 }
185
186 pub const fn zero() -> Duration {
188 Duration(0)
189 }
190
191 pub const fn one() -> Duration {
193 Duration(1)
194 }
195
196 pub const fn as_time_tick(self) -> TimeTick {
198 self.0
199 }
200
201 pub const fn is_zero(self) -> bool {
203 self.0 == 0
204 }
205
206 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
208 self.0.checked_add(rhs.0).map(Duration)
209 }
210
211 pub fn saturating_add(self, rhs: Duration) -> Duration {
213 Duration(self.0.saturating_add(rhs.0))
214 }
215
216 pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
218 self.0.checked_sub(rhs.0).map(Duration)
219 }
220
221 pub fn saturating_sub(self, rhs: Duration) -> Duration {
223 Duration(self.0.saturating_sub(rhs.0))
224 }
225}
226
227#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
229pub struct TickStatus {
230 current_tick: SimTime,
231 skipped: Duration,
232}
233
234impl TickStatus {
235 pub(crate) fn new(current_tick: SimTime, skipped: Duration) -> Self {
237 TickStatus {
238 current_tick,
239 skipped,
240 }
241 }
242
243 pub(crate) fn initialize() -> Self {
245 TickStatus {
246 current_tick: SimTime::zero(),
247 skipped: Duration::zero(),
248 }
249 }
250
251 pub fn current(&self) -> SimTime {
253 self.current_tick
254 }
255
256 pub fn skipped(&self) -> Duration {
258 self.skipped
259 }
260
261 pub fn previous(&self) -> SimTime {
263 if self.current_tick == SimTime::zero() {
264 self.current_tick
265 } else {
266 self.current_tick - self.skipped - Duration::one()
267 }
268 }
269
270 pub fn is_done_ticks(&self, include_zero_tick: bool, tick_count: TimeTick) -> bool {
281 self.previous().as_time_tick() + if include_zero_tick { 1 } else { 0 } >= tick_count
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn sim_time_creation() {
291 let t = SimTime::from_ticks(10);
292 assert_eq!(t.as_time_tick(), 10);
293 assert!(!t.is_zero());
294
295 let t_zero = SimTime::zero();
296 assert_eq!(t_zero.as_time_tick(), 0);
297 assert!(t_zero.is_zero());
298 }
299
300 #[test]
301 fn sim_time_from_tick() {
302 let t = SimTime::from(5);
303 assert_eq!(t.as_time_tick(), 5);
304 }
305
306 #[test]
307 fn sim_time_from_usize_conversion() {
308 let val: usize = 123;
309 let sim_time = SimTime::from(val);
310 assert_eq!(sim_time.as_time_tick(), val);
311 }
312
313 #[test]
314 fn sim_time_display() {
315 let t = SimTime::from_ticks(100);
316 assert_eq!(format!("{}", t), "100");
317 }
318
319 #[test]
320 fn sim_time_add_duration() {
321 let t1 = SimTime::from_ticks(10);
322 let d = Duration::ticks(5);
323 let t2 = t1 + d;
324 assert_eq!(t2.as_time_tick(), 15);
325 }
326
327 #[test]
328 fn sim_time_add_assign_duration() {
329 let mut t = SimTime::from_ticks(10);
330 let d = Duration::ticks(5);
331 t += d;
332 assert_eq!(t.as_time_tick(), 15);
333 }
334
335 #[test]
336 fn sim_time_sub_sim_time() {
337 let t1 = SimTime::from_ticks(10);
338 let t2 = SimTime::from_ticks(5);
339 let d = t1 - t2;
340 assert_eq!(d.as_time_tick(), 5);
341 }
342
343 #[test]
344 fn sim_time_sub_duration() {
345 let t = SimTime::from_ticks(10);
346 let d = Duration::ticks(5);
347 let t_new = t - d;
348 assert_eq!(t_new.as_time_tick(), 5);
349 }
350
351 #[test]
352 fn sim_time_checked_add() {
353 let t = SimTime::from_ticks(10);
354 let d = Duration::ticks(5);
355 assert_eq!(t.checked_add(d), Some(SimTime::from_ticks(15)));
356 let max_tick = TimeTick::MAX - 1;
357 let t_max = SimTime::from_ticks(max_tick);
358 let d_one = Duration::one();
359 assert_eq!(
360 t_max.checked_add(d_one),
361 Some(SimTime::from_ticks(TimeTick::MAX))
362 );
363 assert_eq!(t_max.checked_add(Duration::ticks(2)), None);
364 }
365
366 #[test]
367 fn sim_time_saturating_add() {
368 let t = SimTime::from_ticks(10);
369 let d = Duration::ticks(5);
370 assert_eq!(t.saturating_add(d), SimTime::from_ticks(15));
371 let max_tick = TimeTick::MAX - 1;
372 let t_max = SimTime::from_ticks(max_tick);
373 let d_two = Duration::ticks(2);
374 assert_eq!(
375 t_max.saturating_add(d_two),
376 SimTime::from_ticks(TimeTick::MAX)
377 );
378 }
379
380 #[test]
381 fn sim_time_checked_sub() {
382 let t1 = SimTime::from_ticks(10);
383 let t2 = SimTime::from_ticks(5);
384 assert_eq!(t1.checked_sub(t2), Some(Duration::ticks(5)));
385 let t_zero = SimTime::zero();
386 assert_eq!(t1.checked_sub(SimTime::from_ticks(15)), None);
387 assert_eq!(t_zero.checked_sub(SimTime::from_ticks(1)), None);
388 }
389
390 #[test]
391 fn sim_time_saturating_sub() {
392 let t1 = SimTime::from_ticks(10);
393 let t2 = SimTime::from_ticks(5);
394 assert_eq!(t1.saturating_sub(t2), Duration::ticks(5));
395 assert_eq!(t1.saturating_sub(SimTime::from_ticks(15)), Duration::zero());
396 }
397
398 #[test]
399 fn sim_time_checked_sub_duration() {
400 let t = SimTime::from_ticks(10);
401 let d = Duration::ticks(5);
402 assert_eq!(t.checked_sub_duration(d), Some(SimTime::from_ticks(5)));
403 assert_eq!(t.checked_sub_duration(Duration::ticks(15)), None);
404 }
405
406 #[test]
407 fn sim_time_saturating_sub_duration() {
408 let t = SimTime::from_ticks(10);
409 let d = Duration::ticks(5);
410 assert_eq!(t.saturating_sub_duration(d), SimTime::from_ticks(5));
411 assert_eq!(
412 t.saturating_sub_duration(Duration::ticks(15)),
413 SimTime::zero()
414 );
415 }
416
417 #[test]
418 fn sim_time_add_zero_duration() {
419 let t = SimTime::from_ticks(100);
420 let d_zero = Duration::zero();
421 assert_eq!(t + d_zero, t);
422 }
423
424 #[test]
425 fn sim_time_sub_zero_duration() {
426 let t = SimTime::from_ticks(100);
427 let d_zero = Duration::zero();
428 assert_eq!(t - d_zero, t);
429 }
430
431 #[test]
432 fn sim_time_max_value_subtraction() {
433 let max_sim_time = SimTime::from_ticks(TimeTick::MAX);
434 let one_duration = Duration::one();
435 let expected_sim_time = SimTime::from_ticks(TimeTick::MAX - 1);
436 assert_eq!(max_sim_time - one_duration, expected_sim_time);
437 }
438
439 #[test]
440 fn sim_time_comparison() {
441 let t1 = SimTime::from_ticks(10);
442 let t2 = SimTime::from_ticks(20);
443 let t3 = SimTime::from_ticks(10);
444
445 assert!(t1 < t2);
446 assert!(t2 > t1);
447 assert!(t1 <= t3);
448 assert!(t1 >= t3);
449 assert_eq!(t1, t3);
450 assert_ne!(t1, t2);
451 }
452
453 #[test]
454 fn duration_creation() {
455 let d = Duration::ticks(10);
456 assert_eq!(d.as_time_tick(), 10);
457 assert!(!d.is_zero());
458
459 let d_zero = Duration::zero();
460 assert_eq!(d_zero.as_time_tick(), 0);
461 assert!(d_zero.is_zero());
462
463 let d_one = Duration::one();
464 assert_eq!(d_one.as_time_tick(), 1);
465 }
466
467 #[test]
468 fn duration_from_tick() {
469 let d = Duration::from(5);
470 assert_eq!(d.as_time_tick(), 5);
471 }
472
473 #[test]
474 fn duration_from_usize_conversion() {
475 let val: usize = 456;
476 let duration = Duration::from(val);
477 assert_eq!(duration.as_time_tick(), val);
478 }
479
480 #[test]
481 fn duration_display() {
482 let d = Duration::ticks(100);
483 assert_eq!(format!("{}", d), "100");
484 }
485
486 #[test]
487 fn duration_add_duration() {
488 let d1 = Duration::ticks(10);
489 let d2 = Duration::ticks(5);
490 let d3 = d1 + d2;
491 assert_eq!(d3.as_time_tick(), 15);
492 }
493
494 #[test]
495 fn duration_add_assign_duration() {
496 let mut d = Duration::ticks(10);
497 let d_add = Duration::ticks(5);
498 d += d_add;
499 assert_eq!(d.as_time_tick(), 15);
500 }
501
502 #[test]
503 fn duration_sub_duration() {
504 let d1 = Duration::ticks(10);
505 let d2 = Duration::ticks(5);
506 let d3 = d1 - d2;
507 assert_eq!(d3.as_time_tick(), 5);
508 }
509
510 #[test]
511 fn duration_sub_assign_duration() {
512 let mut d = Duration::ticks(10);
513 let d_sub = Duration::ticks(5);
514 d -= d_sub;
515 assert_eq!(d.as_time_tick(), 5);
516 }
517
518 #[test]
519 fn duration_checked_add() {
520 let d1 = Duration::ticks(10);
521 let d2 = Duration::ticks(5);
522 assert_eq!(d1.checked_add(d2), Some(Duration::ticks(15)));
523 let max_tick = TimeTick::MAX - 1;
524 let d_max = Duration::ticks(max_tick);
525 let d_one = Duration::one();
526 assert_eq!(
527 d_max.checked_add(d_one),
528 Some(Duration::ticks(TimeTick::MAX))
529 );
530 assert_eq!(d_max.checked_add(Duration::ticks(2)), None);
531 }
532
533 #[test]
534 fn duration_saturating_add() {
535 let d1 = Duration::ticks(10);
536 let d2 = Duration::ticks(5);
537 assert_eq!(d1.saturating_add(d2), Duration::ticks(15));
538 let max_tick = TimeTick::MAX - 1;
539 let d_max = Duration::ticks(max_tick);
540 let d_two = Duration::ticks(2);
541 assert_eq!(d_max.saturating_add(d_two), Duration::ticks(TimeTick::MAX));
542 }
543
544 #[test]
545 fn duration_checked_sub() {
546 let d1 = Duration::ticks(10);
547 let d2 = Duration::ticks(5);
548 assert_eq!(d1.checked_sub(d2), Some(Duration::ticks(5)));
549 assert_eq!(d1.checked_sub(Duration::ticks(15)), None);
550 }
551
552 #[test]
553 fn duration_saturating_sub() {
554 let d1 = Duration::ticks(10);
555 let d2 = Duration::ticks(5);
556 assert_eq!(d1.saturating_sub(d2), Duration::ticks(5));
557 assert_eq!(d1.saturating_sub(Duration::ticks(15)), Duration::zero());
558 }
559
560 #[test]
561 fn duration_add_zero_duration() {
562 let d = Duration::ticks(100);
563 let d_zero = Duration::zero();
564 assert_eq!(d + d_zero, d);
565 }
566
567 #[test]
568 fn duration_sub_zero_duration() {
569 let d = Duration::ticks(100);
570 let d_zero = Duration::zero();
571 assert_eq!(d - d_zero, d);
572 }
573
574 #[test]
575 fn duration_max_value_subtraction() {
576 let max_duration = Duration::ticks(TimeTick::MAX);
577 let one_duration = Duration::one();
578 let expected_duration = Duration::ticks(TimeTick::MAX - 1);
579 assert_eq!(max_duration - one_duration, expected_duration);
580 }
581
582 #[test]
583 fn duration_comparison() {
584 let d1 = Duration::ticks(10);
585 let d2 = Duration::ticks(20);
586 let d3 = Duration::ticks(10);
587
588 assert!(d1 < d2);
589 assert!(d2 > d1);
590 assert!(d1 <= d3);
591 assert!(d1 >= d3);
592 assert_eq!(d1, d3);
593 assert_ne!(d1, d2);
594 }
595
596 #[test]
597 fn tick_status_initialize() {
598 let status = TickStatus::initialize();
599 assert_eq!(status.current(), SimTime::zero());
600 assert_eq!(status.skipped(), Duration::zero());
601 assert_eq!(status.previous(), SimTime::zero());
602 }
603
604 #[test]
605 fn tick_status_new() {
606 let current = SimTime::from_ticks(10);
607 let skipped = Duration::ticks(2);
608 let status = TickStatus::new(current, skipped);
609 assert_eq!(status.current(), current);
610 assert_eq!(status.skipped(), skipped);
611 }
612
613 #[test]
614 fn tick_status_previous() {
615 let status = TickStatus::initialize();
617 assert_eq!(status.previous(), SimTime::zero());
618
619 let status = TickStatus::new(SimTime::from_ticks(5), Duration::zero());
621 assert_eq!(status.previous(), SimTime::from_ticks(4));
622
623 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(2));
625 assert_eq!(status.previous(), SimTime::from_ticks(2));
626 }
627
628 #[test]
629 fn tick_status_is_done_ticks_no_skip() {
630 let status = TickStatus::initialize();
633 assert!(!status.is_done_ticks(true, 2));
634
635 let status = TickStatus::new(SimTime::from_ticks(2), Duration::zero());
637 assert!(status.is_done_ticks(true, 2));
638
639 let status = TickStatus::initialize();
642 assert!(!status.is_done_ticks(false, 2));
643
644 let status = TickStatus::new(SimTime::from_ticks(2), Duration::zero());
646 assert!(!status.is_done_ticks(false, 2));
647
648 let status = TickStatus::new(SimTime::from_ticks(3), Duration::zero());
650 assert!(status.is_done_ticks(false, 2));
651 }
652
653 #[test]
654 fn tick_status_is_done_ticks_with_skip() {
655 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(2));
659 assert!(status.is_done_ticks(true, 2));
660
661 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(2));
664 assert!(status.is_done_ticks(true, 3));
665
666 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(2));
669 assert!(!status.is_done_ticks(true, 4));
670
671 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(2));
675 assert!(status.is_done_ticks(false, 2));
676
677 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(2));
680 assert!(!status.is_done_ticks(false, 3));
681 }
682
683 #[test]
684 fn tick_status_previous_large_current_tick() {
685 let current = SimTime::from_ticks(TimeTick::MAX);
686 let skipped = Duration::ticks(100);
687 let status = TickStatus::new(current, skipped);
688 assert_eq!(
689 status.previous(),
690 SimTime::from_ticks(TimeTick::MAX - 100 - 1)
691 );
692 }
693
694 #[test]
695 fn tick_status_is_done_ticks_edge_cases() {
696 let status = TickStatus::initialize();
698 assert!(status.is_done_ticks(true, 0));
699 assert!(status.is_done_ticks(false, 0));
700
701 let status = TickStatus::initialize();
704 assert!(status.is_done_ticks(true, 1));
705
706 let status = TickStatus::initialize();
709 assert!(!status.is_done_ticks(false, 1));
710
711 let status = TickStatus::new(SimTime::from_ticks(1), Duration::zero());
715 assert!(!status.is_done_ticks(false, 1));
716
717 let status = TickStatus::new(SimTime::from_ticks(2), Duration::zero());
721 assert!(status.is_done_ticks(false, 1));
722 }
723
724 #[test]
725 fn tick_status_previous_with_skipped_equal_to_current_minus_one() {
726 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(4));
728 assert_eq!(status.previous(), SimTime::zero());
729 }
730
731 #[test]
732 #[should_panic(expected = "attempt to subtract with overflow")]
733 fn tick_status_previous_with_skipped_greater_than_current_minus_one() {
734 let status = TickStatus::new(SimTime::from_ticks(5), Duration::ticks(5));
736 status.previous();
737 }
738
739 #[test]
740 fn sim_time_saturating_add_boundary() {
741 let max_sim_time = SimTime::from_ticks(TimeTick::MAX);
742 let d_one = Duration::one();
743 assert_eq!(max_sim_time.saturating_add(d_one), max_sim_time);
744
745 let d_max = Duration::ticks(TimeTick::MAX);
746 assert_eq!(max_sim_time.saturating_add(d_max), max_sim_time);
747 }
748
749 #[test]
750 fn sim_time_saturating_sub_boundary() {
751 let t_zero = SimTime::zero();
752 let t_five = SimTime::from_ticks(5);
753 let d_five = Duration::ticks(5);
754
755 assert_eq!(t_zero.saturating_sub(t_five), Duration::zero());
756 assert_eq!(t_zero.saturating_sub_duration(d_five), SimTime::zero());
757
758 let t_three = SimTime::from_ticks(3);
759 assert_eq!(t_three.saturating_sub(t_five), Duration::zero());
760 assert_eq!(t_three.saturating_sub_duration(d_five), SimTime::zero());
761 }
762
763 #[test]
764 fn sim_time_checked_sub_duration_underflow() {
765 let t_three = SimTime::from_ticks(3);
766 let d_five = Duration::ticks(5);
767 assert_eq!(t_three.checked_sub_duration(d_five), None);
768
769 let t_zero = SimTime::zero();
770 let d_one = Duration::one();
771 assert_eq!(t_zero.checked_sub_duration(d_one), None);
772 }
773
774 #[test]
775 #[should_panic(expected = "attempt to subtract with overflow")]
776 fn sim_time_sub_operator_underflow_panic() {
777 let t_three = SimTime::from_ticks(3);
778 let d_five = Duration::ticks(5);
779 let _ = t_three - d_five;
780 }
781}