tatara_lisp_script/stdlib/
uuid.rs1use std::sync::Arc;
12
13use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
14
15use crate::script_ctx::ScriptCtx;
16
17pub fn install(interp: &mut Interpreter<ScriptCtx>) {
18 interp.register_fn(
19 "uuid-v4",
20 Arity::Exact(0),
21 |_args: &[Value], _ctx: &mut ScriptCtx, sp| {
22 let mut buf = [0u8; 16];
23 fill_random(&mut buf)
24 .map_err(|e| EvalError::native_fn("uuid-v4", format!("rng: {e}"), sp))?;
25 buf[6] = (buf[6] & 0x0f) | 0x40;
27 buf[8] = (buf[8] & 0x3f) | 0x80;
28 Ok(Value::Str(Arc::from(format_uuid(&buf))))
29 },
30 );
31
32 interp.register_fn(
33 "random-bytes",
34 Arity::Exact(1),
35 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
36 let n: usize = match &args[0] {
37 Value::Int(n) if *n >= 0 => usize::try_from(*n).map_err(|_| {
38 EvalError::native_fn("random-bytes", format!("size {n} too large"), sp)
39 })?,
40 v => {
41 return Err(EvalError::native_fn(
42 "random-bytes",
43 format!("size must be non-negative int, got {v:?}"),
44 sp,
45 ))
46 }
47 };
48 let mut buf = vec![0u8; n];
49 fill_random(&mut buf)
50 .map_err(|e| EvalError::native_fn("random-bytes", format!("rng: {e}"), sp))?;
51 Ok(Value::Str(Arc::from(hex_lower(&buf))))
52 },
53 );
54}
55
56fn fill_random(buf: &mut [u8]) -> std::io::Result<()> {
57 use std::io::Read;
59 let mut f = std::fs::File::open("/dev/urandom")?;
60 f.read_exact(buf)?;
61 Ok(())
62}
63
64fn format_uuid(b: &[u8; 16]) -> String {
65 format!(
67 "{:02x}{:02x}{:02x}{:02x}-\
68 {:02x}{:02x}-{:02x}{:02x}-\
69 {:02x}{:02x}-\
70 {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
71 b[0],
72 b[1],
73 b[2],
74 b[3],
75 b[4],
76 b[5],
77 b[6],
78 b[7],
79 b[8],
80 b[9],
81 b[10],
82 b[11],
83 b[12],
84 b[13],
85 b[14],
86 b[15],
87 )
88}
89
90fn hex_lower(bytes: &[u8]) -> String {
91 let mut s = String::with_capacity(bytes.len() * 2);
92 for b in bytes {
93 s.push_str(&format!("{b:02x}"));
94 }
95 s
96}