aria2-gid 0.1.0

Aria2-Gid 是一个用于生成和解析 Aria2 GID 的库。
Documentation
//! # Aria2-Gid
//!
//! [![GitHub last commit](https://img.shields.io/github/last-commit/share121/aria2-gid/master)](https://github.com/share121/aria2-gid/commits/master)
//! [![Test](https://github.com/share121/aria2-gid/workflows/Test/badge.svg)](https://github.com/share121/aria2-gid/actions)
//! [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/share121/aria2-gid/blob/master/LICENSE)
//!
//! Aria2-Gid 是一个用于生成和解析 Aria2 GID 的库。
//!
//! GID 是一个 64 位无符号整数,由两部分组成:
//! - 高 32 位:UNIX 时间戳(秒级)
//! - 低 32 位:随机数
//!
//! # 示例
//!
//! ```rust
//! use aria2_gid::Gid;
//!
//! // 生成新的 GID
//! let gid = Gid::new();
//! println!("新生成的 GID: {}", gid);
//!
//! // 从十六进制字符串解析
//! let hex_gid: Gid = "5f9a3b801a4c3d2e".parse().unwrap();
//! println!("解析的 GID: {}", hex_gid);
//!
//! // 获取时间戳和随机数部分
//! println!("时间戳: {}", gid.timestamp());
//! println!("随机数: {}", gid.random());
//! ```

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; // 2023-01-01 00:00:00 UTC
        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()); // 无效字符
    }
}