grafeo_common/types/
validity.rs1use std::cmp::{Ordering, Reverse};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub struct ValidityTs(Reverse<i64>);
22
23impl ValidityTs {
24 #[must_use]
26 pub fn new(ts: i64) -> Self {
27 Self(Reverse(ts))
28 }
29
30 #[must_use]
35 pub fn current() -> Self {
36 Self(Reverse(i64::MAX))
37 }
38
39 #[must_use]
41 pub fn timestamp(&self) -> i64 {
42 self.0.0
43 }
44
45 #[must_use]
55 pub fn versioned_key(entity_id: u64, ts: Self) -> [u8; 16] {
56 let mut key = [0u8; 16];
57 key[..8].copy_from_slice(&entity_id.to_be_bytes());
58 key[8..].copy_from_slice(&ts.0.0.to_be_bytes());
59 key
60 }
61
62 #[must_use]
71 pub fn from_versioned_key(key: &[u8; 16]) -> (u64, Self) {
72 let entity_id = u64::from_be_bytes(
73 key[..8]
74 .try_into()
75 .expect("first 8 bytes of a [u8; 16] are always a valid [u8; 8]"),
76 );
77 let ts = i64::from_be_bytes(
78 key[8..]
79 .try_into()
80 .expect("last 8 bytes of a [u8; 16] are always a valid [u8; 8]"),
81 );
82 (entity_id, Self::new(ts))
83 }
84}
85
86impl Ord for ValidityTs {
87 fn cmp(&self, other: &Self) -> Ordering {
88 self.0.cmp(&other.0)
89 }
90}
91
92impl PartialOrd for ValidityTs {
93 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
94 Some(self.cmp(other))
95 }
96}
97
98impl From<i64> for ValidityTs {
99 fn from(ts: i64) -> Self {
100 Self::new(ts)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_reverse_ordering() {
110 let ts1 = ValidityTs::new(10);
111 let ts2 = ValidityTs::new(20);
112
113 assert!(ts2 < ts1);
115 assert!(ts1 > ts2);
116 }
117
118 #[test]
119 fn test_current_sentinel() {
120 let current = ValidityTs::current();
121 let recent = ValidityTs::new(1_000_000);
122
123 assert!(current < recent);
125 assert_eq!(current.timestamp(), i64::MAX);
126 }
127
128 #[test]
129 fn test_versioned_key_roundtrip() {
130 let entity_id = 42u64;
131 let ts = ValidityTs::new(12345);
132
133 let key = ValidityTs::versioned_key(entity_id, ts);
134 let (decoded_id, decoded_ts) = ValidityTs::from_versioned_key(&key);
135
136 assert_eq!(decoded_id, entity_id);
137 assert_eq!(decoded_ts, ts);
138 }
139
140 #[test]
141 fn test_versioned_key_entity_ordering() {
142 let key1 = ValidityTs::versioned_key(1, ValidityTs::new(100));
144 let key2 = ValidityTs::versioned_key(2, ValidityTs::new(100));
145
146 assert!(key1 < key2);
147 }
148
149 #[test]
150 fn test_equality() {
151 let ts1 = ValidityTs::new(42);
152 let ts2 = ValidityTs::new(42);
153 let ts3 = ValidityTs::new(43);
154
155 assert_eq!(ts1, ts2);
156 assert_ne!(ts1, ts3);
157 }
158
159 #[test]
160 fn test_from_i64() {
161 let ts: ValidityTs = 42i64.into();
162 assert_eq!(ts.timestamp(), 42);
163 }
164}