#[must_use]
#[cfg(target_arch = "wasm32")]
pub fn bytes(len: usize) -> Vec<u8> {
crate::wasi_http::wasi::random::random::get_random_bytes(len as u64)
}
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), test))]
pub fn bytes(len: usize) -> Vec<u8> {
let mut buf = vec![0u8; len];
getrandom::fill(&mut buf).expect("system RNG failure");
buf
}
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
pub fn bytes(_len: usize) -> Vec<u8> {
panic!(
"\n\
╔══════════════════════════════════════════════════════════════════╗\n\
║ random::bytes() is only available in WASM builds ║\n\
╠══════════════════════════════════════════════════════════════════╣\n\
║ ║\n\
║ This SDK is designed for WASI HTTP handlers. ║\n\
║ Random functions use wasi:random/random in WASM. ║\n\
║ ║\n\
║ To build for WASM: ║\n\
║ cargo component build --target wasm32-wasip2 ║\n\
║ ║\n\
║ Or run tests (which use getrandom): ║\n\
║ cargo test ║\n\
║ ║\n\
╚══════════════════════════════════════════════════════════════════╝\n"
)
}
#[must_use]
#[cfg(target_arch = "wasm32")]
pub fn u64() -> u64 {
crate::wasi_http::wasi::random::random::get_random_u64()
}
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), test))]
pub fn u64() -> u64 {
let mut buf = [0u8; 8];
getrandom::fill(&mut buf).expect("system RNG failure");
u64::from_le_bytes(buf)
}
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
pub fn u64() -> u64 {
panic!(
"\n\
╔══════════════════════════════════════════════════════════════════╗\n\
║ random::u64() is only available in WASM builds ║\n\
╠══════════════════════════════════════════════════════════════════╣\n\
║ ║\n\
║ This SDK is designed for WASI HTTP handlers. ║\n\
║ Random functions use wasi:random/random in WASM. ║\n\
║ ║\n\
║ To build for WASM: ║\n\
║ cargo component build --target wasm32-wasip2 ║\n\
║ ║\n\
╚══════════════════════════════════════════════════════════════════╝\n"
)
}
#[must_use]
#[cfg(any(target_arch = "wasm32", test))]
pub fn uuid() -> String {
let mut buf = bytes(16);
buf[6] = (buf[6] & 0x0F) | 0x40;
buf[8] = (buf[8] & 0x3F) | 0x80;
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
buf[0],
buf[1],
buf[2],
buf[3],
buf[4],
buf[5],
buf[6],
buf[7],
buf[8],
buf[9],
buf[10],
buf[11],
buf[12],
buf[13],
buf[14],
buf[15]
)
}
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
pub fn uuid() -> String {
panic!(
"\n\
╔══════════════════════════════════════════════════════════════════╗\n\
║ random::uuid() is only available in WASM builds ║\n\
╠══════════════════════════════════════════════════════════════════╣\n\
║ ║\n\
║ This SDK is designed for WASI HTTP handlers. ║\n\
║ Random functions use wasi:random/random in WASM. ║\n\
║ ║\n\
║ To build for WASM: ║\n\
║ cargo component build --target wasm32-wasip2 ║\n\
║ ║\n\
╚══════════════════════════════════════════════════════════════════╝\n"
)
}
#[must_use]
#[cfg(any(target_arch = "wasm32", test))]
pub fn hex(byte_len: usize) -> String {
use crate::constants::HEX_CHARS;
let random_bytes = bytes(byte_len);
let mut result = String::with_capacity(byte_len * 2);
for b in random_bytes {
result.push(HEX_CHARS[(b >> 4) as usize] as char);
result.push(HEX_CHARS[(b & 0x0f) as usize] as char);
}
result
}
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
pub fn hex(_byte_len: usize) -> String {
panic!(
"\n\
╔══════════════════════════════════════════════════════════════════╗\n\
║ random::hex() is only available in WASM builds ║\n\
╠══════════════════════════════════════════════════════════════════╣\n\
║ ║\n\
║ This SDK is designed for WASI HTTP handlers. ║\n\
║ Random functions use wasi:random/random in WASM. ║\n\
║ ║\n\
║ To build for WASM: ║\n\
║ cargo component build --target wasm32-wasip2 ║\n\
║ ║\n\
╚══════════════════════════════════════════════════════════════════╝\n"
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bytes_length() {
assert_eq!(bytes(16).len(), 16);
assert_eq!(bytes(32).len(), 32);
assert_eq!(bytes(0).len(), 0);
}
#[test]
fn test_bytes_randomness() {
let b1 = bytes(32);
let b2 = bytes(32);
assert_ne!(b1, b2, "Two random byte arrays should differ");
}
#[test]
fn test_u64_returns_value() {
let v = u64();
let _ = v;
}
#[test]
fn test_u64_randomness() {
let v1 = u64();
let v2 = u64();
assert_ne!(v1, v2, "Two random u64s should differ");
}
#[test]
fn test_uuid_format() {
let id = uuid();
assert_eq!(id.len(), 36);
assert_eq!(id.chars().nth(8), Some('-'));
assert_eq!(id.chars().nth(13), Some('-'));
assert_eq!(id.chars().nth(18), Some('-'));
assert_eq!(id.chars().nth(23), Some('-'));
assert_eq!(id.chars().nth(14), Some('4'));
let variant = id
.chars()
.nth(19)
.expect("UUID must have character at position 19");
assert!(matches!(variant, '8' | '9' | 'a' | 'b'));
}
#[test]
fn test_uuid_version_bits() {
for _ in 0..100 {
let id = uuid();
assert_eq!(id.chars().nth(14), Some('4'));
}
}
#[test]
fn test_uuid_variant_bits() {
for _ in 0..100 {
let id = uuid();
let variant = id.chars().nth(19).unwrap();
assert!(
matches!(variant, '8' | '9' | 'a' | 'b'),
"Variant char '{variant}' not in valid set [8,9,a,b]"
);
}
}
#[test]
fn test_uuid_version_byte_exact() {
for _ in 0..100 {
let id = uuid();
let version_char = id.chars().nth(14).unwrap();
assert_eq!(
version_char, '4',
"Version nibble must be 4, got {version_char}"
);
let byte6_high = u8::from_str_radix(&id[14..15], 16).unwrap();
assert_eq!(byte6_high, 4, "Version high nibble must be 0x4");
}
}
#[test]
fn test_uuid_variant_byte_exact() {
for _ in 0..100 {
let id = uuid();
let variant_char = id.chars().nth(19).unwrap();
let variant_nibble = u8::from_str_radix(&variant_char.to_string(), 16).unwrap();
assert!(
(8..=11).contains(&variant_nibble),
"Variant nibble must be 8-11 (0x8-0xB), got {variant_nibble}"
);
assert!((variant_nibble & 0x8) != 0, "Variant bit 7 must be set");
assert!((variant_nibble & 0x4) == 0, "Variant bit 6 must be clear");
}
}
#[test]
fn test_uuid_uniqueness() {
let id1 = uuid();
let id2 = uuid();
assert_ne!(id1, id2, "Two UUIDs should be unique");
}
#[test]
fn test_uuid_all_lowercase() {
let id = uuid();
let hex_chars: String = id.chars().filter(|c| *c != '-').collect();
assert!(
hex_chars
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()),
"UUID should be all lowercase hex"
);
}
#[test]
fn test_uuid_raw_bytes_version() {
for _ in 0..100 {
let id = uuid();
let hex_str: String = id.chars().filter(|c| *c != '-').collect();
let bytes: Vec<u8> = (0..16)
.map(|i| u8::from_str_radix(&hex_str[i * 2..i * 2 + 2], 16).unwrap())
.collect();
assert_eq!(
bytes[6] & 0xF0,
0x40,
"Version byte high nibble must be 0x40, got {:#04x}",
bytes[6]
);
}
}
#[test]
fn test_uuid_raw_bytes_variant() {
for _ in 0..100 {
let id = uuid();
let hex_str: String = id.chars().filter(|c| *c != '-').collect();
let bytes: Vec<u8> = (0..16)
.map(|i| u8::from_str_radix(&hex_str[i * 2..i * 2 + 2], 16).unwrap())
.collect();
assert_eq!(
bytes[8] & 0xC0,
0x80,
"Variant byte high 2 bits must be 10, got {:#04x}",
bytes[8]
);
}
}
#[test]
fn test_hex_length() {
assert_eq!(hex(8).len(), 16);
assert_eq!(hex(16).len(), 32);
}
#[test]
fn test_hex_zero_length() {
assert_eq!(hex(0).len(), 0);
assert_eq!(hex(0), "");
}
#[test]
fn test_hex_is_valid() {
let h = hex(16);
assert!(h.chars().all(|c| c.is_ascii_hexdigit()));
assert!(
h.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
);
}
#[test]
fn test_hex_uniqueness() {
let h1 = hex(16);
let h2 = hex(16);
assert_ne!(h1, h2, "Two hex strings should differ");
}
}