1use crate::Serialize;
2use bytes::BufMut;
3use core::{fmt::Display, time::Duration};
4
5pub const TIME_UNITS_PER_HOUR: u32 = 1 << 31;
7
8const NANOS_PER_HOUR: u64 = 3_600_000_000_000;
10
11#[allow(clippy::cast_precision_loss)]
13const NANOS_PER_TIME_UNIT: f64 = NANOS_PER_HOUR as f64 / TIME_UNITS_PER_HOUR as f64;
14
15const MAX_NANOS: u64 = 3_599_999_998_324; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum Timestamp {
23 Relative(TimeUnits),
27 Absolute(TimeUnits),
31}
32
33impl Timestamp {
34 pub const RELATIVE_BIT: u32 = 0b0;
36
37 pub const ABSOLUTE_BIT: u32 = 0b1;
39
40 #[inline]
53 #[must_use]
54 pub const fn new(value: u32) -> Self {
55 #[allow(unsafe_code)]
57 let time_units = unsafe { TimeUnits::new_unchecked(value >> 1) };
58 let is_relative = (value & 1) == Self::RELATIVE_BIT;
59
60 if is_relative {
61 Self::Relative(time_units)
62 } else {
63 Self::Absolute(time_units)
64 }
65 }
66
67 #[inline]
80 #[must_use]
81 pub const fn time_units(self) -> TimeUnits {
82 match self {
83 Self::Absolute(time_units) | Self::Relative(time_units) => time_units,
84 }
85 }
86
87 #[inline]
97 #[must_use]
98 pub const fn is_relative(self) -> bool {
99 matches!(self, Self::Relative(_))
100 }
101
102 #[inline]
112 #[must_use]
113 pub const fn is_absolute(self) -> bool {
114 matches!(self, Self::Absolute(_))
115 }
116
117 #[inline]
130 #[must_use]
131 pub const fn to_u32(self) -> u32 {
132 let time_units = self.time_units().inner();
133 let bit = match self {
134 Self::Relative(_) => Self::RELATIVE_BIT,
135 Self::Absolute(_) => Self::ABSOLUTE_BIT,
136 };
137
138 (time_units << 1) | bit
139 }
140
141 #[inline]
155 #[must_use]
156 pub fn to_duration(self) -> Duration {
157 self.time_units().to_duration()
160 }
161}
162
163impl Serialize for Timestamp {
164 #[inline]
165 fn serialize(&self, buf: &mut bytes::BytesMut) -> u16 {
166 buf.put_u32(self.to_u32());
167 4
168 }
169}
170
171impl Default for Timestamp {
172 #[inline]
173 fn default() -> Self {
174 Self::Relative(TimeUnits::default())
175 }
176}
177
178impl From<u32> for Timestamp {
179 #[inline]
180 fn from(value: u32) -> Self {
181 Self::new(value)
182 }
183}
184
185impl From<Timestamp> for u32 {
186 #[inline]
187 fn from(value: Timestamp) -> Self {
188 value.to_u32()
189 }
190}
191
192impl From<Timestamp> for Duration {
193 #[inline]
194 fn from(value: Timestamp) -> Self {
195 value.to_duration()
196 }
197}
198
199impl PartialEq<u32> for Timestamp {
200 #[inline]
201 fn eq(&self, other: &u32) -> bool {
202 self.to_u32().eq(other)
203 }
204}
205
206impl PartialEq<Timestamp> for u32 {
207 #[inline]
208 fn eq(&self, other: &Timestamp) -> bool {
209 self.eq(&other.to_u32())
210 }
211}
212
213impl PartialOrd<u32> for Timestamp {
214 #[inline]
215 fn partial_cmp(&self, other: &u32) -> Option<core::cmp::Ordering> {
216 self.to_u32().partial_cmp(other)
217 }
218}
219
220impl PartialOrd<Timestamp> for u32 {
221 #[inline]
222 fn partial_cmp(&self, other: &Timestamp) -> Option<core::cmp::Ordering> {
223 self.partial_cmp(&other.to_u32())
224 }
225}
226
227#[cfg(feature = "serde")]
228mod serde {
229 use super::Timestamp;
230 use serde::{Deserialize, Deserializer, Serialize, Serializer};
231
232 impl Serialize for Timestamp {
233 #[inline]
234 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
235 self.to_u32().serialize(serializer)
236 }
237 }
238
239 impl<'de> Deserialize<'de> for Timestamp {
240 #[inline]
241 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
242 u32::deserialize(deserializer).map(Self::new)
243 }
244 }
245}
246
247#[repr(transparent)]
253#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
254pub struct TimeUnits(u32);
255
256impl TimeUnits {
257 pub const ZERO: Self = Self(0);
259
260 pub const MAX: Self = Self(TIME_UNITS_PER_HOUR - 1);
262
263 #[inline]
281 #[must_use]
282 pub const fn new(value: u32) -> Option<Self> {
283 if value < TIME_UNITS_PER_HOUR {
284 Some(Self(value))
285 } else {
286 None
287 }
288 }
289
290 #[inline]
307 #[must_use]
308 #[allow(unsafe_code)]
309 pub const unsafe fn new_unchecked(value: u32) -> Self {
310 Self(value)
311 }
312
313 #[inline]
333 #[must_use]
334 pub fn from_duration(duration: Duration) -> Option<Self> {
335 let nanos = duration.as_nanos();
338
339 if nanos <= u128::from(MAX_NANOS) {
340 let time_units = crate::math::round((nanos as f64) / NANOS_PER_TIME_UNIT) as u32;
341 Some(Self(time_units))
342 } else {
343 None
344 }
345 }
346
347 #[inline]
361 #[must_use]
362 pub const fn inner(self) -> u32 {
363 self.0
364 }
365
366 #[inline]
377 #[must_use]
378 pub const fn is_zero(self) -> bool {
379 self.0 == Self::ZERO.0
380 }
381
382 #[inline]
393 #[must_use]
394 pub const fn is_max(self) -> bool {
395 self.0 == Self::MAX.0
396 }
397
398 #[inline]
412 #[must_use]
413 pub fn to_duration(self) -> Duration {
414 let nanos = crate::math::round(f64::from(self.0) * NANOS_PER_TIME_UNIT) as u64;
417 Duration::from_nanos(nanos)
418 }
419}
420
421impl Default for TimeUnits {
428 #[inline]
429 fn default() -> Self {
430 Self::ZERO
431 }
432}
433
434impl Display for TimeUnits {
435 #[inline]
436 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
437 self.0.fmt(f)
438 }
439}
440
441impl From<TimeUnits> for u32 {
442 #[inline]
443 fn from(value: TimeUnits) -> Self {
444 value.inner()
445 }
446}
447
448impl From<TimeUnits> for Duration {
449 #[inline]
450 fn from(value: TimeUnits) -> Self {
451 value.to_duration()
452 }
453}
454
455impl PartialEq<u32> for TimeUnits {
456 #[inline]
457 fn eq(&self, other: &u32) -> bool {
458 self.0.eq(other)
459 }
460}
461
462impl PartialEq<TimeUnits> for u32 {
463 #[inline]
464 fn eq(&self, other: &TimeUnits) -> bool {
465 self.eq(&other.0)
466 }
467}
468
469impl PartialOrd<u32> for TimeUnits {
470 #[inline]
471 fn partial_cmp(&self, other: &u32) -> Option<core::cmp::Ordering> {
472 self.0.partial_cmp(other)
473 }
474}
475
476impl PartialOrd<TimeUnits> for u32 {
477 #[inline]
478 fn partial_cmp(&self, other: &TimeUnits) -> Option<core::cmp::Ordering> {
479 self.partial_cmp(&other.0)
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486 use bytes::BytesMut;
487 use core::cmp::Ordering;
488 use rstest::rstest;
489
490 #[rstest]
491 #[case(Timestamp::Relative(TimeUnits::ZERO), [0x00, 0x00, 0x00, 0x00])]
492 #[case(Timestamp::Relative(TimeUnits::new(152_709_948).unwrap()), [0x12, 0x34, 0x56, 0x78])]
493 #[case(Timestamp::Relative(TimeUnits::MAX), [0xff, 0xff, 0xff, 0xfe])]
494 #[case(Timestamp::Absolute(TimeUnits::ZERO), [0x00, 0x00, 0x00, 0x01])]
495 #[case(Timestamp::Absolute(TimeUnits::new(152_709_948).unwrap()), [0x12, 0x34, 0x56, 0x79])]
496 #[case(Timestamp::Absolute(TimeUnits::MAX), [0xff, 0xff, 0xff, 0xff])]
497 fn timestamp_parse_and_serialize_roundtrip(
498 #[case] timestamp: Timestamp,
499 #[case] expected: [u8; core::mem::size_of::<u32>()],
500 ) {
501 let (slice, timestamp_parse) =
502 nom::number::complete::be_u32::<&[u8], nom::error::Error<&[u8]>>(expected.as_slice())
503 .map(|(slice, timestamp)| (slice, Timestamp::new(timestamp)))
504 .unwrap();
505 assert!(slice.is_empty());
506 assert_eq!(timestamp_parse, timestamp);
507
508 let mut buf = BytesMut::with_capacity(expected.len());
509 timestamp.serialize(&mut buf);
510 assert_eq!(buf.len(), expected.len());
511 assert_eq!(buf, expected.as_slice());
512 }
513
514 #[rstest]
515 #[case(Timestamp::Relative(TimeUnits::ZERO), 0)]
516 #[case(Timestamp::Relative(TimeUnits::new(123_456_789).unwrap()), 246_913_578)]
517 #[case(Timestamp::Relative(TimeUnits::MAX), 4_294_967_294)]
518 #[case(Timestamp::Absolute(TimeUnits::ZERO), 1)]
519 #[case(Timestamp::Absolute(TimeUnits::new(123_456_789).unwrap()), 246_913_579)]
520 #[case(Timestamp::Absolute(TimeUnits::MAX), 4_294_967_295)]
521 fn timestamp_u32_roundtrip(#[case] timestamp: Timestamp, #[case] expected: u32) {
522 assert_eq!(Timestamp::new(expected), timestamp);
523 assert_eq!(Timestamp::from(expected), timestamp);
524 assert_eq!(timestamp.to_u32(), expected);
525 assert_eq!(u32::from(timestamp), expected);
526 }
527
528 #[rstest]
529 #[case(Timestamp::Relative(TimeUnits::ZERO), Duration::ZERO)]
530 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), Duration::from_mins(30))]
531 #[case(
532 Timestamp::Relative(TimeUnits::MAX),
533 Duration::from_nanos(3_599_999_998_324)
534 )]
535 #[case(Timestamp::Absolute(TimeUnits::ZERO), Duration::ZERO)]
536 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), Duration::from_mins(30))]
537 #[case(
538 Timestamp::Absolute(TimeUnits::MAX),
539 Duration::from_nanos(3_599_999_998_324)
540 )]
541 fn timestamp_duration_consistency(#[case] timestamp: Timestamp, #[case] duration: Duration) {
542 assert_eq!(timestamp.to_duration(), duration);
543 assert_eq!(Duration::from(timestamp), duration);
544 }
545
546 #[rstest]
547 #[case(Timestamp::Relative(TimeUnits::ZERO))]
548 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()))]
549 #[case(Timestamp::Relative(TimeUnits::MAX))]
550 #[case(Timestamp::Absolute(TimeUnits::ZERO))]
551 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()))]
552 #[case(Timestamp::Absolute(TimeUnits::MAX))]
553 fn timestamp_time_units_consistency(#[case] timestamp: Timestamp) {
554 assert_eq!(
555 timestamp.to_duration(),
556 timestamp.time_units().to_duration()
557 );
558 }
559
560 #[rstest]
561 #[case(Timestamp::Relative(TimeUnits::ZERO))]
562 #[case(Timestamp::Relative(TimeUnits::MAX))]
563 fn timestamp_is_relative(#[case] timestamp: Timestamp) {
564 assert!(timestamp.is_relative());
565 assert!(!timestamp.is_absolute());
566 }
567
568 #[rstest]
569 #[case(Timestamp::Absolute(TimeUnits::ZERO))]
570 #[case(Timestamp::Absolute(TimeUnits::MAX))]
571 fn timestamp_is_absolute(#[case] timestamp: Timestamp) {
572 assert!(!timestamp.is_relative());
573 assert!(timestamp.is_absolute());
574 }
575
576 #[test]
577 fn timestamp_default() {
578 assert_eq!(Timestamp::default(), Timestamp::Relative(TimeUnits::ZERO));
579 }
580
581 #[rstest]
582 #[case(Timestamp::Relative(TimeUnits::ZERO), 0, true)]
583 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_648, true)]
584 #[case(Timestamp::Relative(TimeUnits::MAX), 4_294_967_294, true)]
585 #[case(Timestamp::Relative(TimeUnits::ZERO), 1, false)]
586 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_649, false)]
587 #[case(Timestamp::Relative(TimeUnits::MAX), 4_294_967_295, false)]
588 #[case(Timestamp::Absolute(TimeUnits::ZERO), 1, true)]
589 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_649, true)]
590 #[case(Timestamp::Absolute(TimeUnits::MAX), 4_294_967_295, true)]
591 #[case(Timestamp::Absolute(TimeUnits::ZERO), 0, false)]
592 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_648, false)]
593 #[case(Timestamp::Absolute(TimeUnits::MAX), 4_294_967_294, false)]
594 fn timestamp_partial_eq(
595 #[case] timestamp: Timestamp,
596 #[case] value: u32,
597 #[case] expected: bool,
598 ) {
599 assert_eq!(timestamp == value, expected);
600 assert_eq!(value == timestamp, expected);
601 }
602
603 #[rstest]
604 #[case(Timestamp::Relative(TimeUnits::ZERO), 0, Ordering::Equal)]
605 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_648, Ordering::Equal)]
606 #[case(Timestamp::Relative(TimeUnits::MAX), 4_294_967_294, Ordering::Equal)]
607 #[case(Timestamp::Relative(TimeUnits::ZERO), 4_294_967_294, Ordering::Less)]
608 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_647, Ordering::Greater)]
609 #[case(Timestamp::Relative(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_649, Ordering::Less)]
610 #[case(Timestamp::Relative(TimeUnits::MAX), 0, Ordering::Greater)]
611 #[case(Timestamp::Absolute(TimeUnits::ZERO), 1, Ordering::Equal)]
612 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_649, Ordering::Equal)]
613 #[case(Timestamp::Absolute(TimeUnits::MAX), 4_294_967_295, Ordering::Equal)]
614 #[case(Timestamp::Absolute(TimeUnits::ZERO), 4_294_967_295, Ordering::Less)]
615 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_648, Ordering::Greater)]
616 #[case(Timestamp::Absolute(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap()), 2_147_483_650, Ordering::Less)]
617 #[case(Timestamp::Absolute(TimeUnits::MAX), 0, Ordering::Greater)]
618 fn timestamp_partial_ord(
619 #[case] timestamp: Timestamp,
620 #[case] value: u32,
621 #[case] expected: Ordering,
622 ) {
623 let expected = Some(expected);
624
625 assert_eq!(timestamp.partial_cmp(&value), expected);
626 assert_eq!(
627 value.partial_cmp(×tamp),
628 expected.map(Ordering::reverse)
629 );
630 }
631
632 #[rstest]
633 #[case(Timestamp::Relative(TimeUnits::ZERO), 1, Timestamp::Relative(TimeUnits::new(1).unwrap()))]
634 #[case(
635 Timestamp::Relative(TimeUnits::ZERO),
636 2_147_483_648,
637 Timestamp::Relative(TimeUnits::MAX)
638 )]
639 #[case(Timestamp::Relative(TimeUnits::new(2_147_483_646).unwrap()), 4_294_967_293, Timestamp::Relative(TimeUnits::MAX))]
640 #[case(Timestamp::Absolute(TimeUnits::ZERO), 2, Timestamp::Absolute(TimeUnits::new(1).unwrap()))]
641 #[case(
642 Timestamp::Absolute(TimeUnits::ZERO),
643 2_147_483_648,
644 Timestamp::Absolute(TimeUnits::MAX)
645 )]
646 #[case(Timestamp::Absolute(TimeUnits::new(2_147_483_646).unwrap()), 4_294_967_294, Timestamp::Absolute(TimeUnits::MAX))]
647 fn timestamp_partial_ord_transitive(
648 #[case] a: Timestamp,
649 #[case] b: u32,
650 #[case] c: Timestamp,
651 ) {
652 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
653 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
654 assert_eq!(b.partial_cmp(&c), Some(Ordering::Less));
655 assert_eq!(c.partial_cmp(&b), Some(Ordering::Greater));
656 assert_eq!(a.partial_cmp(&c.to_u32()), Some(Ordering::Less));
657 assert_eq!(c.partial_cmp(&a.to_u32()), Some(Ordering::Greater));
658 }
659
660 #[cfg(feature = "serde")]
661 #[rstest]
662 #[case(Timestamp::Relative(TimeUnits::ZERO), 0)]
663 #[case(Timestamp::Relative(TimeUnits::new(123_456_789).unwrap()), 246_913_578)]
664 #[case(Timestamp::Relative(TimeUnits::MAX), 4_294_967_294)]
665 #[case(Timestamp::Absolute(TimeUnits::ZERO), 1)]
666 #[case(Timestamp::Absolute(TimeUnits::new(123_456_789).unwrap()), 246_913_579)]
667 #[case(Timestamp::Absolute(TimeUnits::MAX), 4_294_967_295)]
668 fn timestamp_serde_roundtrip(#[case] timestamp: Timestamp, #[case] expected: u32) {
669 let expected = serde_json::Value::Number(serde_json::Number::from(expected));
670
671 let timestamp_de = serde_json::from_value::<Timestamp>(expected.clone()).unwrap();
672 assert_eq!(timestamp_de, timestamp);
673
674 let timestamp_ser = serde_json::to_value(timestamp).unwrap();
675 assert_eq!(timestamp_ser, expected);
676 }
677
678 #[rstest]
679 #[case(TimeUnits::ZERO, Duration::ZERO)]
680 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), Duration::from_mins(30))]
681 #[case(TimeUnits::MAX, Duration::from_nanos(3_599_999_998_324))]
682 fn time_units_duration_consistency(#[case] time_units: TimeUnits, #[case] duration: Duration) {
683 assert_eq!(TimeUnits::from_duration(duration).unwrap(), time_units);
684 assert_eq!(Duration::from(time_units), duration);
685 assert_eq!(time_units.to_duration(), duration);
686 }
687
688 #[rstest]
689 #[case(TimeUnits::ZERO, 0)]
690 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), TIME_UNITS_PER_HOUR / 2)]
691 #[case(TimeUnits::MAX, 2_147_483_647)]
692 fn time_units_inner_value(#[case] time_units: TimeUnits, #[case] value: u32) {
693 assert_eq!(time_units.inner(), value);
694 assert_eq!(u32::from(time_units), value);
695 }
696
697 #[rstest]
698 #[case(TimeUnits::ZERO.inner())]
699 #[case(TIME_UNITS_PER_HOUR / 2)]
700 #[case(TimeUnits::MAX.inner())]
701 fn time_units_ok(#[case] value: u32) {
702 let time_units = TimeUnits::new(value);
703 assert!(time_units.is_some());
704 assert_eq!(time_units.unwrap().inner(), value);
705 }
706
707 #[rstest]
708 #[case(TimeUnits::MAX.inner() + 1)]
709 #[case(TIME_UNITS_PER_HOUR)]
710 #[case(u32::MAX)]
711 fn time_units_err(#[case] value: u32) {
712 assert!(TimeUnits::new(value).is_none());
713 }
714
715 #[rstest]
716 fn time_units_is_zero() {
717 assert!(TimeUnits::ZERO.is_zero());
718 assert!(!TimeUnits::MAX.is_zero());
719 assert!(!TimeUnits::new(1).unwrap().is_zero());
720 }
721
722 #[rstest]
723 fn time_units_is_max() {
724 assert!(!TimeUnits::ZERO.is_max());
725 assert!(TimeUnits::MAX.is_max());
726 assert!(!TimeUnits::new(2_147_483_646).unwrap().is_max());
727 }
728
729 #[test]
730 fn time_units_default() {
731 assert_eq!(TimeUnits::default(), TimeUnits::ZERO);
732 }
733
734 #[rstest]
735 #[case(TimeUnits::ZERO, "0")]
736 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), "1073741824")]
737 #[case(TimeUnits::MAX, "2147483647")]
738 fn time_units_display(#[case] time_units: TimeUnits, #[case] expected: &str) {
739 use alloc::string::ToString;
740 assert_eq!(time_units.to_string(), expected);
741 }
742
743 #[rstest]
744 #[case(TimeUnits::ZERO, 0, true)]
745 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), 1_073_741_824, true)]
746 #[case(TimeUnits::MAX, 2_147_483_647, true)]
747 #[case(TimeUnits::ZERO, 1, false)]
748 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), 1_073_741_825, false)]
749 #[case(TimeUnits::MAX, 2_147_483_646, false)]
750 fn time_units_partial_eq(
751 #[case] time_units: TimeUnits,
752 #[case] value: u32,
753 #[case] expected: bool,
754 ) {
755 assert_eq!(time_units == value, expected);
756 assert_eq!(value == time_units, expected);
757 }
758
759 #[rstest]
760 #[case(TimeUnits::ZERO, 0, Ordering::Equal)]
761 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), 1_073_741_824, Ordering::Equal)]
762 #[case(TimeUnits::MAX, 2_147_483_647, Ordering::Equal)]
763 #[case(TimeUnits::ZERO, 2_147_483_647, Ordering::Less)]
764 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), 1_073_741_823, Ordering::Greater)]
765 #[case(TimeUnits::new(TIME_UNITS_PER_HOUR / 2).unwrap(), 1_073_741_825, Ordering::Less)]
766 #[case(TimeUnits::MAX, 0, Ordering::Greater)]
767 fn time_units_partial_ord(
768 #[case] time_units: TimeUnits,
769 #[case] value: u32,
770 #[case] expected: Ordering,
771 ) {
772 let expected = Some(expected);
773
774 assert_eq!(time_units.partial_cmp(&value), expected);
775 assert_eq!(
776 value.partial_cmp(&time_units),
777 expected.map(Ordering::reverse)
778 );
779 }
780
781 #[rstest]
782 #[case(TimeUnits::ZERO, 1, TimeUnits::new(2).unwrap())]
783 #[case(TimeUnits::ZERO, 1_073_741_824, TimeUnits::MAX)]
784 #[case(TimeUnits::new(2_147_483_645).unwrap(), 2_147_483_646, TimeUnits::MAX)]
785 fn time_units_partial_ord_transitive(
786 #[case] a: TimeUnits,
787 #[case] b: u32,
788 #[case] c: TimeUnits,
789 ) {
790 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
791 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
792 assert_eq!(b.partial_cmp(&c), Some(Ordering::Less));
793 assert_eq!(c.partial_cmp(&b), Some(Ordering::Greater));
794 assert_eq!(a.partial_cmp(&c), Some(Ordering::Less));
795 assert_eq!(c.partial_cmp(&a), Some(Ordering::Greater));
796 }
797}