buup/transformers/
uuid_generate.rs1use crate::{Transform, TransformError, TransformerCategory};
6use core::cell::Cell; use core::fmt::Write;
8
9thread_local!(static LCG_STATE: Cell<u32> = const { Cell::new(12345) });
13
14fn lcg_rand() -> u32 {
15 LCG_STATE.with(|state_cell| {
16 let current_state = state_cell.get();
17 let next_state = ((1103515245u64 * current_state as u64 + 12345) % 2147483648u64) as u32;
21 state_cell.set(next_state);
22 next_state
26 })
27}
28
29fn generate_random_bytes() -> [u8; 16] {
31 let mut bytes = [0u8; 16];
32 for chunk in bytes.chunks_mut(4) {
33 let random_u32 = lcg_rand();
34 chunk.copy_from_slice(&random_u32.to_be_bytes());
35 }
36 bytes
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct UuidGenerate;
42
43impl Transform for UuidGenerate {
44 fn name(&self) -> &'static str {
45 "UUID Generate (v4)"
46 }
47
48 fn id(&self) -> &'static str {
49 "uuid_generate"
50 }
51
52 fn description(&self) -> &'static str {
53 "Generates a version 4 UUID. Input is ignored. WARNING: Uses a non-cryptographically secure PRNG."
54 }
55
56 fn category(&self) -> TransformerCategory {
57 TransformerCategory::Other
58 }
59
60 fn transform(&self, _input: &str) -> Result<String, TransformError> {
61 LCG_STATE.with(|state_cell| {
65 if state_cell.get() == 12345 {
66 let seed = (_input.as_ptr() as u32) ^ 0xDEADBEEF;
69 state_cell.set(seed.wrapping_add(1)); }
71 });
72
73 let mut bytes = generate_random_bytes();
74
75 bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; let mut uuid_str = String::with_capacity(36);
81 write!(
82 &mut uuid_str,
83 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
84 bytes[0], bytes[1], bytes[2], bytes[3],
85 bytes[4], bytes[5],
86 bytes[6], bytes[7],
87 bytes[8], bytes[9],
88 bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
89 ).map_err(|e| TransformError::InvalidArgument(format!("Failed to format UUID: {}", e).into()))?;
90
91 Ok(uuid_str)
92 }
93
94 fn default_test_input(&self) -> &'static str {
95 "" }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use std::collections::HashSet;
103
104 #[test]
105 fn test_uuid_generate_format() {
106 let transformer = UuidGenerate;
107 let uuid_str = transformer.transform("test").unwrap();
108
109 assert_eq!(uuid_str.len(), 36);
111
112 assert_eq!(uuid_str.chars().nth(8), Some('-'));
114 assert_eq!(uuid_str.chars().nth(13), Some('-'));
115 assert_eq!(uuid_str.chars().nth(18), Some('-'));
116 assert_eq!(uuid_str.chars().nth(23), Some('-'));
117
118 assert_eq!(uuid_str.chars().nth(14), Some('4'));
120
121 let variant_char = uuid_str.chars().nth(19).unwrap();
123 assert!(matches!(variant_char, '8' | '9' | 'a' | 'b'));
124
125 for (i, c) in uuid_str.chars().enumerate() {
127 if ![8, 13, 18, 23].contains(&i) {
128 assert!(
129 c.is_ascii_hexdigit(),
130 "Char at index {} is not hex: {}",
131 i,
132 c
133 );
134 }
135 }
136 }
137
138 #[test]
139 fn test_uuid_generate_uniqueness_basic() {
140 let transformer = UuidGenerate;
142 let mut generated_uuids = HashSet::new();
143 for i in 0..100 {
144 let uuid_str = transformer.transform(&format!("seed_{}", i)).unwrap(); assert!(
146 generated_uuids.insert(uuid_str),
147 "Duplicate UUID generated (basic check)"
148 );
149 }
150 assert_eq!(generated_uuids.len(), 100);
151 }
152}