use std::cmp::{Ordering, Reverse};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ValidityTs(Reverse<i64>);
impl ValidityTs {
#[must_use]
pub fn new(ts: i64) -> Self {
Self(Reverse(ts))
}
#[must_use]
pub fn current() -> Self {
Self(Reverse(i64::MAX))
}
#[must_use]
pub fn timestamp(&self) -> i64 {
self.0.0
}
#[must_use]
pub fn versioned_key(entity_id: u64, ts: Self) -> [u8; 16] {
let mut key = [0u8; 16];
key[..8].copy_from_slice(&entity_id.to_be_bytes());
key[8..].copy_from_slice(&ts.0.0.to_be_bytes());
key
}
#[must_use]
pub fn from_versioned_key(key: &[u8; 16]) -> (u64, Self) {
let entity_id = u64::from_be_bytes(
key[..8]
.try_into()
.expect("first 8 bytes of a [u8; 16] are always a valid [u8; 8]"),
);
let ts = i64::from_be_bytes(
key[8..]
.try_into()
.expect("last 8 bytes of a [u8; 16] are always a valid [u8; 8]"),
);
(entity_id, Self::new(ts))
}
}
impl Ord for ValidityTs {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl PartialOrd for ValidityTs {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl From<i64> for ValidityTs {
fn from(ts: i64) -> Self {
Self::new(ts)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reverse_ordering() {
let ts1 = ValidityTs::new(10);
let ts2 = ValidityTs::new(20);
assert!(ts2 < ts1);
assert!(ts1 > ts2);
}
#[test]
fn test_current_sentinel() {
let current = ValidityTs::current();
let recent = ValidityTs::new(1_000_000);
assert!(current < recent);
assert_eq!(current.timestamp(), i64::MAX);
}
#[test]
fn test_versioned_key_roundtrip() {
let entity_id = 42u64;
let ts = ValidityTs::new(12345);
let key = ValidityTs::versioned_key(entity_id, ts);
let (decoded_id, decoded_ts) = ValidityTs::from_versioned_key(&key);
assert_eq!(decoded_id, entity_id);
assert_eq!(decoded_ts, ts);
}
#[test]
fn test_versioned_key_entity_ordering() {
let key1 = ValidityTs::versioned_key(1, ValidityTs::new(100));
let key2 = ValidityTs::versioned_key(2, ValidityTs::new(100));
assert!(key1 < key2);
}
#[test]
fn test_equality() {
let ts1 = ValidityTs::new(42);
let ts2 = ValidityTs::new(42);
let ts3 = ValidityTs::new(43);
assert_eq!(ts1, ts2);
assert_ne!(ts1, ts3);
}
#[test]
fn test_from_i64() {
let ts: ValidityTs = 42i64.into();
assert_eq!(ts.timestamp(), 42);
}
}