1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// WARNING: This uses a non-cryptographically secure pseudo-random number generator.
// It is purely for demonstration purposes within the zero-dependency constraint.
// Do NOT use these UUIDs for security-sensitive applications.
use crate::{Transform, TransformError, TransformerCategory};
use core::cell::Cell; // Using Cell for interior mutability for the PRNG state
use core::fmt::Write;
// Simple Linear Congruential Generator (LCG) state
// Parameters from POSIX `rand()` - not great, but simple and dependency-free
// We use Cell for interior mutability without needing &mut self in transform
thread_local!(static LCG_STATE: Cell<u32> = const { Cell::new(12345) });
fn lcg_rand() -> u32 {
LCG_STATE.with(|state_cell| {
let current_state = state_cell.get();
// LCG formula: X_{n+1} = (a * X_n + c) mod m
// Using m = 2^31, a = 1103515245, c = 12345 from POSIX standard
// We compute using u64 to avoid overflow during multiplication
let next_state = ((1103515245u64 * current_state as u64 + 12345) % 2147483648u64) as u32;
state_cell.set(next_state);
// Return the upper 16 bits like some `rand()` implementations do
// to get slightly better distribution in higher bits
// but for UUID we need 32 bits, so let's just return next_state for now.
next_state
})
}
// Function to generate 16 bytes of pseudo-random data
fn generate_random_bytes() -> [u8; 16] {
let mut bytes = [0u8; 16];
for chunk in bytes.chunks_mut(4) {
let random_u32 = lcg_rand();
chunk.copy_from_slice(&random_u32.to_be_bytes());
}
bytes
}
/// UUID Generate transformer
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UuidGenerate;
impl Transform for UuidGenerate {
fn name(&self) -> &'static str {
"UUID Generate (v4)"
}
fn id(&self) -> &'static str {
"uuid_generate"
}
fn description(&self) -> &'static str {
"Generates a version 4 UUID. Input is ignored. WARNING: Uses a non-cryptographically secure PRNG."
}
fn category(&self) -> TransformerCategory {
TransformerCategory::Other
}
fn transform(&self, _input: &str) -> Result<String, TransformError> {
// Seed the LCG minimally on first call per thread if needed,
// using something slightly varying. Still very weak.
// A proper seed would ideally use system time or /dev/urandom if allowed.
LCG_STATE.with(|state_cell| {
if state_cell.get() == 12345 {
// Default initial value
// Use address of input string XORed with a constant as a *very weak* seed attempt
let seed = (_input.as_ptr() as u32) ^ 0xDEADBEEF;
state_cell.set(seed.wrapping_add(1)); // Avoid 0 if possible
}
});
let mut bytes = generate_random_bytes();
// Set version (4) and variant (RFC 4122)
bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 1 (RFC 4122)
// Format as UUID string xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
let mut uuid_str = String::with_capacity(36);
write!(
&mut uuid_str,
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5],
bytes[6], bytes[7],
bytes[8], bytes[9],
bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
).map_err(|e| TransformError::InvalidArgument(format!("Failed to format UUID: {}", e).into()))?;
Ok(uuid_str)
}
fn default_test_input(&self) -> &'static str {
"" // Input is ignored, so empty string is fine
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn test_uuid_generate_format() {
let transformer = UuidGenerate;
let uuid_str = transformer.transform("test").unwrap();
// Check length
assert_eq!(uuid_str.len(), 36);
// Check hyphens
assert_eq!(uuid_str.chars().nth(8), Some('-'));
assert_eq!(uuid_str.chars().nth(13), Some('-'));
assert_eq!(uuid_str.chars().nth(18), Some('-'));
assert_eq!(uuid_str.chars().nth(23), Some('-'));
// Check version (char 14 should be '4')
assert_eq!(uuid_str.chars().nth(14), Some('4'));
// Check variant (char 19 should be '8', '9', 'a', or 'b')
let variant_char = uuid_str.chars().nth(19).unwrap();
assert!(matches!(variant_char, '8' | '9' | 'a' | 'b'));
// Check if all other chars are hex
for (i, c) in uuid_str.chars().enumerate() {
if ![8, 13, 18, 23].contains(&i) {
assert!(
c.is_ascii_hexdigit(),
"Char at index {} is not hex: {}",
i,
c
);
}
}
}
#[test]
fn test_uuid_generate_uniqueness_basic() {
// This test is weak due to the poor PRNG, but checks for basic differences.
let transformer = UuidGenerate;
let mut generated_uuids = HashSet::new();
for i in 0..100 {
let uuid_str = transformer.transform(&format!("seed_{}", i)).unwrap(); // Vary input slightly
assert!(
generated_uuids.insert(uuid_str),
"Duplicate UUID generated (basic check)"
);
}
assert_eq!(generated_uuids.len(), 100);
}
}