use anyhow::Result;
use std::time::{SystemTime, UNIX_EPOCH};
pub fn current_timestamp_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
pub fn generate_random_bytes(len: usize) -> Vec<u8> {
use rand_core::{OsRng, RngCore};
let mut bytes = vec![0u8; len];
OsRng.fill_bytes(&mut bytes);
bytes
}
pub fn hex_encode(bytes: &[u8]) -> String {
hex::encode(bytes)
}
pub fn hex_decode(hex_str: &str) -> Result<Vec<u8>> {
hex::decode(hex_str).map_err(|e| anyhow::anyhow!("Hex decode error: {}", e))
}
pub fn sha256_hash(data: &[u8]) -> [u8; 32] {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().into()
}
pub fn format_bytes(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
const THRESHOLD: u64 = 1024;
if bytes < THRESHOLD {
return format!("{} B", bytes);
}
let mut size = bytes as f64;
let mut unit_index = 0;
while size >= THRESHOLD as f64 && unit_index < UNITS.len() - 1 {
size /= THRESHOLD as f64;
unit_index += 1;
}
format!("{:.1} {}", size, UNITS[unit_index])
}
pub fn format_duration_ms(ms: u64) -> String {
if ms < 1000 {
format!("{}ms", ms)
} else if ms < 60_000 {
format!("{:.1}s", ms as f64 / 1000.0)
} else if ms < 3_600_000 {
format!("{:.1}m", ms as f64 / 60_000.0)
} else {
format!("{:.1}h", ms as f64 / 3_600_000.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timestamp_generation() {
let ts1 = current_timestamp_ms();
let ts2 = current_timestamp_ms();
assert!(ts2 >= ts1, "Timestamps should be non-decreasing");
}
#[test]
fn test_timestamp_is_reasonable() {
let ts = current_timestamp_ms();
assert!(ts > 1704067200000, "Timestamp should be after 2024");
assert!(ts < 4102444800000, "Timestamp should be before 2100");
}
#[test]
fn test_random_bytes() {
let bytes1 = generate_random_bytes(16);
let bytes2 = generate_random_bytes(16);
assert_eq!(bytes1.len(), 16);
assert_eq!(bytes2.len(), 16);
assert_ne!(bytes1, bytes2, "Random bytes should be different");
}
#[test]
fn test_random_bytes_zero_length() {
let bytes = generate_random_bytes(0);
assert!(bytes.is_empty());
}
#[test]
fn test_random_bytes_large() {
let bytes = generate_random_bytes(1024);
assert_eq!(bytes.len(), 1024);
}
#[test]
fn test_random_bytes_various_sizes() {
for size in [1, 8, 32, 64, 128, 256] {
let bytes = generate_random_bytes(size);
assert_eq!(bytes.len(), size);
}
}
#[test]
fn test_hex_encoding() {
let data = b"hello world";
let encoded = hex_encode(data);
let decoded = hex_decode(&encoded).unwrap();
assert_eq!(decoded, data);
}
#[test]
fn test_hex_encode_empty() {
let encoded = hex_encode(&[]);
assert!(encoded.is_empty());
}
#[test]
fn test_hex_encode_single_byte() {
assert_eq!(hex_encode(&[0x00]), "00");
assert_eq!(hex_encode(&[0xff]), "ff");
assert_eq!(hex_encode(&[0x0a]), "0a");
}
#[test]
fn test_hex_encode_known_values() {
assert_eq!(hex_encode(b"ABC"), "414243");
assert_eq!(hex_encode(&[0xde, 0xad, 0xbe, 0xef]), "deadbeef");
}
#[test]
fn test_hex_decode_empty() {
let decoded = hex_decode("").unwrap();
assert!(decoded.is_empty());
}
#[test]
fn test_hex_decode_known_values() {
assert_eq!(hex_decode("414243").unwrap(), b"ABC");
assert_eq!(
hex_decode("deadbeef").unwrap(),
vec![0xde, 0xad, 0xbe, 0xef]
);
}
#[test]
fn test_hex_decode_uppercase() {
assert_eq!(
hex_decode("DEADBEEF").unwrap(),
vec![0xde, 0xad, 0xbe, 0xef]
);
}
#[test]
fn test_hex_decode_invalid_char() {
let result = hex_decode("xyz");
assert!(result.is_err());
}
#[test]
fn test_hex_decode_odd_length() {
let result = hex_decode("abc");
assert!(result.is_err());
}
#[test]
fn test_sha256_hash() {
let data = b"test data";
let hash1 = sha256_hash(data);
let hash2 = sha256_hash(data);
assert_eq!(hash1, hash2, "Hash should be deterministic");
assert_eq!(hash1.len(), 32, "SHA256 should be 32 bytes");
}
#[test]
fn test_sha256_hash_empty() {
let hash = sha256_hash(&[]);
let expected =
hex_decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap();
assert_eq!(hash.as_slice(), expected.as_slice());
}
#[test]
fn test_sha256_hash_known_value() {
let hash = sha256_hash(b"hello");
let hex_hash = hex_encode(&hash);
assert_eq!(
hex_hash,
"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
);
}
#[test]
fn test_sha256_hash_different_inputs() {
let hash1 = sha256_hash(b"hello");
let hash2 = sha256_hash(b"world");
assert_ne!(hash1, hash2);
}
#[test]
fn test_format_bytes() {
assert_eq!(format_bytes(512), "512 B");
assert_eq!(format_bytes(1024), "1.0 KB");
assert_eq!(format_bytes(1536), "1.5 KB");
assert_eq!(format_bytes(1048576), "1.0 MB");
assert_eq!(format_bytes(1073741824), "1.0 GB");
}
#[test]
fn test_format_bytes_zero() {
assert_eq!(format_bytes(0), "0 B");
}
#[test]
fn test_format_bytes_edge_cases() {
assert_eq!(format_bytes(1023), "1023 B");
assert_eq!(format_bytes(1025), "1.0 KB");
}
#[test]
fn test_format_bytes_terabytes() {
let tb = 1024u64 * 1024 * 1024 * 1024;
assert_eq!(format_bytes(tb), "1.0 TB");
}
#[test]
fn test_format_bytes_large() {
let large = 10u64 * 1024 * 1024 * 1024 * 1024; assert_eq!(format_bytes(large), "10.0 TB");
}
#[test]
fn test_format_duration() {
assert_eq!(format_duration_ms(500), "500ms");
assert_eq!(format_duration_ms(1500), "1.5s");
assert_eq!(format_duration_ms(90000), "1.5m");
assert_eq!(format_duration_ms(7200000), "2.0h");
}
#[test]
fn test_format_duration_zero() {
assert_eq!(format_duration_ms(0), "0ms");
}
#[test]
fn test_format_duration_edge_cases() {
assert_eq!(format_duration_ms(999), "999ms");
assert_eq!(format_duration_ms(1000), "1.0s");
assert_eq!(format_duration_ms(59999), "60.0s");
assert_eq!(format_duration_ms(60000), "1.0m");
assert_eq!(format_duration_ms(3599999), "60.0m");
assert_eq!(format_duration_ms(3600000), "1.0h");
}
#[test]
fn test_format_duration_large() {
assert_eq!(format_duration_ms(24 * 3600000), "24.0h");
}
#[test]
fn test_hex_roundtrip_random() {
let original = generate_random_bytes(64);
let encoded = hex_encode(&original);
let decoded = hex_decode(&encoded).unwrap();
assert_eq!(original, decoded);
}
}