use crate::Uuid;
use switchy_env::var_parse_or;
use switchy_random::{GenericRng, Rng};
static RNG: std::sync::LazyLock<Rng> = std::sync::LazyLock::new(|| {
let seed = var_parse_or("SIMULATOR_UUID_SEED", 12345u64);
log::debug!("Using UUID seed: {seed}");
Rng::from_seed(seed)
});
#[must_use]
pub fn new_v4() -> Uuid {
let mut bytes = [0u8; 16];
RNG.fill_bytes(&mut bytes);
bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80;
Uuid::from_bytes(bytes)
}
#[must_use]
pub fn new_v4_string() -> String {
new_v4().to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test_log::test]
fn test_deterministic_generation() {
let uuid1 = new_v4();
let uuid2 = new_v4();
let uuid3 = new_v4();
assert_ne!(uuid1, uuid2);
assert_ne!(uuid2, uuid3);
assert_ne!(uuid1, uuid3);
}
#[test_log::test]
fn test_uuid_v4_format_compliance() {
let uuid = new_v4();
let bytes = uuid.as_bytes();
let version_byte = bytes[6];
assert_eq!(
version_byte & 0xf0,
0x40,
"UUID version bits should be 0100 (v4)"
);
let variant_byte = bytes[8];
assert_eq!(
variant_byte & 0xc0,
0x80,
"UUID variant bits should be 10 (RFC 4122)"
);
}
#[test_log::test]
fn test_string_conversion_consistency() {
let uuid = new_v4();
let string_from_uuid = uuid.to_string();
let uuid_from_string = new_v4_string();
assert_eq!(string_from_uuid.len(), 36);
assert_eq!(uuid_from_string.len(), 36);
assert_eq!(string_from_uuid.chars().nth(8).unwrap(), '-');
assert_eq!(string_from_uuid.chars().nth(13).unwrap(), '-');
assert_eq!(string_from_uuid.chars().nth(18).unwrap(), '-');
assert_eq!(string_from_uuid.chars().nth(23).unwrap(), '-');
}
#[test_log::test]
fn test_multiple_uuids_are_unique() {
let mut uuids = std::collections::BTreeSet::new();
for _ in 0..100 {
let uuid = new_v4();
assert!(uuids.insert(uuid), "Generated duplicate UUID: {uuid}");
}
assert_eq!(uuids.len(), 100);
}
#[test_log::test]
fn test_new_v4_string_produces_valid_uuid() {
let uuid_string = new_v4_string();
let parsed = Uuid::parse_str(&uuid_string);
assert!(
parsed.is_ok(),
"new_v4_string should produce valid UUID string"
);
let uuid = parsed.unwrap();
let bytes = uuid.as_bytes();
assert_eq!(bytes[6] & 0xf0, 0x40, "Parsed UUID should be version 4");
assert_eq!(
bytes[8] & 0xc0,
0x80,
"Parsed UUID should have RFC 4122 variant"
);
}
#[test_log::test]
fn test_version_variant_bits_consistently_set_across_many_uuids() {
for i in 0..100 {
let uuid = new_v4();
let bytes = uuid.as_bytes();
assert_eq!(
bytes[6] & 0xf0,
0x40,
"UUID #{i} version bits incorrect: got {:02x}, expected 0x4x",
bytes[6]
);
assert_eq!(
bytes[8] & 0xc0,
0x80,
"UUID #{i} variant bits incorrect: got {:02x}, expected 0x8x-0xbx",
bytes[8]
);
}
}
#[test_log::test]
fn test_random_bits_preserved_in_version_variant_bytes() {
let mut byte6_lower_nibbles = std::collections::BTreeSet::new();
let mut byte8_lower_bits = std::collections::BTreeSet::new();
for _ in 0..50 {
let uuid = new_v4();
let bytes = uuid.as_bytes();
byte6_lower_nibbles.insert(bytes[6] & 0x0f);
byte8_lower_bits.insert(bytes[8] & 0x3f);
}
assert!(
byte6_lower_nibbles.len() >= 4,
"Expected variation in byte[6] lower nibble, but only saw {} unique values: {:?}",
byte6_lower_nibbles.len(),
byte6_lower_nibbles
);
assert!(
byte8_lower_bits.len() >= 8,
"Expected variation in byte[8] lower bits, but only saw {} unique values: {:?}",
byte8_lower_bits.len(),
byte8_lower_bits
);
}
#[test_log::test]
fn test_concurrent_uuid_generation_produces_unique_uuids() {
use std::collections::BTreeSet;
use std::sync::Arc;
let num_threads = 4;
let uuids_per_thread = 50;
let all_uuids: Arc<std::sync::Mutex<BTreeSet<Uuid>>> =
Arc::new(std::sync::Mutex::new(BTreeSet::new()));
std::thread::scope(|s| {
for _ in 0..num_threads {
let uuids_clone = Arc::clone(&all_uuids);
s.spawn(move || {
for _ in 0..uuids_per_thread {
let uuid = new_v4();
let was_inserted = uuids_clone.lock().unwrap().insert(uuid);
assert!(
was_inserted,
"Concurrent UUID generation produced duplicate: {uuid}"
);
}
});
}
});
let final_count = all_uuids.lock().unwrap().len();
assert_eq!(
final_count,
num_threads * uuids_per_thread,
"Expected {} unique UUIDs from concurrent generation, got {}",
num_threads * uuids_per_thread,
final_count
);
}
#[test_log::test]
fn test_all_uuid_bytes_are_populated() {
use std::collections::BTreeSet;
let mut byte_variations: [BTreeSet<u8>; 16] = Default::default();
for _ in 0..100 {
let uuid = new_v4();
let bytes = uuid.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
byte_variations[i].insert(byte);
}
}
for (i, variations) in byte_variations.iter().enumerate() {
assert!(
variations.len() >= 2,
"Byte position {i} shows no variation across 100 UUIDs: {variations:?}"
);
}
}
}