Skip to main content

robinpath_modules/modules/
uuid_mod.rs

1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4    rp.register_builtin("uuid.v4", |_args, _| {
5        Ok(Value::String(gen_v4()))
6    });
7
8    rp.register_builtin("uuid.isValid", |args, _| {
9        let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
10        let re = regex::Regex::new(
11            r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
12        )
13        .unwrap();
14        Ok(Value::Bool(re.is_match(&s)))
15    });
16
17    rp.register_builtin("uuid.version", |args, _| {
18        let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
19        if s.len() >= 15 {
20            if let Some(ch) = s.chars().nth(14) {
21                if let Some(v) = ch.to_digit(16) {
22                    return Ok(Value::Number(v as f64));
23                }
24            }
25        }
26        Ok(Value::Null)
27    });
28
29    rp.register_builtin("uuid.parse", |args, _| {
30        let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
31        let mut obj = indexmap::IndexMap::new();
32        if s.len() >= 36 {
33            let version = s.chars().nth(14).and_then(|c| c.to_digit(16)).unwrap_or(0);
34            obj.insert("version".to_string(), Value::Number(version as f64));
35            let variant_char = s.chars().nth(19).unwrap_or('0');
36            let variant_val = variant_char.to_digit(16).unwrap_or(0);
37            let variant = if variant_val & 0x8 == 0 {
38                "NCS"
39            } else if variant_val & 0xC == 0x8 {
40                "RFC4122"
41            } else if variant_val & 0xE == 0xC {
42                "Microsoft"
43            } else {
44                "Future"
45            };
46            obj.insert("variant".to_string(), Value::String(variant.to_string()));
47            let hex = s.replace('-', "");
48            obj.insert("hex".to_string(), Value::String(hex));
49        }
50        Ok(Value::Object(obj))
51    });
52
53    rp.register_builtin("uuid.generate", |args, _| {
54        let count = args.first().map(|v| v.to_number() as usize).unwrap_or(1);
55        if count == 1 {
56            return Ok(Value::String(gen_v4()));
57        }
58        let uuids: Vec<Value> = (0..count).map(|_| Value::String(gen_v4())).collect();
59        Ok(Value::Array(uuids))
60    });
61
62    rp.register_builtin("uuid.nil", |_args, _| {
63        Ok(Value::String(
64            "00000000-0000-0000-0000-000000000000".to_string(),
65        ))
66    });
67}
68
69fn gen_v4() -> String {
70    // Simple UUID v4 using random bytes
71    use std::time::{SystemTime, UNIX_EPOCH};
72    let seed = SystemTime::now()
73        .duration_since(UNIX_EPOCH)
74        .unwrap_or_default()
75        .as_nanos();
76    // Use a simple LCG PRNG seeded with time + thread id
77    let mut state = seed ^ (std::ptr::from_ref(&seed) as u128).wrapping_mul(6364136223846793005);
78    let mut bytes = [0u8; 16];
79    for byte in &mut bytes {
80        state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
81        *byte = (state >> 33) as u8;
82    }
83    // Set version 4
84    bytes[6] = (bytes[6] & 0x0f) | 0x40;
85    // Set variant RFC4122
86    bytes[8] = (bytes[8] & 0x3f) | 0x80;
87
88    format!(
89        "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
90        bytes[0], bytes[1], bytes[2], bytes[3],
91        bytes[4], bytes[5],
92        bytes[6], bytes[7],
93        bytes[8], bytes[9],
94        bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
95    )
96}