use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Gid(u64);
impl Gid {
pub fn new() -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
Self::from_duration(timestamp)
}
pub fn from_duration(timestamp: Duration) -> Self {
let timestamp = timestamp.as_secs();
let random_num: u32 = rand::random();
Gid(timestamp << 32 | random_num as u64)
}
pub fn timestamp(&self) -> u32 {
(self.0 >> 32) as u32
}
pub fn random(&self) -> u32 {
self.0 as u32
}
pub fn to_hex_string(&self) -> String {
format!("{:08x}{:08x}", self.timestamp(), self.random())
}
pub fn from_hex_string(s: &str) -> Option<Self> {
if s.len() != 16 {
return None;
}
let timestamp = u32::from_str_radix(&s[..8], 16).ok()? as u64;
let random = u32::from_str_radix(&s[8..], 16).ok()? as u64;
Some(Gid(timestamp << 32 | random))
}
}
impl std::fmt::Display for Gid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_hex_string())
}
}
impl std::str::FromStr for Gid {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Gid::from_hex_string(s).ok_or("Invalid GID format")
}
}
impl Default for Gid {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gid_new() {
let gid1 = Gid::new();
let gid2 = Gid::new();
assert_ne!(gid1, gid2);
let timestamp1 = gid1.timestamp();
let timestamp2 = gid2.timestamp();
assert!(timestamp2 >= timestamp1);
assert!(timestamp2 - timestamp1 <= 1);
}
#[test]
fn test_gid_from_duration() {
let duration = Duration::from_secs(1234567890);
let gid = Gid::from_duration(duration);
assert_eq!(gid.timestamp(), 1234567890);
}
#[test]
fn test_gid_timestamp_and_random() {
let timestamp = 1672531200; let random_val = 0x12345678;
let gid = Gid((timestamp as u64) << 32 | random_val as u64);
assert_eq!(gid.timestamp(), timestamp);
assert_eq!(gid.random(), random_val);
}
#[test]
fn test_gid_to_hex_string() {
let gid = Gid(0x1234567890ABCDEF);
let hex_string = gid.to_hex_string();
assert_eq!(hex_string, "1234567890abcdef");
}
#[test]
fn test_gid_from_hex_string() {
let hex_string = "1234567890abcdef";
let gid = Gid::from_hex_string(hex_string).unwrap();
assert_eq!(gid.timestamp(), 0x12345678);
assert_eq!(gid.random(), 0x90ABCDEF);
assert!(Gid::from_hex_string("").is_none()); assert!(Gid::from_hex_string("123").is_none()); assert!(Gid::from_hex_string("1234567890abcdef123").is_none()); assert!(Gid::from_hex_string("gggggggggggggggg").is_none()); }
}