1use lex_bytecode::{MapKey, Value};
6use std::collections::{BTreeMap, BTreeSet, HashMap};
7use std::sync::{Mutex, OnceLock};
8
9pub fn is_pure_call(kind: &str, op: &str) -> bool {
13 if !is_pure_module(kind) { return false; }
14 !matches!(
15 (kind, op),
16 ("crypto", "random")
17 | ("crypto", "random_str_hex")
18 | ("crypto", "p256_generate")
21 | ("crypto", "secp256k1_generate")
23 | ("datetime", "now")
24 | ("http", "send")
25 | ("http", "get")
26 | ("http", "post")
27 | ("http", "stream_lines")
28 | ("arrow", "read_csv")
30 | ("arrow", "read_parquet")
32 | ("arrow", "read_parquet_cols")
33 | ("arrow", "write_parquet")
34 | ("arrow", "write_csv")
35 )
36}
37
38pub fn call_pure_builtin(kind: &str, op: &str, args: Vec<Value>) -> Result<Value, String> {
45 if (kind, op) == ("list", "cons") {
46 let mut it = args.into_iter();
47 let head = it.next().unwrap_or(Value::Unit);
48 let mut tail = match it.next() {
49 Some(Value::List(v)) => v,
50 Some(other) => return Err(format!("list.cons: expected List, got {other:?}")),
51 None => std::collections::VecDeque::new(),
52 };
53 tail.push_front(head);
54 return Ok(Value::List(tail));
55 }
56 dispatch(kind, op, &args)
57}
58
59pub fn try_pure_builtin(kind: &str, op: &str, args: &[Value]) -> Option<Result<Value, String>> {
66 if !is_pure_call(kind, op) { return None; }
67 Some(dispatch(kind, op, args))
68}
69
70pub fn is_pure_module(kind: &str) -> bool {
73 matches!(kind, "str" | "int" | "float" | "bool" | "list" | "iter"
74 | "option" | "result" | "tuple" | "json" | "bytes" | "flow" | "math"
75 | "map" | "set" | "crypto" | "regex" | "deque" | "datetime" | "duration" | "http"
76 | "toml" | "yaml" | "dotenv" | "csv" | "test" | "random" | "parser"
77 | "cli" | "arrow" | "df" | "decimal")
78}
79
80fn dispatch(kind: &str, op: &str, args: &[Value]) -> Result<Value, String> {
81 match (kind, op) {
82 ("str", "is_empty") => Ok(Value::Bool(expect_str(args.first())?.is_empty())),
84 ("str", "len") => Ok(Value::Int(expect_str(args.first())?.len() as i64)),
85 ("str", "char_at") => {
93 let s = expect_str(args.first())?;
94 let i = expect_int(args.get(1))?;
95 if i < 0 {
96 Ok(Value::Str("".into()))
97 } else {
98 match s.as_bytes().get(i as usize) {
99 Some(&b) if b < 128 => {
100 Ok(Value::Str((b as char).to_string().into()))
101 }
102 _ => Ok(Value::Str("".into())),
103 }
104 }
105 }
106 ("str", "concat") => {
107 let a = expect_str(args.first())?;
108 let b = expect_str(args.get(1))?;
109 Ok(Value::Str(format!("{a}{b}").into()))
110 }
111 ("str", "to_int") => {
112 let s = expect_str(args.first())?;
113 match s.parse::<i64>() {
114 Ok(n) => Ok(some(Value::Int(n))),
115 Err(_) => Ok(none()),
116 }
117 }
118 ("str", "split") => {
119 let s = expect_str(args.first())?;
120 let sep = expect_str(args.get(1))?;
121 let items: std::collections::VecDeque<Value> = if sep.is_empty() {
122 s.chars().map(|c| Value::Str(c.to_string().into())).collect()
123 } else {
124 s.split(sep.as_str()).map(|p| Value::Str(p.into())).collect()
125 };
126 Ok(Value::List(items))
127 }
128 ("str", "join") => {
129 let parts = expect_list(args.first())?;
130 let sep = expect_str(args.get(1))?;
131 let mut out = String::new();
132 for (i, p) in parts.iter().enumerate() {
133 if i > 0 { out.push_str(&sep); }
134 match p {
135 Value::Str(s) => out.push_str(s),
136 other => return Err(format!("str.join element must be Str, got {other:?}")),
137 }
138 }
139 Ok(Value::Str(out.into()))
140 }
141 ("str", "starts_with") => {
142 let s = expect_str(args.first())?;
143 let prefix = expect_str(args.get(1))?;
144 Ok(Value::Bool(s.starts_with(prefix.as_str())))
145 }
146 ("str", "ends_with") => {
147 let s = expect_str(args.first())?;
148 let suffix = expect_str(args.get(1))?;
149 Ok(Value::Bool(s.ends_with(suffix.as_str())))
150 }
151 ("str", "contains") => {
152 let s = expect_str(args.first())?;
153 let needle = expect_str(args.get(1))?;
154 Ok(Value::Bool(s.contains(needle.as_str())))
155 }
156 ("str", "cmp") => {
157 let a = expect_str(args.first())?;
158 let b = expect_str(args.get(1))?;
159 Ok(Value::Int(match a.as_str().cmp(b.as_str()) {
160 std::cmp::Ordering::Less => -1,
161 std::cmp::Ordering::Equal => 0,
162 std::cmp::Ordering::Greater => 1,
163 }))
164 }
165 ("str", "replace") => {
166 let s = expect_str(args.first())?;
167 let from = expect_str(args.get(1))?;
168 let to = expect_str(args.get(2))?;
169 Ok(Value::Str(s.replace(from.as_str(), to.as_str()).into()))
170 }
171 ("str", "trim") => Ok(Value::Str(expect_str(args.first())?.trim().into())),
172 ("str", "to_upper") => Ok(Value::Str(expect_str(args.first())?.to_uppercase().into())),
173 ("str", "to_lower") => Ok(Value::Str(expect_str(args.first())?.to_lowercase().into())),
174 ("str", "strip_prefix") => {
175 let s = expect_str(args.first())?;
176 let prefix = expect_str(args.get(1))?;
177 Ok(match s.strip_prefix(prefix.as_str()) {
178 Some(rest) => some(Value::Str(rest.into())),
179 None => none(),
180 })
181 }
182 ("str", "strip_suffix") => {
183 let s = expect_str(args.first())?;
184 let suffix = expect_str(args.get(1))?;
185 Ok(match s.strip_suffix(suffix.as_str()) {
186 Some(rest) => some(Value::Str(rest.into())),
187 None => none(),
188 })
189 }
190 ("str", "slice") => {
191 let s = expect_str(args.first())?;
196 let lo_i = expect_int(args.get(1))?;
197 let hi_i = expect_int(args.get(2))?;
198 let lo_cp = lo_i.max(0) as usize;
199 let hi_cp = hi_i.max(0) as usize;
200 if lo_cp > hi_cp {
201 return Err(format!(
202 "str.slice: reversed range [{lo_cp}..{hi_cp}]"));
203 }
204 let lo_byte = s.char_indices().nth(lo_cp).map(|(b, _)| b).unwrap_or(s.len());
207 let hi_byte = s.char_indices().nth(hi_cp).map(|(b, _)| b).unwrap_or(s.len());
208 Ok(Value::Str(s[lo_byte..hi_byte].into()))
209 }
210
211 ("int", "to_str") => Ok(Value::Str(expect_int(args.first())?.to_string().into())),
213 ("int", "to_float") => Ok(Value::Float(expect_int(args.first())? as f64)),
214 ("float", "to_int") => Ok(Value::Int(expect_float(args.first())? as i64)),
215 ("float", "to_str") => Ok(Value::Str(expect_float(args.first())?.to_string().into())),
216 ("str", "to_float") => {
217 let s = expect_str(args.first())?;
218 match s.parse::<f64>() {
219 Ok(f) => Ok(some(Value::Float(f))),
220 Err(_) => Ok(none()),
221 }
222 }
223
224 ("list", "len") => Ok(Value::Int(expect_list(args.first())?.len() as i64)),
226 ("list", "is_empty") => Ok(Value::Bool(expect_list(args.first())?.is_empty())),
227 ("list", "head") => {
228 let xs = expect_list(args.first())?;
229 match xs.front() {
230 Some(v) => Ok(some(v.clone())),
231 None => Ok(none()),
232 }
233 }
234 ("list", "tail") => {
235 let xs = expect_list(args.first())?;
236 if xs.is_empty() { Ok(Value::List(std::collections::VecDeque::new())) }
237 else { Ok(Value::List(xs.iter().skip(1).cloned().collect::<std::collections::VecDeque<_>>())) }
238 }
239 ("list", "range") => {
240 let lo = expect_int(args.first())?;
241 let hi = expect_int(args.get(1))?;
242 Ok(Value::List((lo..hi).map(Value::Int).collect::<std::collections::VecDeque<_>>()))
243 }
244 ("list", "concat") => {
245 let mut out = expect_list(args.first())?.clone();
246 out.extend(expect_list(args.get(1))?.iter().cloned());
247 Ok(Value::List(out))
248 }
249 ("list", "reverse") => {
250 let out = expect_list(args.first())?.clone();
251 let rev: std::collections::VecDeque<Value> = out.into_iter().rev().collect();
252 Ok(Value::List(rev))
253 }
254 ("list", "cons") => {
258 let head = args.first().cloned().unwrap_or(Value::Unit);
259 let mut out: std::collections::VecDeque<Value> =
260 expect_list(args.get(1))?.iter().cloned().collect();
261 out.push_front(head);
262 Ok(Value::List(out))
263 }
264 ("list", "enumerate") => {
265 let xs = expect_list(args.first())?;
266 let pairs = xs.iter().cloned().enumerate()
267 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v]))
268 .collect::<std::collections::VecDeque<_>>();
269 Ok(Value::List(pairs))
270 }
271
272 ("tuple", "fst") => tuple_index(first_arg(args)?, 0),
277 ("tuple", "snd") => tuple_index(first_arg(args)?, 1),
278 ("tuple", "third") => tuple_index(first_arg(args)?, 2),
279 ("tuple", "len") => match first_arg(args)? {
280 Value::Tuple(items) => Ok(Value::Int(items.len() as i64)),
281 other => Err(format!("tuple.len: expected Tuple, got {other:?}")),
282 },
283
284 ("option", "unwrap_or") => {
286 let opt = first_arg(args)?;
287 let default = args.get(1).cloned().unwrap_or(Value::Unit);
288 match opt {
289 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
290 Value::Variant { name, .. } if name == "None" => Ok(default),
291 other => Err(format!("option.unwrap_or expected Option, got {other:?}")),
292 }
293 }
294 ("option", "unwrap_or_else") => {
299 let opt = first_arg(args)?;
300 match opt {
301 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
302 Value::Variant { name, .. } if name == "None" => {
303 Ok(args.get(1).cloned().unwrap_or(Value::Unit))
307 }
308 other => Err(format!("option.unwrap_or_else expected Option, got {other:?}")),
309 }
310 }
311 ("option", "is_some") => match first_arg(args)? {
312 Value::Variant { name, .. } => Ok(Value::Bool(name == "Some")),
313 other => Err(format!("option.is_some expected Option, got {other:?}")),
314 },
315 ("option", "is_none") => match first_arg(args)? {
316 Value::Variant { name, .. } => Ok(Value::Bool(name == "None")),
317 other => Err(format!("option.is_none expected Option, got {other:?}")),
318 },
319
320 ("result", "is_ok") => match first_arg(args)? {
322 Value::Variant { name, .. } => Ok(Value::Bool(name == "Ok")),
323 other => Err(format!("result.is_ok expected Result, got {other:?}")),
324 },
325 ("result", "is_err") => match first_arg(args)? {
326 Value::Variant { name, .. } => Ok(Value::Bool(name == "Err")),
327 other => Err(format!("result.is_err expected Result, got {other:?}")),
328 },
329 ("result", "unwrap_or") => {
330 let res = first_arg(args)?;
331 let default = args.get(1).cloned().unwrap_or(Value::Unit);
332 match res {
333 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
334 Value::Variant { name, .. } if name == "Err" => Ok(default),
335 other => Err(format!("result.unwrap_or expected Result, got {other:?}")),
336 }
337 }
338
339 ("json", "stringify") => {
341 let v = first_arg(args)?;
342 Ok(Value::Str(serde_json::to_string(&value_to_json(v)).unwrap_or_default().into()))
343 }
344 ("json", "parse") => {
345 let s = expect_str(args.first())?;
346 match serde_json::from_str::<serde_json::Value>(&s) {
347 Ok(v) => Ok(ok_v(json_to_value(&v))),
348 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
349 }
350 }
351 ("json", "parse_strict") => {
354 let s = expect_str(args.first())?;
355 let required = required_field_names(args.get(1))?;
356 let schema = extract_type_schema(args.get(2));
357 match serde_json::from_str::<serde_json::Value>(&s) {
358 Ok(v) => {
359 if let Err(e) = check_required_fields(&v, &required) {
360 return Ok(err_v(Value::Str(e.into())));
361 }
362 if let Err(e) = validate_field_types(&v, &schema) {
363 return Ok(err_v(Value::Str(e.into())));
364 }
365 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
366 }
367 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
368 }
369 }
370
371 ("toml", "parse") => {
376 let s = expect_str(args.first())?;
377 match toml::from_str::<serde_json::Value>(&s) {
378 Ok(mut v) => {
379 unwrap_toml_datetime_markers(&mut v);
380 Ok(ok_v(json_to_value(&v)))
381 }
382 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
383 }
384 }
385 ("json", "parse_strict_typed") => {
389 let s = expect_str(args.first())?;
390 let required = required_field_names(args.get(1))?;
391 let schema = extract_type_schema(args.get(2));
392 match serde_json::from_str::<serde_json::Value>(&s) {
393 Ok(v) => {
394 if let Err(e) = check_required_fields(&v, &required) {
395 return Ok(err_v(Value::Str(e.into())));
396 }
397 if let Err(e) = validate_field_types(&v, &schema) {
398 return Ok(err_v(Value::Str(e.into())));
399 }
400 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
401 }
402 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
403 }
404 }
405
406 ("toml", "parse_strict") => {
409 let s = expect_str(args.first())?;
410 let required = required_field_names(args.get(1))?;
411 let schema = extract_type_schema(args.get(2));
412 match toml::from_str::<serde_json::Value>(&s) {
413 Ok(mut v) => {
414 unwrap_toml_datetime_markers(&mut v);
415 if let Err(e) = check_required_fields(&v, &required) {
416 return Ok(err_v(Value::Str(e.into())));
417 }
418 if let Err(e) = validate_field_types(&v, &schema) {
419 return Ok(err_v(Value::Str(e.into())));
420 }
421 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
422 }
423 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
424 }
425 }
426 ("toml", "parse_strict_typed") => {
427 let s = expect_str(args.first())?;
428 let required = required_field_names(args.get(1))?;
429 let schema = extract_type_schema(args.get(2));
430 match toml::from_str::<serde_json::Value>(&s) {
431 Ok(mut v) => {
432 unwrap_toml_datetime_markers(&mut v);
433 if let Err(e) = check_required_fields(&v, &required) {
434 return Ok(err_v(Value::Str(e.into())));
435 }
436 if let Err(e) = validate_field_types(&v, &schema) {
437 return Ok(err_v(Value::Str(e.into())));
438 }
439 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
440 }
441 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
442 }
443 }
444 ("toml", "stringify") => {
445 let v = first_arg(args)?;
446 let json = value_to_json(v);
452 match toml::to_string(&json) {
453 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
454 Err(e) => Ok(err_v(Value::Str(format!("toml.stringify: {e}").into()))),
455 }
456 }
457
458 ("yaml", "parse") => {
464 let s = expect_str(args.first())?;
465 match serde_yaml::from_str::<serde_json::Value>(&s) {
466 Ok(v) => Ok(ok_v(json_to_value(&v))),
467 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
468 }
469 }
470 ("yaml", "parse_strict") => {
473 let s = expect_str(args.first())?;
474 let required = required_field_names(args.get(1))?;
475 let schema = extract_type_schema(args.get(2));
476 match serde_yaml::from_str::<serde_json::Value>(&s) {
477 Ok(v) => {
478 if let Err(e) = check_required_fields(&v, &required) {
479 return Ok(err_v(Value::Str(e.into())));
480 }
481 if let Err(e) = validate_field_types(&v, &schema) {
482 return Ok(err_v(Value::Str(e.into())));
483 }
484 Ok(ok_v(json_to_value(&v)))
485 }
486 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
487 }
488 }
489 ("yaml", "parse_strict_typed") => {
490 let s = expect_str(args.first())?;
491 let required = required_field_names(args.get(1))?;
492 let schema = extract_type_schema(args.get(2));
493 match serde_yaml::from_str::<serde_json::Value>(&s) {
494 Ok(v) => {
495 if let Err(e) = check_required_fields(&v, &required) {
496 return Ok(err_v(Value::Str(e.into())));
497 }
498 if let Err(e) = validate_field_types(&v, &schema) {
499 return Ok(err_v(Value::Str(e.into())));
500 }
501 Ok(ok_v(json_to_value(&v)))
502 }
503 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
504 }
505 }
506 ("yaml", "stringify") => {
507 let v = first_arg(args)?;
508 let json = value_to_json(v);
509 match serde_yaml::to_string(&json) {
510 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
511 Err(e) => Ok(err_v(Value::Str(format!("yaml.stringify: {e}").into()))),
512 }
513 }
514
515 ("dotenv", "parse") => {
523 use std::collections::BTreeMap;
524 use lex_bytecode::MapKey;
525 let s = expect_str(args.first())?;
526 match parse_dotenv(&s) {
527 Ok(map) => {
528 let mut bt: BTreeMap<MapKey, Value> = BTreeMap::new();
529 for (k, v) in map {
530 bt.insert(MapKey::Str(k), Value::Str(v.into()));
531 }
532 Ok(ok_v(Value::Map(bt)))
533 }
534 Err(e) => Ok(err_v(Value::Str(e.into()))),
535 }
536 }
537
538 ("csv", "parse") => {
543 let s = expect_str(args.first())?;
544 let mut rdr = csv::ReaderBuilder::new()
545 .has_headers(false)
546 .flexible(true)
547 .from_reader(s.as_bytes());
548 let mut rows: std::collections::VecDeque<Value> = std::collections::VecDeque::new();
549 for r in rdr.records() {
550 match r {
551 Ok(rec) => {
552 let row: std::collections::VecDeque<Value> = rec.iter()
553 .map(|f| Value::Str(f.into()))
554 .collect();
555 rows.push_back(Value::List(row));
556 }
557 Err(e) => return Ok(err_v(Value::Str(format!("csv.parse: {e}").into()))),
558 }
559 }
560 Ok(ok_v(Value::List(rows)))
561 }
562 ("csv", "stringify") => {
563 let v = first_arg(args)?;
568 let rows = match v {
569 Value::List(rs) => rs,
570 _ => return Ok(err_v(Value::Str("csv.stringify expects List[List[Str]]".into()))),
571 };
572 let mut out = Vec::new();
573 {
574 let mut wtr = csv::WriterBuilder::new()
575 .has_headers(false)
576 .from_writer(&mut out);
577 for row in rows {
578 let cells = match row {
579 Value::List(cs) => cs,
580 _ => return Ok(err_v(Value::Str("csv.stringify row must be List[Str]".into()))),
581 };
582 let strs: Vec<String> = cells.iter().map(|c| match c {
583 Value::Str(s) => s.to_string(),
584 other => serde_json::to_string(&other.to_json())
585 .unwrap_or_else(|_| String::new()),
586 }).collect();
587 if let Err(e) = wtr.write_record(&strs) {
588 return Ok(err_v(Value::Str(format!("csv.stringify: {e}").into())));
589 }
590 }
591 if let Err(e) = wtr.flush() {
592 return Ok(err_v(Value::Str(format!("csv.stringify flush: {e}").into())));
593 }
594 }
595 match String::from_utf8(out) {
596 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
597 Err(e) => Ok(err_v(Value::Str(format!("csv.stringify utf8: {e}").into()))),
598 }
599 }
600
601 ("test", "assert_eq") => {
608 let a = first_arg(args)?;
609 let b = args.get(1).ok_or("test.assert_eq: missing second arg")?;
610 if a == b {
611 Ok(ok_v(Value::Unit))
612 } else {
613 Ok(err_v(Value::Str(format!("assert_eq: lhs {} != rhs {}",
614 value_to_json(a), value_to_json(b)).into())))
615 }
616 }
617 ("test", "assert_ne") => {
618 let a = first_arg(args)?;
619 let b = args.get(1).ok_or("test.assert_ne: missing second arg")?;
620 if a != b {
621 Ok(ok_v(Value::Unit))
622 } else {
623 Ok(err_v(Value::Str(format!("assert_ne: both sides are {}",
624 value_to_json(a)).into())))
625 }
626 }
627 ("test", "assert_true") => {
628 match first_arg(args)? {
629 Value::Bool(true) => Ok(ok_v(Value::Unit)),
630 Value::Bool(false) => Ok(err_v(Value::Str("assert_true: was false".into()))),
631 other => Err(format!("test.assert_true expects Bool, got {other:?}")),
632 }
633 }
634 ("test", "assert_false") => {
635 match first_arg(args)? {
636 Value::Bool(false) => Ok(ok_v(Value::Unit)),
637 Value::Bool(true) => Ok(err_v(Value::Str("assert_false: was true".into()))),
638 other => Err(format!("test.assert_false expects Bool, got {other:?}")),
639 }
640 }
641
642 ("bytes", "len") => {
644 let b = expect_bytes(args.first())?;
645 Ok(Value::Int(b.len() as i64))
646 }
647 ("bytes", "eq") => {
648 let a = expect_bytes(args.first())?;
649 let b = expect_bytes(args.get(1))?;
650 Ok(Value::Bool(a == b))
651 }
652 ("bytes", "from_str") => {
653 let s = expect_str(args.first())?;
654 Ok(Value::Bytes(s.into_bytes()))
655 }
656 ("bytes", "to_str") => {
657 let b = expect_bytes(args.first())?;
658 match String::from_utf8(b.to_vec()) {
659 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
660 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
661 }
662 }
663 ("bytes", "slice") => {
664 let b = expect_bytes(args.first())?;
665 let lo = expect_int(args.get(1))? as usize;
666 let hi = expect_int(args.get(2))? as usize;
667 if lo > hi || hi > b.len() {
668 return Err(format!("bytes.slice: out of range [{lo}..{hi}] of {}", b.len()));
669 }
670 Ok(Value::Bytes(b[lo..hi].to_vec()))
671 }
672 ("bytes", "is_empty") => {
673 let b = expect_bytes(args.first())?;
674 Ok(Value::Bool(b.is_empty()))
675 }
676
677 ("math", "exp") => Ok(Value::Float(expect_float(args.first())?.exp())),
684 ("math", "log") => Ok(Value::Float(expect_float(args.first())?.ln())),
685 ("math", "log2") => Ok(Value::Float(expect_float(args.first())?.log2())),
686 ("math", "log10") => Ok(Value::Float(expect_float(args.first())?.log10())),
687 ("math", "sqrt") => Ok(Value::Float(expect_float(args.first())?.sqrt())),
688 ("math", "abs") => Ok(Value::Float(expect_float(args.first())?.abs())),
689 ("math", "sin") => Ok(Value::Float(expect_float(args.first())?.sin())),
690 ("math", "cos") => Ok(Value::Float(expect_float(args.first())?.cos())),
691 ("math", "tan") => Ok(Value::Float(expect_float(args.first())?.tan())),
692 ("math", "asin") => Ok(Value::Float(expect_float(args.first())?.asin())),
693 ("math", "acos") => Ok(Value::Float(expect_float(args.first())?.acos())),
694 ("math", "atan") => Ok(Value::Float(expect_float(args.first())?.atan())),
695 ("math", "floor") => Ok(Value::Float(expect_float(args.first())?.floor())),
696 ("math", "ceil") => Ok(Value::Float(expect_float(args.first())?.ceil())),
697 ("math", "round") => Ok(Value::Float(expect_float(args.first())?.round())),
698 ("math", "trunc") => Ok(Value::Float(expect_float(args.first())?.trunc())),
699 ("math", "pow") => {
700 let a = expect_float(args.first())?;
701 let b = expect_float(args.get(1))?;
702 Ok(Value::Float(a.powf(b)))
703 }
704 ("math", "atan2") => {
705 let y = expect_float(args.first())?;
706 let x = expect_float(args.get(1))?;
707 Ok(Value::Float(y.atan2(x)))
708 }
709 ("math", "min") => {
710 let a = expect_float(args.first())?;
711 let b = expect_float(args.get(1))?;
712 Ok(Value::Float(a.min(b)))
713 }
714 ("math", "max") => {
715 let a = expect_float(args.first())?;
716 let b = expect_float(args.get(1))?;
717 Ok(Value::Float(a.max(b)))
718 }
719 ("math", "zeros") => {
720 let r = expect_int(args.first())?;
721 let c = expect_int(args.get(1))?;
722 if r < 0 || c < 0 {
723 return Err(format!("math.zeros: negative dim {r}x{c}"));
724 }
725 let r = r as usize; let c = c as usize;
726 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![0.0; r * c] })
727 }
728 ("math", "ones") => {
729 let r = expect_int(args.first())?;
730 let c = expect_int(args.get(1))?;
731 if r < 0 || c < 0 {
732 return Err(format!("math.ones: negative dim {r}x{c}"));
733 }
734 let r = r as usize; let c = c as usize;
735 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![1.0; r * c] })
736 }
737 ("math", "from_lists") => {
738 let rows = expect_list(args.first())?;
739 let r = rows.len();
740 if r == 0 {
741 return Ok(Value::F64Array { rows: 0, cols: 0, data: Vec::new() });
742 }
743 let first_row = match &rows[0] {
744 Value::List(xs) => xs,
745 other => return Err(format!("math.from_lists: row 0 not List, got {other:?}")),
746 };
747 let c = first_row.len();
748 let mut data = Vec::with_capacity(r * c);
749 for (i, row) in rows.iter().enumerate() {
750 let row = match row {
751 Value::List(xs) => xs,
752 other => return Err(format!("math.from_lists: row {i} not List, got {other:?}")),
753 };
754 if row.len() != c {
755 return Err(format!("math.from_lists: row {i} has {} cols, expected {c}", row.len()));
756 }
757 for (j, v) in row.iter().enumerate() {
758 let f = match v {
759 Value::Float(f) => *f,
760 Value::Int(n) => *n as f64,
761 other => return Err(format!("math.from_lists: ({i},{j}) not numeric, got {other:?}")),
762 };
763 data.push(f);
764 }
765 }
766 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
767 }
768 ("math", "from_flat") => {
769 let r = expect_int(args.first())?;
770 let c = expect_int(args.get(1))?;
771 let xs = expect_list(args.get(2))?;
772 if r < 0 || c < 0 {
773 return Err(format!("math.from_flat: negative dim {r}x{c}"));
774 }
775 let r = r as usize; let c = c as usize;
776 if xs.len() != r * c {
777 return Err(format!("math.from_flat: list len {} != {}*{}", xs.len(), r, c));
778 }
779 let mut data = Vec::with_capacity(r * c);
780 for v in xs {
781 data.push(match v {
782 Value::Float(f) => *f,
783 Value::Int(n) => *n as f64,
784 other => return Err(format!("math.from_flat: non-numeric element {other:?}")),
785 });
786 }
787 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
788 }
789 ("math", "rows") => {
790 let (r, _, _) = unpack_matrix(first_arg(args)?)?;
791 Ok(Value::Int(r as i64))
792 }
793 ("math", "cols") => {
794 let (_, c, _) = unpack_matrix(first_arg(args)?)?;
795 Ok(Value::Int(c as i64))
796 }
797 ("math", "get") => {
798 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
799 let i = expect_int(args.get(1))? as usize;
800 let j = expect_int(args.get(2))? as usize;
801 if i >= r || j >= c {
802 return Err(format!("math.get: ({i},{j}) out of {r}x{c}"));
803 }
804 Ok(Value::Float(data[i * c + j]))
805 }
806 ("math", "to_flat") => {
807 let (_, _, data) = unpack_matrix(first_arg(args)?)?;
808 Ok(Value::List(data.into_iter().map(Value::Float).collect()))
809 }
810 ("math", "transpose") => {
811 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
812 let mut out = vec![0.0; r * c];
813 for i in 0..r {
814 for j in 0..c {
815 out[j * r + i] = data[i * c + j];
816 }
817 }
818 Ok(Value::F64Array { rows: c as u32, cols: r as u32, data: out })
819 }
820 ("math", "matmul") => {
821 let (m, k1, a) = unpack_matrix(first_arg(args)?)?;
822 let (k2, n, b) = unpack_matrix(args.get(1).ok_or("math.matmul: missing arg 1")?)?;
823 if k1 != k2 {
824 return Err(format!("math.matmul: dim mismatch {m}x{k1} · {k2}x{n}"));
825 }
826 let mut c = vec![0.0; m * n];
830 for i in 0..m {
831 for kk in 0..k1 {
832 let aik = a[i * k1 + kk];
833 for j in 0..n {
834 c[i * n + j] += aik * b[kk * n + j];
835 }
836 }
837 }
838 Ok(Value::F64Array { rows: m as u32, cols: n as u32, data: c })
839 }
840 ("math", "scale") => {
841 let s = expect_float(args.first())?;
842 let (r, c, mut data) = unpack_matrix(args.get(1).ok_or("math.scale: missing arg 1")?)?;
843 for x in &mut data { *x *= s; }
844 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
845 }
846 ("math", "add") | ("math", "sub") => {
847 let (ar, ac, a) = unpack_matrix(first_arg(args)?)?;
848 let (br, bc, b) = unpack_matrix(args.get(1).ok_or("math.add/sub: missing arg 1")?)?;
849 if ar != br || ac != bc {
850 return Err(format!("math.{op}: shape mismatch {ar}x{ac} vs {br}x{bc}"));
851 }
852 let neg = op == "sub";
853 let mut out = a;
854 for (i, x) in out.iter_mut().enumerate() {
855 if neg { *x -= b[i] } else { *x += b[i] }
856 }
857 Ok(Value::F64Array { rows: ar as u32, cols: ac as u32, data: out })
858 }
859 ("math", "sigmoid") => {
860 let (r, c, mut data) = unpack_matrix(first_arg(args)?)?;
861 for x in &mut data { *x = 1.0 / (1.0 + (-*x).exp()); }
862 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
863 }
864
865 ("map", "new") => Ok(Value::Map(BTreeMap::new())),
867 ("map", "size") => Ok(Value::Int(expect_map(args.first())?.len() as i64)),
868 ("map", "has") => {
869 let m = expect_map(args.first())?;
870 let k = MapKey::from_value(args.get(1).ok_or("map.has: missing key")?)?;
871 Ok(Value::Bool(m.contains_key(&k)))
872 }
873 ("map", "get") => {
874 let m = expect_map(args.first())?;
875 let k = MapKey::from_value(args.get(1).ok_or("map.get: missing key")?)?;
876 Ok(match m.get(&k) {
877 Some(v) => some(v.clone()),
878 None => none(),
879 })
880 }
881 ("map", "set") => {
882 let mut m = expect_map(args.first())?.clone();
883 let k = MapKey::from_value(args.get(1).ok_or("map.set: missing key")?)?;
884 let v = args.get(2).ok_or("map.set: missing value")?.clone();
885 m.insert(k, v);
886 Ok(Value::Map(m))
887 }
888 ("map", "delete") => {
889 let mut m = expect_map(args.first())?.clone();
890 let k = MapKey::from_value(args.get(1).ok_or("map.delete: missing key")?)?;
891 m.remove(&k);
892 Ok(Value::Map(m))
893 }
894 ("map", "keys") => {
895 let m = expect_map(args.first())?;
896 Ok(Value::List(m.keys().cloned().map(MapKey::into_value).collect()))
897 }
898 ("map", "values") => {
899 let m = expect_map(args.first())?;
900 Ok(Value::List(m.values().cloned().collect()))
901 }
902 ("map", "entries") => {
903 let m = expect_map(args.first())?;
904 Ok(Value::List(m.iter()
905 .map(|(k, v)| Value::Tuple(vec![k.as_value(), v.clone()]))
906 .collect()))
907 }
908 ("map", "from_list") => {
909 let pairs = expect_list(args.first())?;
910 let mut m = BTreeMap::new();
911 for p in pairs {
912 let items = match p {
913 Value::Tuple(items) if items.len() == 2 => items,
914 other => return Err(format!(
915 "map.from_list element must be a 2-tuple, got {other:?}")),
916 };
917 let k = MapKey::from_value(&items[0])?;
918 m.insert(k, items[1].clone());
919 }
920 Ok(Value::Map(m))
921 }
922
923 ("set", "new") => Ok(Value::Set(BTreeSet::new())),
925 ("set", "size") => Ok(Value::Int(expect_set(args.first())?.len() as i64)),
926 ("set", "has") => {
927 let s = expect_set(args.first())?;
928 let k = MapKey::from_value(args.get(1).ok_or("set.has: missing element")?)?;
929 Ok(Value::Bool(s.contains(&k)))
930 }
931 ("set", "add") => {
932 let mut s = expect_set(args.first())?.clone();
933 let k = MapKey::from_value(args.get(1).ok_or("set.add: missing element")?)?;
934 s.insert(k);
935 Ok(Value::Set(s))
936 }
937 ("set", "delete") => {
938 let mut s = expect_set(args.first())?.clone();
939 let k = MapKey::from_value(args.get(1).ok_or("set.delete: missing element")?)?;
940 s.remove(&k);
941 Ok(Value::Set(s))
942 }
943 ("set", "to_list") => {
944 let s = expect_set(args.first())?;
945 Ok(Value::List(s.iter().cloned().map(MapKey::into_value).collect()))
946 }
947 ("set", "from_list") => {
948 let xs = expect_list(args.first())?;
949 let mut s = BTreeSet::new();
950 for x in xs {
951 s.insert(MapKey::from_value(x)?);
952 }
953 Ok(Value::Set(s))
954 }
955 ("set", "union") => {
956 let a = expect_set(args.first())?;
957 let b = expect_set(args.get(1))?;
958 Ok(Value::Set(a.union(b).cloned().collect()))
959 }
960 ("set", "intersect") => {
961 let a = expect_set(args.first())?;
962 let b = expect_set(args.get(1))?;
963 Ok(Value::Set(a.intersection(b).cloned().collect()))
964 }
965 ("set", "diff") => {
966 let a = expect_set(args.first())?;
967 let b = expect_set(args.get(1))?;
968 Ok(Value::Set(a.difference(b).cloned().collect()))
969 }
970 ("set", "is_empty") => Ok(Value::Bool(expect_set(args.first())?.is_empty())),
971 ("set", "is_subset") => {
972 let a = expect_set(args.first())?;
973 let b = expect_set(args.get(1))?;
974 Ok(Value::Bool(a.is_subset(b)))
975 }
976
977 ("map", "merge") => {
979 let a = expect_map(args.first())?.clone();
982 let b = expect_map(args.get(1))?;
983 let mut out = a;
984 for (k, v) in b {
985 out.insert(k.clone(), v.clone());
986 }
987 Ok(Value::Map(out))
988 }
989 ("map", "is_empty") => Ok(Value::Bool(expect_map(args.first())?.is_empty())),
990
991 ("deque", "new") => Ok(Value::Deque(std::collections::VecDeque::new())),
993 ("deque", "size") => Ok(Value::Int(expect_deque(args.first())?.len() as i64)),
994 ("deque", "is_empty") => Ok(Value::Bool(expect_deque(args.first())?.is_empty())),
995 ("deque", "push_back") => {
996 let mut d = expect_deque(args.first())?.clone();
997 let x = args.get(1).ok_or("deque.push_back: missing value")?.clone();
998 d.push_back(x);
999 Ok(Value::Deque(d))
1000 }
1001 ("deque", "push_front") => {
1002 let mut d = expect_deque(args.first())?.clone();
1003 let x = args.get(1).ok_or("deque.push_front: missing value")?.clone();
1004 d.push_front(x);
1005 Ok(Value::Deque(d))
1006 }
1007 ("deque", "pop_back") => {
1008 let mut d = expect_deque(args.first())?.clone();
1009 match d.pop_back() {
1010 Some(x) => Ok(Value::Variant {
1011 name: "Some".into(),
1012 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
1013 }),
1014 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1015 }
1016 }
1017 ("deque", "pop_front") => {
1018 let mut d = expect_deque(args.first())?.clone();
1019 match d.pop_front() {
1020 Some(x) => Ok(Value::Variant {
1021 name: "Some".into(),
1022 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
1023 }),
1024 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1025 }
1026 }
1027 ("deque", "peek_back") => {
1028 let d = expect_deque(args.first())?;
1029 match d.back() {
1030 Some(x) => Ok(Value::Variant {
1031 name: "Some".into(),
1032 args: vec![x.clone()],
1033 }),
1034 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1035 }
1036 }
1037 ("deque", "peek_front") => {
1038 let d = expect_deque(args.first())?;
1039 match d.front() {
1040 Some(x) => Ok(Value::Variant {
1041 name: "Some".into(),
1042 args: vec![x.clone()],
1043 }),
1044 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1045 }
1046 }
1047 ("deque", "from_list") => {
1048 let xs = expect_list(args.first())?;
1049 Ok(Value::Deque(xs.iter().cloned().collect()))
1050 }
1051 ("deque", "to_list") => {
1052 let d = expect_deque(args.first())?;
1053 Ok(Value::List(d.iter().cloned().collect()))
1054 }
1055
1056 ("crypto", "sha256") => {
1059 use sha2::{Digest, Sha256};
1060 let data = expect_bytes(args.first())?;
1061 let mut h = Sha256::new();
1062 h.update(data);
1063 Ok(Value::Bytes(h.finalize().to_vec()))
1064 }
1065 ("crypto", "sha512") => {
1066 use sha2::{Digest, Sha512};
1067 let data = expect_bytes(args.first())?;
1068 let mut h = Sha512::new();
1069 h.update(data);
1070 Ok(Value::Bytes(h.finalize().to_vec()))
1071 }
1072 ("crypto", "md5") => {
1073 use md5::{Digest, Md5};
1074 let data = expect_bytes(args.first())?;
1075 let mut h = Md5::new();
1076 h.update(data);
1077 Ok(Value::Bytes(h.finalize().to_vec()))
1078 }
1079 ("crypto", "blake2b") => {
1083 use blake2::{Blake2b512, Digest};
1084 let data = expect_bytes(args.first())?;
1085 let mut h = Blake2b512::new();
1086 h.update(data);
1087 Ok(Value::Bytes(h.finalize().to_vec()))
1088 }
1089 ("crypto", "keccak256") => {
1094 use sha3::{Digest, Keccak256};
1095 let data = expect_bytes(args.first())?;
1096 let mut h = Keccak256::new();
1097 h.update(data);
1098 Ok(Value::Bytes(h.finalize().to_vec()))
1099 }
1100 ("crypto", "sha256_str") => {
1104 use sha2::{Digest, Sha256};
1105 let s = expect_str(args.first())?;
1106 let mut h = Sha256::new();
1107 h.update(s.as_bytes());
1108 Ok(Value::Str(hex::encode(h.finalize()).into()))
1109 }
1110 ("crypto", "sha512_str") => {
1111 use sha2::{Digest, Sha512};
1112 let s = expect_str(args.first())?;
1113 let mut h = Sha512::new();
1114 h.update(s.as_bytes());
1115 Ok(Value::Str(hex::encode(h.finalize()).into()))
1116 }
1117 ("crypto", "hmac_sha256") => {
1118 use hmac::{Hmac, KeyInit, Mac};
1119 type HmacSha256 = Hmac<sha2::Sha256>;
1120 let key = expect_bytes(args.first())?;
1121 let data = expect_bytes(args.get(1))?;
1122 let mut mac = HmacSha256::new_from_slice(key)
1123 .map_err(|e| format!("hmac_sha256 key: {e}"))?;
1124 mac.update(data);
1125 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1126 }
1127 ("crypto", "hmac_sha512") => {
1128 use hmac::{Hmac, KeyInit, Mac};
1129 type HmacSha512 = Hmac<sha2::Sha512>;
1130 let key = expect_bytes(args.first())?;
1131 let data = expect_bytes(args.get(1))?;
1132 let mut mac = HmacSha512::new_from_slice(key)
1133 .map_err(|e| format!("hmac_sha512 key: {e}"))?;
1134 mac.update(data);
1135 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1136 }
1137 ("crypto", "ed25519_public_key") => {
1141 use ed25519_dalek::SigningKey;
1142 let secret = expect_bytes(args.first())?;
1143 let seed: [u8; 32] = match secret.as_slice().try_into() {
1144 Ok(s) => s,
1145 Err(_) => return Ok(err_v(Value::Str("ed25519_public_key: secret must be 32 bytes".into()))),
1146 };
1147 let sk = SigningKey::from_bytes(&seed);
1148 Ok(ok_v(Value::Bytes(sk.verifying_key().to_bytes().to_vec())))
1149 }
1150 ("crypto", "ed25519_sign") => {
1151 use ed25519_dalek::{Signer, SigningKey};
1152 let secret = expect_bytes(args.first())?;
1153 let message = expect_bytes(args.get(1))?;
1154 let seed: [u8; 32] = match secret.as_slice().try_into() {
1155 Ok(s) => s,
1156 Err(_) => return Ok(err_v(Value::Str("ed25519_sign: secret must be 32 bytes".into()))),
1157 };
1158 let sk = SigningKey::from_bytes(&seed);
1159 Ok(ok_v(Value::Bytes(sk.sign(message).to_bytes().to_vec())))
1160 }
1161 ("crypto", "ed25519_verify") => {
1162 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
1163 let public = expect_bytes(args.first())?;
1164 let message = expect_bytes(args.get(1))?;
1165 let sig_bytes = expect_bytes(args.get(2))?;
1166 let pk_arr: [u8; 32] = match public.as_slice().try_into() {
1167 Ok(p) => p,
1168 Err(_) => return Ok(Value::Bool(false)),
1169 };
1170 let sig_arr: [u8; 64] = match sig_bytes.as_slice().try_into() {
1171 Ok(s) => s,
1172 Err(_) => return Ok(Value::Bool(false)),
1173 };
1174 let vk = match VerifyingKey::from_bytes(&pk_arr) {
1175 Ok(v) => v,
1176 Err(_) => return Ok(Value::Bool(false)),
1177 };
1178 let sig = Signature::from_bytes(&sig_arr);
1179 Ok(Value::Bool(vk.verify(message, &sig).is_ok()))
1180 }
1181 ("crypto", "p256_public_key") => {
1193 use p256::ecdsa::SigningKey;
1194 let secret = expect_bytes(args.first())?;
1195 let sk = match SigningKey::from_slice(secret) {
1196 Ok(k) => k,
1197 Err(_) => return Ok(err_v(Value::Str(
1198 "p256_public_key: secret must be a 32-byte P-256 scalar".into()))),
1199 };
1200 let point = sk.verifying_key().to_encoded_point(true);
1201 Ok(ok_v(Value::Bytes(point.as_bytes().to_vec())))
1202 }
1203 ("crypto", "p256_sign") => {
1204 use p256::ecdsa::{signature::Signer, Signature, SigningKey};
1205 let secret = expect_bytes(args.first())?;
1206 let message = expect_bytes(args.get(1))?;
1207 let sk = match SigningKey::from_slice(secret) {
1208 Ok(k) => k,
1209 Err(_) => return Ok(err_v(Value::Str(
1210 "p256_sign: secret must be a 32-byte P-256 scalar".into()))),
1211 };
1212 let sig: Signature = sk.sign(message);
1213 Ok(ok_v(Value::Bytes(sig.to_der().as_bytes().to_vec())))
1214 }
1215 ("crypto", "p256_verify") => {
1216 use p256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
1217 let public = expect_bytes(args.first())?;
1218 let message = expect_bytes(args.get(1))?;
1219 let sig_bytes = expect_bytes(args.get(2))?;
1220 let vk = match VerifyingKey::from_sec1_bytes(public) {
1221 Ok(v) => v,
1222 Err(_) => return Ok(Value::Bool(false)),
1223 };
1224 let sig = match Signature::from_der(sig_bytes) {
1225 Ok(s) => s,
1226 Err(_) => return Ok(Value::Bool(false)),
1227 };
1228 Ok(Value::Bool(vk.verify(message, &sig).is_ok()))
1229 }
1230 ("crypto", "secp256k1_public_key") => {
1241 use k256::ecdsa::SigningKey;
1242 let secret = expect_bytes(args.first())?;
1243 let sk = match SigningKey::from_slice(secret) {
1244 Ok(k) => k,
1245 Err(_) => return Ok(err_v(Value::Str(
1246 "secp256k1_public_key: secret must be a 32-byte secp256k1 scalar".into()))),
1247 };
1248 let point = sk.verifying_key().to_encoded_point(false);
1251 Ok(ok_v(Value::Bytes(point.as_bytes().to_vec())))
1252 }
1253 ("crypto", "secp256k1_sign_digest") => {
1254 use k256::ecdsa::SigningKey;
1255 let secret = expect_bytes(args.first())?;
1256 let digest = expect_bytes(args.get(1))?;
1257 if digest.len() != 32 {
1258 return Ok(err_v(Value::Str(
1259 "secp256k1_sign_digest: digest must be exactly 32 bytes".into())));
1260 }
1261 let sk = match SigningKey::from_slice(secret) {
1262 Ok(k) => k,
1263 Err(_) => return Ok(err_v(Value::Str(
1264 "secp256k1_sign_digest: secret must be a 32-byte secp256k1 scalar".into()))),
1265 };
1266 match sk.sign_prehash_recoverable(digest) {
1269 Ok((sig, recid)) => {
1270 let mut out = sig.to_bytes().to_vec(); out.push(27u8 + recid.to_byte());
1272 Ok(ok_v(Value::Bytes(out)))
1273 }
1274 Err(e) => Ok(err_v(Value::Str(
1275 format!("secp256k1_sign_digest: {e}").into()))),
1276 }
1277 }
1278 ("crypto", "secp256k1_recover") => {
1279 use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
1280 let digest = expect_bytes(args.first())?;
1281 let sig_bytes = expect_bytes(args.get(1))?;
1282 if digest.len() != 32 {
1283 return Ok(err_v(Value::Str(
1284 "secp256k1_recover: digest must be exactly 32 bytes".into())));
1285 }
1286 if sig_bytes.len() != 65 {
1287 return Ok(err_v(Value::Str(
1288 "secp256k1_recover: signature must be 65 bytes (r‖s‖v)".into())));
1289 }
1290 let sig = match Signature::from_slice(&sig_bytes[..64]) {
1291 Ok(s) => s,
1292 Err(_) => return Ok(err_v(Value::Str(
1293 "secp256k1_recover: malformed r‖s".into()))),
1294 };
1295 let v = sig_bytes[64];
1297 let recid_byte = if v >= 27 { v - 27 } else { v };
1298 let recid = match RecoveryId::from_byte(recid_byte) {
1299 Some(r) => r,
1300 None => return Ok(err_v(Value::Str(
1301 "secp256k1_recover: invalid recovery id".into()))),
1302 };
1303 match VerifyingKey::recover_from_prehash(digest, &sig, recid) {
1304 Ok(vk) => Ok(ok_v(Value::Bytes(
1305 vk.to_encoded_point(false).as_bytes().to_vec()))),
1306 Err(e) => Ok(err_v(Value::Str(
1307 format!("secp256k1_recover: {e}").into()))),
1308 }
1309 }
1310 ("crypto", "secp256k1_verify") => {
1311 use k256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};
1312 let public = expect_bytes(args.first())?;
1313 let digest = expect_bytes(args.get(1))?;
1314 let sig_bytes = expect_bytes(args.get(2))?;
1315 if digest.len() != 32 {
1316 return Ok(Value::Bool(false));
1317 }
1318 let vk = match VerifyingKey::from_sec1_bytes(public) {
1319 Ok(v) => v,
1320 Err(_) => return Ok(Value::Bool(false)),
1321 };
1322 let rs = if sig_bytes.len() == 65 { &sig_bytes[..64] } else { sig_bytes.as_slice() };
1324 let sig = match Signature::from_slice(rs) {
1325 Ok(s) => s,
1326 Err(_) => return Ok(Value::Bool(false)),
1327 };
1328 Ok(Value::Bool(vk.verify_prehash(digest, &sig).is_ok()))
1329 }
1330 ("crypto", "base64_encode") => {
1331 use base64::{Engine, engine::general_purpose::STANDARD};
1332 let data = expect_bytes(args.first())?;
1333 Ok(Value::Str(STANDARD.encode(data).into()))
1334 }
1335 ("crypto", "base64_decode") => {
1336 use base64::{Engine, engine::general_purpose::STANDARD};
1337 let s = expect_str(args.first())?;
1338 match STANDARD.decode(s) {
1339 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1340 Err(e) => Ok(err_v(Value::Str(format!("base64: {e}").into()))),
1341 }
1342 }
1343 ("crypto", "base64url_encode") => {
1347 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
1348 let data = expect_bytes(args.first())?;
1349 Ok(Value::Str(URL_SAFE_NO_PAD.encode(data).into()))
1350 }
1351 ("crypto", "base64url_decode") => {
1352 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
1353 let s = expect_str(args.first())?;
1354 match URL_SAFE_NO_PAD.decode(s) {
1355 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1356 Err(e) => Ok(err_v(Value::Str(format!("base64url: {e}").into()))),
1357 }
1358 }
1359 ("crypto", "hex_encode") => {
1360 let data = expect_bytes(args.first())?;
1361 Ok(Value::Str(hex::encode(data).into()))
1362 }
1363 ("crypto", "hex_decode") => {
1364 let s = expect_str(args.first())?;
1365 match hex::decode(s) {
1366 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1367 Err(e) => Ok(err_v(Value::Str(format!("hex: {e}").into()))),
1368 }
1369 }
1370 ("crypto", "base58_encode") => {
1374 let data = expect_bytes(args.first())?;
1375 Ok(Value::Str(bs58::encode(data).into_string().into()))
1376 }
1377 ("crypto", "base58_decode") => {
1378 let s = expect_str(args.first())?;
1379 match bs58::decode(s).into_vec() {
1380 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1381 Err(e) => Ok(err_v(Value::Str(format!("base58: {e}").into()))),
1382 }
1383 }
1384 ("crypto", "constant_time_eq") | ("crypto", "eq") => {
1385 use subtle::ConstantTimeEq;
1386 let a = expect_bytes(args.first())?;
1387 let b = expect_bytes(args.get(1))?;
1388 let eq = if a.len() == b.len() {
1397 a.ct_eq(b).into()
1398 } else {
1399 false
1400 };
1401 Ok(Value::Bool(eq))
1402 }
1403 ("crypto", "eq_str") => {
1406 use subtle::ConstantTimeEq;
1407 let a = expect_str(args.first())?;
1408 let b = expect_str(args.get(1))?;
1409 let eq = if a.len() == b.len() {
1410 a.as_bytes().ct_eq(b.as_bytes()).into()
1411 } else {
1412 false
1413 };
1414 Ok(Value::Bool(eq))
1415 }
1416
1417 ("crypto", "aes_gcm_seal") => Ok(aes_gcm_seal_impl(args)),
1427 ("crypto", "aes_gcm_open") => Ok(aes_gcm_open_impl(args)),
1428 ("crypto", "chacha20_poly1305_seal") => Ok(chacha20_seal_impl(args)),
1429 ("crypto", "chacha20_poly1305_open") => Ok(chacha20_open_impl(args)),
1430 ("crypto", "pbkdf2_sha256") => Ok(pbkdf2_sha256_impl(args)),
1431 ("crypto", "hkdf_sha256") => Ok(hkdf_sha256_impl(args)),
1432 ("crypto", "argon2id") => Ok(argon2id_impl(args)),
1433
1434 ("random", "seed") => {
1441 let s = args.first().ok_or("random.seed: missing arg")?.as_int();
1442 let mixed = splitmix64(s as u64).0;
1447 Ok(rng_value(mixed))
1448 }
1449 ("random", "int") => {
1450 let state = rng_decode(args.first())?;
1451 let lo = args.get(1).ok_or("random.int: missing lo")?.as_int();
1452 let hi = args.get(2).ok_or("random.int: missing hi")?.as_int();
1453 if hi < lo {
1454 return Err(format!(
1455 "random.int: hi ({hi}) must be >= lo ({lo})"));
1456 }
1457 let span = (hi as i128) - (lo as i128) + 1;
1458 let (raw, next_state) = splitmix64(state);
1459 let drawn = lo as i128 + (raw as u128 % span as u128) as i128;
1464 Ok(Value::Tuple(vec![
1465 Value::Int(drawn as i64),
1466 rng_value(next_state),
1467 ]))
1468 }
1469 ("random", "float") => {
1470 let state = rng_decode(args.first())?;
1471 let (raw, next_state) = splitmix64(state);
1472 let f = ((raw >> 11) as f64) / ((1u64 << 53) as f64);
1475 Ok(Value::Tuple(vec![Value::Float(f), rng_value(next_state)]))
1476 }
1477 ("random", "choose") => {
1478 let state = rng_decode(args.first())?;
1479 let xs = match args.get(1) {
1480 Some(Value::List(xs)) => xs,
1481 _ => return Err("random.choose: expected List".into()),
1482 };
1483 if xs.is_empty() {
1484 return Ok(Value::Variant {
1485 name: "None".into(), args: vec![],
1486 });
1487 }
1488 let (raw, next_state) = splitmix64(state);
1489 let idx = (raw as usize) % xs.len();
1490 let pick = xs[idx].clone();
1491 Ok(Value::Variant {
1492 name: "Some".into(),
1493 args: vec![Value::Tuple(vec![pick, rng_value(next_state)])],
1494 })
1495 }
1496
1497 ("parser", "char") => {
1502 let s = expect_str(args.first())?;
1503 if s.chars().count() != 1 {
1504 return Err(format!(
1505 "parser.char: expected 1-character string, got {s:?}"));
1506 }
1507 Ok(parser_node("Char", &[("ch", Value::Str(s.into()))]))
1508 }
1509 ("parser", "string") => {
1510 let s = expect_str(args.first())?;
1511 Ok(parser_node("String", &[("s", Value::Str(s.into()))]))
1512 }
1513 ("parser", "digit") => Ok(parser_node("Digit", &[])),
1514 ("parser", "alpha") => Ok(parser_node("Alpha", &[])),
1515 ("parser", "whitespace") => Ok(parser_node("Whitespace", &[])),
1516 ("parser", "eof") => Ok(parser_node("Eof", &[])),
1517 ("parser", "seq") => {
1518 let a = args.first().cloned()
1519 .ok_or_else(|| "parser.seq: missing first parser".to_string())?;
1520 let b = args.get(1).cloned()
1521 .ok_or_else(|| "parser.seq: missing second parser".to_string())?;
1522 Ok(parser_node("Seq", &[("a", a), ("b", b)]))
1523 }
1524 ("parser", "alt") => {
1525 let a = args.first().cloned()
1526 .ok_or_else(|| "parser.alt: missing first parser".to_string())?;
1527 let b = args.get(1).cloned()
1528 .ok_or_else(|| "parser.alt: missing second parser".to_string())?;
1529 Ok(parser_node("Alt", &[("a", a), ("b", b)]))
1530 }
1531 ("parser", "many") => {
1532 let p = args.first().cloned()
1533 .ok_or_else(|| "parser.many: missing inner parser".to_string())?;
1534 Ok(parser_node("Many", &[("p", p)]))
1535 }
1536 ("parser", "optional") => {
1537 let p = args.first().cloned()
1538 .ok_or_else(|| "parser.optional: missing inner parser".to_string())?;
1539 Ok(parser_node("Optional", &[("p", p)]))
1540 }
1541 ("parser", "map") => {
1545 let p = args.first().cloned()
1546 .ok_or_else(|| "parser.map: missing parser".to_string())?;
1547 let f = args.get(1).cloned()
1548 .ok_or_else(|| "parser.map: missing closure".to_string())?;
1549 Ok(parser_node("Map", &[("p", p), ("f", f)]))
1550 }
1551 ("parser", "and_then") => {
1552 let p = args.first().cloned()
1553 .ok_or_else(|| "parser.and_then: missing parser".to_string())?;
1554 let f = args.get(1).cloned()
1555 .ok_or_else(|| "parser.and_then: missing closure".to_string())?;
1556 Ok(parser_node("AndThen", &[("p", p), ("f", f)]))
1557 }
1558 ("regex", "compile") => {
1568 let pat = expect_str(args.first())?;
1569 match get_or_compile_regex(&pat) {
1570 Ok(_) => Ok(ok_v(Value::Str(pat.into()))),
1571 Err(e) => Ok(err_v(Value::Str(e.into()))),
1572 }
1573 }
1574 ("regex", "is_match") => {
1575 let pat = expect_str(args.first())?;
1576 let s = expect_str(args.get(1))?;
1577 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.is_match: {e}"))?;
1578 Ok(Value::Bool(re.is_match(&s)))
1579 }
1580 ("regex", "is_match_str") => {
1585 let pat = expect_str(args.first())?;
1586 let s = expect_str(args.get(1))?;
1587 match get_or_compile_regex(&pat) {
1588 Ok(re) => Ok(Value::Bool(re.is_match(&s))),
1589 Err(_) => Ok(Value::Bool(false)),
1590 }
1591 }
1592 ("regex", "find") => {
1593 let pat = expect_str(args.first())?;
1594 let s = expect_str(args.get(1))?;
1595 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find: {e}"))?;
1596 match re.captures(&s) {
1597 Some(caps) => Ok(Value::Variant {
1598 name: "Some".into(),
1599 args: vec![match_value(&caps)],
1600 }),
1601 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1602 }
1603 }
1604 ("regex", "find_all") => {
1605 let pat = expect_str(args.first())?;
1606 let s = expect_str(args.get(1))?;
1607 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find_all: {e}"))?;
1608 let items: std::collections::VecDeque<Value> = re.captures_iter(&s).map(|caps| match_value(&caps)).collect();
1609 Ok(Value::List(items))
1610 }
1611 ("regex", "replace") => {
1612 let pat = expect_str(args.first())?;
1613 let s = expect_str(args.get(1))?;
1614 let rep = expect_str(args.get(2))?;
1615 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace: {e}"))?;
1616 Ok(Value::Str(re.replace(&s, rep.as_str()).into_owned().into()))
1617 }
1618 ("regex", "replace_all") => {
1619 let pat = expect_str(args.first())?;
1620 let s = expect_str(args.get(1))?;
1621 let rep = expect_str(args.get(2))?;
1622 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace_all: {e}"))?;
1623 Ok(Value::Str(re.replace_all(&s, rep.as_str()).into_owned().into()))
1624 }
1625 ("datetime", "parse_iso") => {
1628 let s = expect_str(args.first())?;
1629 match chrono::DateTime::parse_from_rfc3339(&s) {
1630 Ok(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1631 Err(e) => Ok(err_v(Value::Str(format!("parse_iso: {e}").into()))),
1632 }
1633 }
1634 ("datetime", "format_iso") => {
1635 let n = expect_int(args.first())?;
1636 Ok(Value::Str(format_iso(n).into()))
1637 }
1638 ("datetime", "parse") => {
1639 let s = expect_str(args.first())?;
1640 let fmt = expect_str(args.get(1))?;
1641 match chrono::NaiveDateTime::parse_from_str(&s, &fmt) {
1642 Ok(naive) => {
1643 use chrono::TimeZone;
1644 match chrono::Utc.from_local_datetime(&naive).single() {
1645 Some(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1646 None => Ok(err_v(Value::Str("parse: ambiguous local time".into()))),
1647 }
1648 }
1649 Err(e) => Ok(err_v(Value::Str(format!("parse: {e}").into()))),
1650 }
1651 }
1652 ("datetime", "format") => {
1653 let n = expect_int(args.first())?;
1654 let fmt = expect_str(args.get(1))?;
1655 let dt = chrono_from_instant(n);
1656 Ok(Value::Str(dt.format(&fmt).to_string().into()))
1657 }
1658 ("datetime", "to_components") => {
1659 let n = expect_int(args.first())?;
1660 let tz = match parse_tz_arg(args.get(1)) {
1661 Ok(t) => t,
1662 Err(e) => return Ok(err_v(Value::Str(e.into()))),
1663 };
1664 match resolve_tz_to_components(n, &tz) {
1665 Ok(rec) => Ok(ok_v(rec)),
1666 Err(e) => Ok(err_v(Value::Str(e.into()))),
1667 }
1668 }
1669 ("datetime", "from_components") => {
1670 let rec = match args.first() {
1671 Some(Value::Record { fields: r, .. }) => r.clone(),
1672 _ => return Err("from_components: expected DateTime record".into()),
1673 };
1674 match instant_from_components(&rec) {
1675 Ok(n) => Ok(ok_v(Value::Int(n))),
1676 Err(e) => Ok(err_v(Value::Str(e.into()))),
1677 }
1678 }
1679 ("datetime", "add") => {
1680 let a = expect_int(args.first())?;
1681 let d = expect_int(args.get(1))?;
1682 Ok(Value::Int(a.saturating_add(d)))
1683 }
1684 ("datetime", "diff") => {
1685 let a = expect_int(args.first())?;
1686 let b = expect_int(args.get(1))?;
1687 Ok(Value::Int(a.saturating_sub(b)))
1688 }
1689 ("datetime", "duration_seconds") => {
1690 let s = expect_float(args.first())?;
1691 let nanos = (s * 1_000_000_000.0) as i64;
1692 Ok(Value::Int(nanos))
1693 }
1694 ("datetime", "duration_minutes") => {
1695 let m = expect_int(args.first())?;
1696 Ok(Value::Int(m.saturating_mul(60_000_000_000)))
1697 }
1698 ("datetime", "duration_days") => {
1699 let d = expect_int(args.first())?;
1700 Ok(Value::Int(d.saturating_mul(86_400_000_000_000)))
1701 }
1702 ("datetime", "before") => {
1704 let a = expect_int(args.first())?;
1705 let b = expect_int(args.get(1))?;
1706 Ok(Value::Bool(a < b))
1707 }
1708 ("datetime", "after") => {
1709 let a = expect_int(args.first())?;
1710 let b = expect_int(args.get(1))?;
1711 Ok(Value::Bool(a > b))
1712 }
1713 ("datetime", "compare") => {
1714 let a = expect_int(args.first())?;
1715 let b = expect_int(args.get(1))?;
1716 Ok(Value::Int(a.cmp(&b) as i64))
1717 }
1718 ("duration", "seconds") => {
1720 let nanos = expect_int(args.first())?;
1721 Ok(Value::Int(nanos / 1_000_000_000))
1722 }
1723
1724 ("regex", "split") => {
1725 let pat = expect_str(args.first())?;
1726 let s = expect_str(args.get(1))?;
1727 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.split: {e}"))?;
1728 let parts: std::collections::VecDeque<Value> = re.split(&s).map(|p| Value::Str(p.into())).collect();
1729 Ok(Value::List(parts))
1730 }
1731
1732 ("http", "with_header") => {
1735 let req = expect_record_pure(args.first())?.clone();
1736 let k = expect_str(args.get(1))?;
1737 let v = expect_str(args.get(2))?;
1738 Ok(Value::record_interned(http_set_header(req, &k, &v)))
1739 }
1740 ("http", "with_auth") => {
1741 let req = expect_record_pure(args.first())?.clone();
1742 let scheme = expect_str(args.get(1))?;
1743 let token = expect_str(args.get(2))?;
1744 let value = format!("{scheme} {token}");
1745 Ok(Value::record_interned(http_set_header(req, "Authorization", &value)))
1746 }
1747 ("http", "with_query") => {
1748 let req = expect_record_pure(args.first())?.clone();
1749 let params = match args.get(1) {
1750 Some(Value::Map(m)) => m.clone(),
1751 Some(other) => return Err(format!(
1752 "http.with_query: params must be Map[Str, Str], got {other:?}")),
1753 None => return Err("http.with_query: missing params argument".into()),
1754 };
1755 Ok(Value::record_interned(http_append_query(req, ¶ms)))
1756 }
1757 ("http", "with_timeout_ms") => {
1758 let req = expect_record_pure(args.first())?.clone();
1759 let ms = expect_int(args.get(1))?;
1760 let mut out = req;
1761 out.insert("timeout_ms".into(), Value::Variant {
1762 name: "Some".into(),
1763 args: vec![Value::Int(ms)],
1764 });
1765 Ok(Value::record_interned(out))
1766 }
1767 ("http", "json_body") => {
1768 let resp = expect_record_pure(args.first())?;
1769 let body = match resp.get("body") {
1770 Some(Value::Bytes(b)) => b.clone(),
1771 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1772 };
1773 let s = match std::str::from_utf8(&body) {
1774 Ok(s) => s,
1775 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1776 };
1777 match serde_json::from_str::<serde_json::Value>(s) {
1778 Ok(j) => Ok(ok_v(Value::from_json(&j))),
1779 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1780 }
1781 }
1782 ("http", "text_body") => {
1783 let resp = expect_record_pure(args.first())?;
1784 let body = match resp.get("body") {
1785 Some(Value::Bytes(b)) => b.clone(),
1786 _ => return Err("http.text_body: HttpResponse.body must be Bytes".into()),
1787 };
1788 match String::from_utf8(body) {
1789 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
1790 Err(e) => Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1791 }
1792 }
1793
1794 ("cli", "flag") => {
1798 let name = expect_str(args.first())?;
1799 let short = opt_str(args.get(1));
1800 let help = expect_str(args.get(2))?;
1801 Ok(value_from_json(crate::cli::flag_spec(&name, short.as_deref(), &help)))
1802 }
1803 ("cli", "option") => {
1804 let name = expect_str(args.first())?;
1805 let short = opt_str(args.get(1));
1806 let help = expect_str(args.get(2))?;
1807 let default = opt_str(args.get(3));
1808 Ok(value_from_json(crate::cli::option_spec(&name, short.as_deref(), &help, default.as_deref())))
1809 }
1810 ("cli", "positional") => {
1811 let name = expect_str(args.first())?;
1812 let help = expect_str(args.get(1))?;
1813 let required = expect_bool(args.get(2))?;
1814 Ok(value_from_json(crate::cli::positional_spec(&name, &help, required)))
1815 }
1816 ("cli", "spec") => {
1817 let name = expect_str(args.first())?;
1818 let help = expect_str(args.get(1))?;
1819 let arg_specs: Vec<serde_json::Value> = expect_list(args.get(2))?
1820 .iter().map(value_to_json).collect();
1821 let subs: Vec<serde_json::Value> = expect_list(args.get(3))?
1822 .iter().map(value_to_json).collect();
1823 Ok(value_from_json(crate::cli::build_spec(&name, &help, arg_specs, subs)))
1824 }
1825 ("cli", "parse") => {
1826 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1827 let argv: Vec<String> = expect_list(args.get(1))?
1828 .iter().map(|v| match v {
1829 Value::Str(s) => Ok(s.to_string()),
1830 other => Err(format!("cli.parse: argv must be List[Str], got {other:?}")),
1831 }).collect::<Result<_, _>>()?;
1832 match crate::cli::parse(&spec, &argv) {
1833 Ok(parsed) => Ok(ok_v(value_from_json(parsed))),
1834 Err(msg) => Ok(err_v(Value::Str(msg.into()))),
1835 }
1836 }
1837 ("cli", "envelope") => {
1838 let ok = expect_bool(args.first())?;
1839 let cmd = expect_str(args.get(1))?;
1840 let data = value_to_json(args.get(2).unwrap_or(&Value::Unit));
1841 Ok(value_from_json(crate::cli::envelope(ok, &cmd, data)))
1842 }
1843 ("cli", "describe") => {
1844 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1845 Ok(value_from_json(crate::cli::describe(&spec)))
1846 }
1847 ("cli", "help") => {
1848 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1849 Ok(Value::Str(crate::cli::help_text(&spec).into()))
1850 }
1851
1852 ("arrow", op) => match crate::arrow::dispatch(op, args) {
1854 Some(r) => r,
1855 None => Err(format!("unknown pure builtin: arrow.{op}")),
1856 },
1857 #[cfg(feature = "df")]
1861 ("df", op) => match crate::df::dispatch(op, args) {
1862 Some(r) => r,
1863 None => Err(format!("unknown pure builtin: df.{op}")),
1864 },
1865 #[cfg(not(feature = "df"))]
1866 ("df", op) => Err(format!(
1867 "df.{op}: this build was compiled without the `df` feature; \
1868 Polars-backed dataframe query ops are unavailable"
1869 )),
1870
1871 ("decimal", "decimal") => {
1878 let coef = expect_int(args.first())?;
1879 let exp = expect_int(args.get(1))?;
1880 Ok(make_decimal(coef, exp))
1881 }
1882 ("decimal", "zero") => Ok(make_decimal(0, 0)),
1883 ("decimal", "one") => Ok(make_decimal(1, 0)),
1884 ("decimal", "from_int") => {
1885 Ok(make_decimal(expect_int(args.first())?, 0))
1886 }
1887 ("decimal", "pow10") => {
1888 Ok(Value::Int(decimal_pow10(expect_int(args.first())?)?))
1889 }
1890 ("decimal", "add") => {
1891 let (ca, ea) = expect_decimal(args.first())?;
1892 let (cb, eb) = expect_decimal(args.get(1))?;
1893 let (a2, b2, e) = decimal_align(ca, ea, cb, eb)?;
1894 Ok(make_decimal(
1895 a2.checked_add(b2).ok_or("decimal.add: overflow")?, e))
1896 }
1897 ("decimal", "sub") => {
1898 let (ca, ea) = expect_decimal(args.first())?;
1899 let (cb, eb) = expect_decimal(args.get(1))?;
1900 let (a2, b2, e) = decimal_align(ca, ea, cb, eb)?;
1901 Ok(make_decimal(
1902 a2.checked_sub(b2).ok_or("decimal.sub: overflow")?, e))
1903 }
1904 ("decimal", "mul") => {
1905 let (ca, ea) = expect_decimal(args.first())?;
1906 let (cb, eb) = expect_decimal(args.get(1))?;
1907 Ok(make_decimal(
1908 ca.checked_mul(cb).ok_or("decimal.mul: overflow")?,
1909 ea.checked_add(eb).ok_or("decimal.mul: exponent overflow")?,
1910 ))
1911 }
1912 ("decimal", "compare") => {
1913 let (ca, ea) = expect_decimal(args.first())?;
1914 let (cb, eb) = expect_decimal(args.get(1))?;
1915 let (a2, b2, _) = decimal_align(ca, ea, cb, eb)?;
1916 Ok(Value::Int(if a2 < b2 { -1 } else if a2 > b2 { 1 } else { 0 }))
1917 }
1918 ("decimal", "is_zero") => {
1919 let (c, _) = expect_decimal(args.first())?;
1920 Ok(Value::Bool(c == 0))
1921 }
1922 ("decimal", "is_positive") => {
1923 let (c, _) = expect_decimal(args.first())?;
1924 Ok(Value::Bool(c > 0))
1925 }
1926 ("decimal", "is_negative") => {
1927 let (c, _) = expect_decimal(args.first())?;
1928 Ok(Value::Bool(c < 0))
1929 }
1930 ("decimal", "negate") => {
1931 let (c, e) = expect_decimal(args.first())?;
1932 Ok(make_decimal(-c, e))
1933 }
1934 ("decimal", "abs") => {
1935 let (c, e) = expect_decimal(args.first())?;
1936 Ok(make_decimal(c.abs(), e))
1937 }
1938 ("decimal", "normalize") => {
1939 let (mut c, mut e) = expect_decimal(args.first())?;
1940 if c == 0 { return Ok(make_decimal(0, 0)); }
1941 while c % 10 == 0 { c /= 10; e += 1; }
1942 Ok(make_decimal(c, e))
1943 }
1944 ("decimal", "round_to") => {
1945 let (c, e) = expect_decimal(args.first())?;
1946 let target_e = expect_int(args.get(1))?;
1947 let mode = expect_str(args.get(2))?;
1948 Ok(make_decimal(decimal_round(c, e, target_e, &mode)?, target_e))
1949 }
1950 ("decimal", "to_str") => {
1951 let (c, e) = expect_decimal(args.first())?;
1952 Ok(Value::Str(decimal_to_str(c, e)?.into()))
1953 }
1954
1955 _ => Err(format!("unknown pure builtin: {kind}.{op}")),
1956 }
1957}
1958
1959fn expect_decimal(v: Option<&Value>) -> Result<(i64, i64), String> {
1963 match v {
1964 Some(Value::Record { fields, .. }) => {
1965 let coef = match fields.get("coefficient") {
1966 Some(Value::Int(n)) => *n,
1967 _ => return Err("decimal: missing or invalid 'coefficient' field".into()),
1968 };
1969 let exp = match fields.get("exponent") {
1970 Some(Value::Int(n)) => *n,
1971 _ => return Err("decimal: missing or invalid 'exponent' field".into()),
1972 };
1973 Ok((coef, exp))
1974 }
1975 Some(other) => Err(format!("decimal: expected {{ coefficient, exponent }} record, got {other:?}")),
1976 None => Err("decimal: missing argument".into()),
1977 }
1978}
1979
1980fn make_decimal(coefficient: i64, exponent: i64) -> Value {
1982 let mut fields = indexmap::IndexMap::new();
1983 fields.insert("coefficient".into(), Value::Int(coefficient));
1984 fields.insert("exponent".into(), Value::Int(exponent));
1985 Value::record_interned(fields)
1986}
1987
1988fn decimal_pow10(n: i64) -> Result<i64, String> {
1990 if n < 0 { return Err(format!("decimal.pow10: negative exponent {n}")); }
1991 if n > 18 { return Err(format!("decimal.pow10: exponent {n} exceeds max (18)")); }
1992 Ok(10i64.pow(n as u32))
1993}
1994
1995fn decimal_align(ca: i64, ea: i64, cb: i64, eb: i64) -> Result<(i64, i64, i64), String> {
1998 if ea == eb { return Ok((ca, cb, ea)); }
1999 if ea > eb {
2000 let scale = decimal_pow10(ea - eb)?;
2001 let ca2 = ca.checked_mul(scale)
2002 .ok_or_else(|| format!("decimal: overflow aligning (shift {})", ea - eb))?;
2003 Ok((ca2, cb, eb))
2004 } else {
2005 let scale = decimal_pow10(eb - ea)?;
2006 let cb2 = cb.checked_mul(scale)
2007 .ok_or_else(|| format!("decimal: overflow aligning (shift {})", eb - ea))?;
2008 Ok((ca, cb2, ea))
2009 }
2010}
2011
2012fn decimal_round(c: i64, e: i64, target_e: i64, mode: &str) -> Result<i64, String> {
2017 if e >= target_e {
2018 let shift = e - target_e;
2020 let scale = decimal_pow10(shift)?;
2021 return c.checked_mul(scale)
2022 .ok_or_else(|| format!("decimal.round_to: overflow scaling (shift {shift})"));
2023 }
2024 let shift = target_e - e;
2026 let divisor = decimal_pow10(shift)?;
2027 let q = c / divisor;
2028 let r = c % divisor; if r == 0 { return Ok(q); }
2031
2032 let abs_r = r.abs();
2033 let positive = r > 0; let rounded = match mode {
2036 "Down" => q,
2037 "Up" => if positive { q + 1 } else { q - 1 },
2038 "Floor" => if positive { q } else { q - 1 },
2039 "Ceiling" => if positive { q + 1 } else { q },
2040 "HalfUp" => {
2041 if abs_r * 2 >= divisor {
2042 if positive { q + 1 } else { q - 1 }
2043 } else { q }
2044 }
2045 "HalfDown" => {
2046 if abs_r * 2 > divisor {
2047 if positive { q + 1 } else { q - 1 }
2048 } else { q }
2049 }
2050 "HalfEven" => {
2051 if abs_r * 2 > divisor {
2052 if positive { q + 1 } else { q - 1 }
2053 } else if abs_r * 2 == divisor {
2054 if q % 2 == 0 { q } else { if positive { q + 1 } else { q - 1 } }
2056 } else { q }
2057 }
2058 other => return Err(format!(
2059 "decimal.round_to: unknown rounding mode {other:?}; \
2060 valid modes: HalfUp HalfDown HalfEven Down Up Ceiling Floor")),
2061 };
2062 Ok(rounded)
2063}
2064
2065fn decimal_to_str(c: i64, e: i64) -> Result<String, String> {
2068 if e == 0 { return Ok(c.to_string()); }
2069 if e > 0 {
2070 let scale = decimal_pow10(e)?;
2071 let val = c.checked_mul(scale)
2072 .ok_or("decimal.to_str: overflow")?;
2073 return Ok(val.to_string());
2074 }
2075 let scale = decimal_pow10(-e)?;
2077 let sign = if c < 0 { "-" } else { "" };
2078 let abs_c = c.abs();
2079 let int_part = abs_c / scale;
2080 let frac_part = abs_c % scale;
2081 let decimal_places = (-e) as usize;
2082 Ok(format!("{sign}{int_part}.{frac_part:0>decimal_places$}"))
2083}
2084
2085fn opt_str(arg: Option<&Value>) -> Option<String> {
2089 match arg {
2090 Some(Value::Variant { name, args }) if name == "Some" => {
2091 args.first().and_then(|v| match v {
2092 Value::Str(s) => Some(s.to_string()),
2093 _ => None,
2094 })
2095 }
2096 _ => None,
2097 }
2098}
2099
2100fn value_from_json(v: serde_json::Value) -> Value { Value::from_json(&v) }
2101
2102fn regex_cache() -> &'static Mutex<HashMap<String, regex::Regex>> {
2107 static CACHE: OnceLock<Mutex<HashMap<String, regex::Regex>>> = OnceLock::new();
2108 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
2109}
2110
2111fn get_or_compile_regex(pattern: &str) -> Result<regex::Regex, String> {
2112 let cache = regex_cache();
2113 {
2114 let guard = cache.lock().unwrap();
2115 if let Some(re) = guard.get(pattern) {
2116 return Ok(re.clone());
2117 }
2118 }
2119 let re = regex::Regex::new(pattern).map_err(|e| format!("invalid regex: {e}"))?;
2120 let mut guard = cache.lock().unwrap();
2121 guard.insert(pattern.to_string(), re.clone());
2122 Ok(re)
2123}
2124
2125fn match_value(caps: ®ex::Captures) -> Value {
2129 let m0 = caps.get(0).expect("regex match always has group 0");
2130 let mut rec = indexmap::IndexMap::new();
2131 rec.insert("text".into(), Value::Str(m0.as_str().into()));
2132 rec.insert("start".into(), Value::Int(m0.start() as i64));
2133 rec.insert("end".into(), Value::Int(m0.end() as i64));
2134 let groups: std::collections::VecDeque<Value> = (1..caps.len())
2135 .map(|i| {
2136 Value::Str(
2137 caps.get(i)
2138 .map(|m| m.as_str())
2139 .unwrap_or_default()
2140 .into(),
2141 )
2142 })
2143 .collect();
2144 rec.insert("groups".into(), Value::List(groups));
2145 Value::record_dynamic(rec)
2146}
2147
2148fn expect_map(v: Option<&Value>) -> Result<&BTreeMap<MapKey, Value>, String> {
2149 match v {
2150 Some(Value::Map(m)) => Ok(m),
2151 other => Err(format!("expected Map, got {other:?}")),
2152 }
2153}
2154
2155fn expect_set(v: Option<&Value>) -> Result<&BTreeSet<MapKey>, String> {
2156 match v {
2157 Some(Value::Set(s)) => Ok(s),
2158 other => Err(format!("expected Set, got {other:?}")),
2159 }
2160}
2161
2162fn unpack_matrix(v: &Value) -> Result<(usize, usize, Vec<f64>), String> {
2166 if let Value::F64Array { rows, cols, data } = v {
2167 return Ok((*rows as usize, *cols as usize, data.clone()));
2168 }
2169 let rec = match v {
2170 Value::Record { fields: r, .. } => r,
2171 other => return Err(format!("expected matrix, got {other:?}")),
2172 };
2173 let rows = match rec.get("rows") {
2174 Some(Value::Int(n)) => *n as usize,
2175 _ => return Err("matrix: missing/invalid `rows`".into()),
2176 };
2177 let cols = match rec.get("cols") {
2178 Some(Value::Int(n)) => *n as usize,
2179 _ => return Err("matrix: missing/invalid `cols`".into()),
2180 };
2181 let data = match rec.get("data") {
2182 Some(Value::List(items)) => {
2183 let mut out = Vec::with_capacity(items.len());
2184 for it in items {
2185 out.push(match it {
2186 Value::Float(f) => *f,
2187 Value::Int(n) => *n as f64,
2188 other => return Err(format!("matrix data: not numeric, got {other:?}")),
2189 });
2190 }
2191 out
2192 }
2193 _ => return Err("matrix: missing/invalid `data`".into()),
2194 };
2195 if data.len() != rows * cols {
2196 return Err(format!("matrix: data len {} != {rows}*{cols}", data.len()));
2197 }
2198 Ok((rows, cols, data))
2199}
2200
2201fn expect_bytes(v: Option<&Value>) -> Result<&Vec<u8>, String> {
2202 match v {
2203 Some(Value::Bytes(b)) => Ok(b),
2204 Some(other) => Err(format!("expected Bytes, got {other:?}")),
2205 None => Err("missing argument".into()),
2206 }
2207}
2208
2209fn first_arg(args: &[Value]) -> Result<&Value, String> {
2210 args.first().ok_or_else(|| "missing argument".into())
2211}
2212
2213fn tuple_index(v: &Value, i: usize) -> Result<Value, String> {
2214 match v {
2215 Value::Tuple(items) => items.get(i).cloned()
2216 .ok_or_else(|| format!("tuple index {i} out of range (len={})", items.len())),
2217 other => Err(format!("expected Tuple, got {other:?}")),
2218 }
2219}
2220
2221fn expect_str(v: Option<&Value>) -> Result<String, String> {
2222 match v {
2223 Some(Value::Str(s)) => Ok(s.to_string()),
2224 Some(other) => Err(format!("expected Str, got {other:?}")),
2225 None => Err("missing argument".into()),
2226 }
2227}
2228
2229fn expect_int(v: Option<&Value>) -> Result<i64, String> {
2230 match v {
2231 Some(Value::Int(n)) => Ok(*n),
2232 Some(other) => Err(format!("expected Int, got {other:?}")),
2233 None => Err("missing argument".into()),
2234 }
2235}
2236
2237fn expect_float(v: Option<&Value>) -> Result<f64, String> {
2238 match v {
2239 Some(Value::Float(f)) => Ok(*f),
2240 Some(other) => Err(format!("expected Float, got {other:?}")),
2241 None => Err("missing argument".into()),
2242 }
2243}
2244
2245fn expect_list(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
2246 match v {
2247 Some(Value::List(xs)) => Ok(xs),
2248 Some(other) => Err(format!("expected List, got {other:?}")),
2249 None => Err("missing argument".into()),
2250 }
2251}
2252
2253fn expect_bool(v: Option<&Value>) -> Result<bool, String> {
2254 match v {
2255 Some(Value::Bool(b)) => Ok(*b),
2256 Some(other) => Err(format!("expected Bool, got {other:?}")),
2257 None => Err("missing argument".into()),
2258 }
2259}
2260
2261fn expect_deque(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
2262 match v {
2263 Some(Value::Deque(d)) => Ok(d),
2264 Some(other) => Err(format!("expected Deque, got {other:?}")),
2265 None => Err("missing argument".into()),
2266 }
2267}
2268
2269fn some(v: Value) -> Value { Value::Variant { name: "Some".into(), args: vec![v] } }
2270fn none() -> Value { Value::Variant { name: "None".into(), args: Vec::new() } }
2271fn ok_v(v: Value) -> Value { Value::Variant { name: "Ok".into(), args: vec![v] } }
2272fn err_v(v: Value) -> Value { Value::Variant { name: "Err".into(), args: vec![v] } }
2273
2274fn parser_node(kind: &str, fields: &[(&str, Value)]) -> Value {
2282 let mut r = indexmap::IndexMap::new();
2283 r.insert("kind".into(), Value::Str(kind.into()));
2284 for (k, v) in fields {
2285 r.insert((*k).into(), v.clone());
2286 }
2287 Value::record_dynamic(r)
2288}
2289
2290fn splitmix64(state: u64) -> (u64, u64) {
2301 let next = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
2302 let mut z = next;
2303 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2304 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
2305 let z = z ^ (z >> 31);
2306 (z, next)
2307}
2308
2309fn rng_value(state: u64) -> Value {
2313 let mut fields = indexmap::IndexMap::new();
2314 fields.insert("state".into(), Value::Int(state as i64));
2315 Value::record_dynamic(fields)
2316}
2317
2318fn rng_decode(v: Option<&Value>) -> Result<u64, String> {
2320 let rec = match v {
2321 Some(Value::Record { fields: r, .. }) => r,
2322 Some(other) => return Err(format!("expected Rng, got {other:?}")),
2323 None => return Err("missing Rng arg".into()),
2324 };
2325 match rec.get("state") {
2326 Some(Value::Int(n)) => Ok(*n as u64),
2327 _ => Err("malformed Rng: missing `state :: Int`".into()),
2328 }
2329}
2330
2331fn expect_record_pure(v: Option<&Value>) -> Result<&indexmap::IndexMap<smol_str::SmolStr, Value>, String> {
2334 match v {
2335 Some(Value::Record { fields: r, .. }) => Ok(r),
2336 Some(other) => Err(format!("expected Record, got {other:?}")),
2337 None => Err("missing Record argument".into()),
2338 }
2339}
2340
2341fn http_decode_err_pure(msg: String) -> Value {
2342 let inner = Value::Variant {
2343 name: "DecodeError".into(),
2344 args: vec![Value::Str(msg.into())],
2345 };
2346 err_v(inner)
2347}
2348
2349fn http_set_header(
2354 mut req: indexmap::IndexMap<smol_str::SmolStr, Value>,
2355 name: &str,
2356 value: &str,
2357) -> indexmap::IndexMap<smol_str::SmolStr, Value> {
2358 use lex_bytecode::MapKey;
2359 let mut headers = match req.shift_remove("headers") {
2360 Some(Value::Map(m)) => m,
2361 _ => std::collections::BTreeMap::new(),
2362 };
2363 let key = MapKey::Str(name.to_lowercase());
2364 let lowered = name.to_lowercase();
2367 headers.retain(|k, _| match k {
2368 MapKey::Str(s) => s.to_lowercase() != lowered,
2369 _ => true,
2370 });
2371 headers.insert(key, Value::Str(value.into()));
2372 req.insert("headers".into(), Value::Map(headers));
2373 req
2374}
2375
2376fn http_append_query(
2382 mut req: indexmap::IndexMap<smol_str::SmolStr, Value>,
2383 params: &std::collections::BTreeMap<lex_bytecode::MapKey, Value>,
2384) -> indexmap::IndexMap<smol_str::SmolStr, Value> {
2385 use lex_bytecode::MapKey;
2386 let url = match req.get("url") {
2387 Some(Value::Str(s)) => s.clone(),
2388 _ => return req,
2389 };
2390 let mut pieces = Vec::new();
2391 for (k, v) in params {
2392 let kk = match k { MapKey::Str(s) => s.to_string(), _ => continue };
2393 let vv = match v { Value::Str(s) => s.to_string(), _ => continue };
2394 pieces.push(format!("{}={}", url_encode(&kk), url_encode(&vv)));
2395 }
2396 if pieces.is_empty() { return req; }
2397 let sep = if url.contains('?') { '&' } else { '?' };
2398 let new_url = format!("{url}{sep}{}", pieces.join("&"));
2399 req.insert("url".into(), Value::Str(new_url.into()));
2400 req
2401}
2402
2403fn url_encode(s: &str) -> String {
2408 let mut out = String::with_capacity(s.len());
2409 for b in s.bytes() {
2410 match b {
2411 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
2412 out.push(b as char);
2413 }
2414 _ => out.push_str(&format!("%{:02X}", b)),
2415 }
2416 }
2417 out
2418}
2419
2420fn value_to_json(v: &Value) -> serde_json::Value { v.to_json() }
2421
2422fn unwrap_toml_datetime_markers(v: &mut serde_json::Value) {
2430 use serde_json::Value as J;
2431 match v {
2432 J::Object(map) => {
2433 if map.len() == 1 {
2437 if let Some(J::String(s)) = map.get("$__toml_private_datetime") {
2438 let s = s.clone();
2439 *v = J::String(s);
2440 return;
2441 }
2442 }
2443 for (_, child) in map.iter_mut() {
2444 unwrap_toml_datetime_markers(child);
2445 }
2446 }
2447 J::Array(items) => {
2448 for item in items.iter_mut() {
2449 unwrap_toml_datetime_markers(item);
2450 }
2451 }
2452 _ => {}
2453 }
2454}
2455
2456fn json_to_value(v: &serde_json::Value) -> Value { Value::from_json(v) }
2457
2458fn required_field_names(arg: Option<&Value>) -> Result<Vec<String>, String> {
2463 let list = expect_list(arg)?;
2464 let mut out = Vec::with_capacity(list.len());
2465 for v in list {
2466 match v {
2467 Value::Str(s) => out.push(s.to_string()),
2468 other => return Err(format!(
2469 "parse_strict: required-fields list must contain Str, got {other:?}"
2470 )),
2471 }
2472 }
2473 Ok(out)
2474}
2475
2476fn check_required_fields(
2497 value: &serde_json::Value,
2498 required: &[String],
2499) -> Result<(), String> {
2500 if required.is_empty() {
2501 return Ok(());
2502 }
2503 if !matches!(value, serde_json::Value::Object(_)) {
2504 return Err(format!(
2505 "parse_strict: expected top-level object with fields {:?}, got {value}",
2506 required
2507 ));
2508 }
2509 let mut missing: Vec<String> = Vec::new();
2510 for path in required {
2511 if !path_exists(value, path) {
2512 missing.push(path.clone());
2513 }
2514 }
2515 if missing.is_empty() {
2516 Ok(())
2517 } else {
2518 Err(format!("missing required field(s): {}", missing.join(", ")))
2519 }
2520}
2521
2522fn path_exists(value: &serde_json::Value, path: &str) -> bool {
2527 let mut cursor = value;
2528 let segments = split_dotted_path(path);
2529 for seg in &segments {
2530 match cursor {
2531 serde_json::Value::Object(o) => match o.get(seg.as_str()) {
2532 Some(next) => cursor = next,
2533 None => return false,
2534 },
2535 _ => return false,
2536 }
2537 }
2538 true
2539}
2540
2541fn split_dotted_path(path: &str) -> Vec<String> {
2545 let mut out: Vec<String> = Vec::new();
2546 let mut cur = String::new();
2547 let mut iter = path.chars().peekable();
2548 while let Some(c) = iter.next() {
2549 if c == '\\' {
2550 if let Some(&'.') = iter.peek() {
2552 cur.push('.');
2553 iter.next();
2554 continue;
2555 }
2556 cur.push(c);
2557 } else if c == '.' {
2558 out.push(std::mem::take(&mut cur));
2559 } else {
2560 cur.push(c);
2561 }
2562 }
2563 out.push(cur);
2564 out
2565}
2566
2567fn extract_type_schema(v: Option<&Value>) -> Vec<(String, String)> {
2571 match v {
2572 Some(Value::List(pairs)) => pairs.iter().filter_map(|p| {
2573 if let Value::Tuple(items) = p {
2574 if items.len() == 2 {
2575 if let (Value::Str(name), Value::Str(tag)) = (&items[0], &items[1]) {
2576 return Some((name.to_string(), tag.to_string()));
2577 }
2578 }
2579 }
2580 None
2581 }).collect(),
2582 _ => vec![],
2583 }
2584}
2585
2586fn validate_field_types(
2592 json: &serde_json::Value,
2593 schema: &[(String, String)],
2594) -> Result<(), String> {
2595 if schema.is_empty() {
2596 return Ok(());
2597 }
2598 let obj = match json.as_object() {
2599 Some(o) => o,
2600 None => return Ok(()), };
2602 for (field, tag) in schema {
2603 if let Some(val) = obj.get(field) {
2604 if let Err(e) = check_json_type(val, tag) {
2605 return Err(format!("field `{field}`: {e}"));
2606 }
2607 }
2608 }
2609 Ok(())
2610}
2611
2612fn apply_option_wrapping(v: Value, json: &serde_json::Value, schema: &[(String, String)]) -> Value {
2617 if schema.is_empty() {
2618 return v;
2619 }
2620 let fields = match v {
2621 Value::Record { fields, .. } => *fields,
2622 other => return other,
2623 };
2624 let json_obj = match json.as_object() {
2625 Some(o) => o,
2626 None => return Value::record_interned(fields),
2627 };
2628 let mut new_fields = fields;
2629 for (field_name, tag) in schema {
2630 if tag.starts_with("Option[") && tag.ends_with(']') {
2631 let json_val = json_obj.get(field_name.as_str());
2632 let wrapped = match json_val {
2633 None | Some(serde_json::Value::Null) => none(),
2634 Some(_) => {
2635 let inner = new_fields
2636 .get(field_name.as_str())
2637 .cloned()
2638 .unwrap_or(Value::Unit);
2639 some(inner)
2640 }
2641 };
2642 new_fields.insert(smol_str::SmolStr::from(field_name.as_str()), wrapped);
2643 }
2644 }
2645 Value::record_interned(new_fields)
2646}
2647
2648fn check_json_type(val: &serde_json::Value, tag: &str) -> Result<(), String> {
2650 use serde_json::Value as J;
2651 match (tag, val) {
2652 ("Int", J::Number(n)) if n.is_i64() || n.is_u64() => Ok(()),
2653 ("Int", other) => Err(format!("expected Int, got {}", json_type_name(other))),
2654 ("Float", J::Number(_)) => Ok(()),
2655 ("Float", other) => Err(format!("expected Float, got {}", json_type_name(other))),
2656 ("Bool", J::Bool(_)) => Ok(()),
2657 ("Bool", other) => Err(format!("expected Bool, got {}", json_type_name(other))),
2658 ("Str", J::String(_)) => Ok(()),
2659 ("Str", other) => Err(format!("expected Str, got {}", json_type_name(other))),
2660 (tag, J::Null) if tag.starts_with("Option[") => Ok(()),
2662 (tag, val) if tag.starts_with("Option[") && tag.ends_with(']') => {
2663 let inner = &tag[7..tag.len() - 1]; check_json_type(val, inner)
2665 }
2666 (tag, J::Array(items)) if tag.starts_with("List[") && tag.ends_with(']') => {
2668 let inner = &tag[5..tag.len() - 1]; for (i, item) in items.iter().enumerate() {
2670 if let Err(e) = check_json_type(item, inner) {
2671 return Err(format!("[{i}]: {e}"));
2672 }
2673 }
2674 Ok(())
2675 }
2676 ("Record", _) => Ok(()), ("Any", _) => Ok(()), _ => Ok(()), }
2680}
2681
2682fn json_type_name(v: &serde_json::Value) -> &'static str {
2683 match v {
2684 serde_json::Value::Null => "null",
2685 serde_json::Value::Bool(_) => "Bool",
2686 serde_json::Value::Number(_) => "Number",
2687 serde_json::Value::String(_) => "Str",
2688 serde_json::Value::Array(_) => "Array",
2689 serde_json::Value::Object(_) => "Object",
2690 }
2691}
2692
2693fn parse_dotenv(src: &str) -> Result<indexmap::IndexMap<String, String>, String> {
2704 let mut out = indexmap::IndexMap::new();
2705 for (idx, raw) in src.lines().enumerate() {
2706 let line = raw.trim();
2707 if line.is_empty() || line.starts_with('#') {
2708 continue;
2709 }
2710 let after_export = line.strip_prefix("export ").unwrap_or(line);
2713 let (k, v) = match after_export.split_once('=') {
2714 Some(kv) => kv,
2715 None => return Err(format!("dotenv.parse line {}: missing `=`", idx + 1)),
2716 };
2717 let key = k.trim();
2718 if key.is_empty() {
2719 return Err(format!("dotenv.parse line {}: empty key", idx + 1));
2720 }
2721 let v_trim = v.trim();
2722 let value = if let Some(q) = v_trim.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
2723 q.to_string()
2724 } else if let Some(q) = v_trim.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
2725 q.to_string()
2726 } else {
2727 v_trim.to_string()
2728 };
2729 out.insert(key.to_string(), value);
2730 }
2731 Ok(out)
2732}
2733
2734fn instant_from_chrono<Tz: chrono::TimeZone>(dt: chrono::DateTime<Tz>) -> i64 {
2740 dt.timestamp_nanos_opt().unwrap_or(i64::MAX)
2741}
2742
2743fn chrono_from_instant(n: i64) -> chrono::DateTime<chrono::Utc> {
2744 let secs = n.div_euclid(1_000_000_000);
2745 let nanos = n.rem_euclid(1_000_000_000) as u32;
2746 use chrono::TimeZone;
2747 chrono::Utc
2748 .timestamp_opt(secs, nanos)
2749 .single()
2750 .unwrap_or_else(chrono::Utc::now)
2751}
2752
2753fn format_iso(n: i64) -> String {
2754 chrono_from_instant(n).to_rfc3339()
2755}
2756
2757enum TzArg {
2760 Utc,
2761 Local,
2762 Offset(i32),
2764 Iana(String),
2766}
2767
2768fn parse_tz_arg(v: Option<&Value>) -> Result<TzArg, String> {
2769 match v {
2770 Some(Value::Variant { name, args }) => match (name.as_str(), args.as_slice()) {
2771 ("Utc", []) => Ok(TzArg::Utc),
2772 ("Local", []) => Ok(TzArg::Local),
2773 ("Offset", [Value::Int(m)]) => {
2774 let m = i32::try_from(*m).map_err(|_| {
2775 format!("Tz::Offset: minutes out of range: {m}")
2776 })?;
2777 Ok(TzArg::Offset(m))
2778 }
2779 ("Iana", [Value::Str(s)]) => Ok(TzArg::Iana(s.to_string())),
2780 (other, _) => Err(format!(
2781 "expected Tz variant (Utc | Local | Offset(Int) | Iana(Str)), got `{other}` with {} arg(s)",
2782 args.len()
2783 )),
2784 },
2785 Some(other) => Err(format!("expected Tz variant, got {other:?}")),
2786 None => Err("missing Tz argument".into()),
2787 }
2788}
2789
2790fn resolve_tz_to_components(n: i64, tz: &TzArg) -> Result<Value, String> {
2791 use chrono::{TimeZone, Datelike, Timelike, Offset};
2792 let utc_dt = chrono_from_instant(n);
2793 let (y, m, d, hh, mm, ss, ns, off_min) = match tz {
2794 TzArg::Utc => {
2795 let d = utc_dt;
2796 (d.year(), d.month() as i32, d.day() as i32,
2797 d.hour() as i32, d.minute() as i32, d.second() as i32,
2798 d.nanosecond() as i32, 0)
2799 }
2800 TzArg::Local => {
2801 let d = utc_dt.with_timezone(&chrono::Local);
2802 let off = d.offset().fix().local_minus_utc() / 60;
2803 (d.year(), d.month() as i32, d.day() as i32,
2804 d.hour() as i32, d.minute() as i32, d.second() as i32,
2805 d.nanosecond() as i32, off)
2806 }
2807 TzArg::Offset(off_min) => {
2808 let off_secs = off_min.saturating_mul(60);
2809 let fixed = chrono::FixedOffset::east_opt(off_secs)
2810 .ok_or("to_components: offset out of range")?;
2811 let d = utc_dt.with_timezone(&fixed);
2812 (d.year(), d.month() as i32, d.day() as i32,
2813 d.hour() as i32, d.minute() as i32, d.second() as i32,
2814 d.nanosecond() as i32, *off_min)
2815 }
2816 TzArg::Iana(name) => {
2817 let tz: chrono_tz::Tz = name.parse()
2818 .map_err(|e| format!("to_components: unknown timezone `{name}`: {e}"))?;
2819 let d = utc_dt.with_timezone(&tz);
2820 let off = d.offset().fix().local_minus_utc() / 60;
2821 (d.year(), d.month() as i32, d.day() as i32,
2822 d.hour() as i32, d.minute() as i32, d.second() as i32,
2823 d.nanosecond() as i32, off)
2824 }
2825 };
2826 let mut rec = indexmap::IndexMap::new();
2827 rec.insert("year".into(), Value::Int(y as i64));
2828 rec.insert("month".into(), Value::Int(m as i64));
2829 rec.insert("day".into(), Value::Int(d as i64));
2830 rec.insert("hour".into(), Value::Int(hh as i64));
2831 rec.insert("minute".into(), Value::Int(mm as i64));
2832 rec.insert("second".into(), Value::Int(ss as i64));
2833 rec.insert("nano".into(), Value::Int(ns as i64));
2834 rec.insert("tz_offset_minutes".into(), Value::Int(off_min as i64));
2835 let _ = chrono::Utc.timestamp_opt(0, 0); Ok(Value::record_dynamic(rec))
2837}
2838
2839
2840fn instant_from_components(rec: &indexmap::IndexMap<smol_str::SmolStr, Value>) -> Result<i64, String> {
2841 use chrono::TimeZone;
2842 fn get_int(rec: &indexmap::IndexMap<smol_str::SmolStr, Value>, k: &str) -> Result<i64, String> {
2843 match rec.get(k) {
2844 Some(Value::Int(n)) => Ok(*n),
2845 other => Err(format!("from_components: missing or non-int field `{k}`: {other:?}")),
2846 }
2847 }
2848 let y = get_int(rec, "year")? as i32;
2849 let m = get_int(rec, "month")? as u32;
2850 let d = get_int(rec, "day")? as u32;
2851 let hh = get_int(rec, "hour")? as u32;
2852 let mm = get_int(rec, "minute")? as u32;
2853 let ss = get_int(rec, "second")? as u32;
2854 let ns = get_int(rec, "nano")? as u32;
2855 let off_min = get_int(rec, "tz_offset_minutes")? as i32;
2856 let off = chrono::FixedOffset::east_opt(off_min * 60)
2857 .ok_or("from_components: offset out of range")?;
2858 let dt = off
2859 .with_ymd_and_hms(y, m, d, hh, mm, ss)
2860 .single()
2861 .ok_or("from_components: invalid or ambiguous date/time")?;
2862 let dt = dt + chrono::Duration::nanoseconds(ns as i64);
2863 Ok(instant_from_chrono(dt))
2864}
2865
2866type Aead4<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>);
2883
2884type Aead5<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>);
2887
2888fn unpack4_bytes<'a>(
2889 args: &'a [Value],
2890 op: &str,
2891) -> Result<Aead4<'a>, String> {
2892 let pick = |i: usize, name: &str| -> Result<&'a Vec<u8>, String> {
2893 match args.get(i) {
2894 Some(Value::Bytes(b)) => Ok(b),
2895 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
2896 None => Err(format!("{op}: missing {name} argument")),
2897 }
2898 };
2899 Ok((pick(0, "key")?, pick(1, "nonce")?, pick(2, "aad")?, pick(3, "plaintext")?))
2900}
2901
2902fn unpack5_bytes<'a>(
2903 args: &'a [Value],
2904 op: &str,
2905) -> Result<Aead5<'a>, String> {
2906 let pick = |i: usize, name: &str| -> Result<&'a Vec<u8>, String> {
2907 match args.get(i) {
2908 Some(Value::Bytes(b)) => Ok(b),
2909 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
2910 None => Err(format!("{op}: missing {name} argument")),
2911 }
2912 };
2913 Ok((
2914 pick(0, "key")?,
2915 pick(1, "nonce")?,
2916 pick(2, "aad")?,
2917 pick(3, "ciphertext")?,
2918 pick(4, "tag")?,
2919 ))
2920}
2921
2922fn aead_result(ciphertext: Vec<u8>, tag: Vec<u8>) -> Value {
2923 let mut rec = indexmap::IndexMap::new();
2924 rec.insert("ciphertext".into(), Value::Bytes(ciphertext));
2925 rec.insert("tag".into(), Value::Bytes(tag));
2926 Value::record_dynamic(rec)
2927}
2928
2929fn aead_err(msg: impl Into<String>) -> Value {
2930 let s: String = msg.into();
2931 err_v(Value::Str(s.into()))
2932}
2933
2934fn aes_gcm_seal_impl(args: &[Value]) -> Value {
2935 use aes_gcm::aead::{Aead, KeyInit, Payload};
2936 use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
2937 let (key, nonce, aad, plaintext) = match unpack4_bytes(args, "aes_gcm_seal") {
2938 Ok(t) => t,
2939 Err(e) => return aead_err(e),
2940 };
2941 if nonce.len() != 12 {
2942 return aead_err(format!(
2943 "aes_gcm_seal: nonce must be exactly 12 bytes, got {}", nonce.len()
2944 ));
2945 }
2946 let n = Nonce::from_slice(nonce);
2947 let payload = Payload { msg: plaintext, aad };
2948 let combined = match key.len() {
2951 16 => {
2952 let cipher = Aes128Gcm::new_from_slice(key)
2953 .map_err(|e| e.to_string());
2954 match cipher {
2955 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("aes_gcm_seal: {e}")),
2956 Err(e) => Err(format!("aes_gcm_seal: {e}")),
2957 }
2958 }
2959 32 => {
2960 let cipher = Aes256Gcm::new_from_slice(key)
2961 .map_err(|e| e.to_string());
2962 match cipher {
2963 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("aes_gcm_seal: {e}")),
2964 Err(e) => Err(format!("aes_gcm_seal: {e}")),
2965 }
2966 }
2967 other => return aead_err(format!(
2970 "aes_gcm_seal: key must be 16 or 32 bytes, got {other}"
2971 )),
2972 };
2973 match combined {
2974 Ok(mut buf) => {
2975 let tag_start = buf.len() - 16;
2977 let tag = buf.split_off(tag_start);
2978 ok_v(aead_result(buf, tag))
2979 }
2980 Err(e) => aead_err(e),
2981 }
2982}
2983
2984fn aes_gcm_open_impl(args: &[Value]) -> Value {
2985 use aes_gcm::aead::{Aead, KeyInit, Payload};
2986 use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
2987 let (key, nonce, aad, ciphertext, tag) = match unpack5_bytes(args, "aes_gcm_open") {
2988 Ok(t) => t,
2989 Err(e) => return err_v(Value::Str(e.into())),
2990 };
2991 if nonce.len() != 12 {
2992 return err_v(Value::Str(format!(
2993 "aes_gcm_open: nonce must be exactly 12 bytes, got {}", nonce.len()
2994 ).into()));
2995 }
2996 if tag.len() != 16 {
2997 return err_v(Value::Str(format!(
2998 "aes_gcm_open: tag must be exactly 16 bytes, got {}", tag.len()
2999 ).into()));
3000 }
3001 let mut combined = Vec::with_capacity(ciphertext.len() + tag.len());
3003 combined.extend_from_slice(ciphertext);
3004 combined.extend_from_slice(tag);
3005 let n = Nonce::from_slice(nonce);
3006 let payload = Payload { msg: &combined, aad };
3007 let plaintext = match key.len() {
3008 16 => Aes128Gcm::new_from_slice(key)
3009 .map_err(|e| format!("aes_gcm_open: {e}"))
3010 .and_then(|c| c.decrypt(n, payload).map_err(|e| format!("aes_gcm_open: {e}"))),
3011 32 => Aes256Gcm::new_from_slice(key)
3012 .map_err(|e| format!("aes_gcm_open: {e}"))
3013 .and_then(|c| c.decrypt(n, payload).map_err(|e| format!("aes_gcm_open: {e}"))),
3014 other => return err_v(Value::Str(format!(
3015 "aes_gcm_open: key must be 16 or 32 bytes, got {other}"
3016 ).into())),
3017 };
3018 match plaintext {
3019 Ok(p) => ok_v(Value::Bytes(p)),
3020 Err(e) => err_v(Value::Str(e.into())),
3021 }
3022}
3023
3024fn chacha20_seal_impl(args: &[Value]) -> Value {
3025 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
3026 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
3027 let (key, nonce, aad, plaintext) = match unpack4_bytes(args, "chacha20_poly1305_seal") {
3028 Ok(t) => t,
3029 Err(e) => return aead_err(e),
3030 };
3031 if key.len() != 32 {
3032 return aead_err(format!(
3033 "chacha20_poly1305_seal: key must be exactly 32 bytes, got {}", key.len()
3034 ));
3035 }
3036 if nonce.len() != 12 {
3037 return aead_err(format!(
3038 "chacha20_poly1305_seal: nonce must be exactly 12 bytes, got {}", nonce.len()
3039 ));
3040 }
3041 let cipher = ChaCha20Poly1305::new_from_slice(key)
3042 .map_err(|e| format!("chacha20_poly1305_seal: {e}"));
3043 let n = Nonce::from_slice(nonce);
3044 let payload = Payload { msg: plaintext, aad };
3045 let combined = match cipher {
3046 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("chacha20_poly1305_seal: {e}")),
3047 Err(e) => Err(e),
3048 };
3049 match combined {
3050 Ok(mut buf) => {
3051 let tag_start = buf.len() - 16;
3052 let tag = buf.split_off(tag_start);
3053 ok_v(aead_result(buf, tag))
3054 }
3055 Err(e) => aead_err(e),
3056 }
3057}
3058
3059fn chacha20_open_impl(args: &[Value]) -> Value {
3060 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
3061 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
3062 let (key, nonce, aad, ciphertext, tag) = match unpack5_bytes(args, "chacha20_poly1305_open") {
3063 Ok(t) => t,
3064 Err(e) => return err_v(Value::Str(e.into())),
3065 };
3066 if key.len() != 32 {
3067 return err_v(Value::Str(format!(
3068 "chacha20_poly1305_open: key must be exactly 32 bytes, got {}", key.len()
3069 ).into()));
3070 }
3071 if nonce.len() != 12 {
3072 return err_v(Value::Str(format!(
3073 "chacha20_poly1305_open: nonce must be exactly 12 bytes, got {}", nonce.len()
3074 ).into()));
3075 }
3076 if tag.len() != 16 {
3077 return err_v(Value::Str(format!(
3078 "chacha20_poly1305_open: tag must be exactly 16 bytes, got {}", tag.len()
3079 ).into()));
3080 }
3081 let mut combined = Vec::with_capacity(ciphertext.len() + tag.len());
3082 combined.extend_from_slice(ciphertext);
3083 combined.extend_from_slice(tag);
3084 let cipher = ChaCha20Poly1305::new_from_slice(key)
3085 .map_err(|e| format!("chacha20_poly1305_open: {e}"));
3086 let n = Nonce::from_slice(nonce);
3087 let payload = Payload { msg: &combined, aad };
3088 match cipher.and_then(|c| c.decrypt(n, payload).map_err(|e| format!("chacha20_poly1305_open: {e}"))) {
3089 Ok(p) => ok_v(Value::Bytes(p)),
3090 Err(e) => err_v(Value::Str(e.into())),
3091 }
3092}
3093
3094type Kdf4<'a> = (&'a Vec<u8>, &'a Vec<u8>, i64, i64);
3104
3105type Hkdf4<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, i64);
3108
3109type Argon5<'a> = (&'a Vec<u8>, &'a Vec<u8>, i64, i64, i64);
3112
3113fn pick_bytes<'a>(args: &'a [Value], i: usize, op: &str, name: &str)
3114 -> Result<&'a Vec<u8>, String>
3115{
3116 match args.get(i) {
3117 Some(Value::Bytes(b)) => Ok(b),
3118 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3119 None => Err(format!("{op}: missing {name} argument")),
3120 }
3121}
3122
3123fn pick_int(args: &[Value], i: usize, op: &str, name: &str) -> Result<i64, String> {
3124 match args.get(i) {
3125 Some(Value::Int(n)) => Ok(*n),
3126 Some(other) => Err(format!("{op}: {name} must be Int, got {other:?}")),
3127 None => Err(format!("{op}: missing {name} argument")),
3128 }
3129}
3130
3131fn unpack_kdf4<'a>(args: &'a [Value], op: &str) -> Result<Kdf4<'a>, String> {
3132 Ok((
3133 pick_bytes(args, 0, op, "password")?,
3134 pick_bytes(args, 1, op, "salt")?,
3135 pick_int(args, 2, op, "iterations")?,
3136 pick_int(args, 3, op, "len")?,
3137 ))
3138}
3139
3140fn unpack_hkdf4<'a>(args: &'a [Value], op: &str) -> Result<Hkdf4<'a>, String> {
3141 Ok((
3142 pick_bytes(args, 0, op, "ikm")?,
3143 pick_bytes(args, 1, op, "salt")?,
3144 pick_bytes(args, 2, op, "info")?,
3145 pick_int(args, 3, op, "len")?,
3146 ))
3147}
3148
3149fn unpack_argon5<'a>(args: &'a [Value], op: &str) -> Result<Argon5<'a>, String> {
3150 Ok((
3151 pick_bytes(args, 0, op, "password")?,
3152 pick_bytes(args, 1, op, "salt")?,
3153 pick_int(args, 2, op, "t_cost")?,
3154 pick_int(args, 3, op, "m_cost")?,
3155 pick_int(args, 4, op, "len")?,
3156 ))
3157}
3158
3159const KDF_MAX_LEN: usize = 1024 * 1024;
3164
3165fn check_len(op: &str, len: i64) -> Result<usize, String> {
3166 if len <= 0 {
3167 return Err(format!("{op}: len must be > 0, got {len}"));
3168 }
3169 if (len as u64) > KDF_MAX_LEN as u64 {
3170 return Err(format!(
3171 "{op}: len must be <= {KDF_MAX_LEN}, got {len}"
3172 ));
3173 }
3174 Ok(len as usize)
3175}
3176
3177fn pbkdf2_sha256_impl(args: &[Value]) -> Value {
3178 use hmac::Hmac;
3179 use sha2::Sha256;
3180 let op = "pbkdf2_sha256";
3181 let (password, salt, iterations, len) = match unpack_kdf4(args, op) {
3182 Ok(t) => t,
3183 Err(e) => return err_v(Value::Str(e.into())),
3184 };
3185 if iterations <= 0 {
3186 return err_v(Value::Str(format!(
3187 "{op}: iterations must be > 0, got {iterations}"
3188 ).into()));
3189 }
3190 let out_len = match check_len(op, len) {
3191 Ok(n) => n,
3192 Err(e) => return err_v(Value::Str(e.into())),
3193 };
3194 let rounds = match u32::try_from(iterations) {
3195 Ok(r) => r,
3196 Err(_) => {
3197 return err_v(Value::Str(format!(
3198 "{op}: iterations must fit in u32, got {iterations}"
3199 ).into()))
3200 }
3201 };
3202 let mut out = vec![0u8; out_len];
3203 if let Err(e) = pbkdf2::pbkdf2::<Hmac<Sha256>>(password, salt, rounds, &mut out) {
3204 return err_v(Value::Str(format!("{op}: {e}").into()));
3205 }
3206 ok_v(Value::Bytes(out))
3207}
3208
3209fn hkdf_sha256_impl(args: &[Value]) -> Value {
3210 use hkdf::Hkdf;
3211 use sha2::Sha256;
3212 let op = "hkdf_sha256";
3213 let (ikm, salt, info, len) = match unpack_hkdf4(args, op) {
3214 Ok(t) => t,
3215 Err(e) => return err_v(Value::Str(e.into())),
3216 };
3217 let out_len = match check_len(op, len) {
3218 Ok(n) => n,
3219 Err(e) => return err_v(Value::Str(e.into())),
3220 };
3221 let salt_opt: Option<&[u8]> = if salt.is_empty() { None } else { Some(salt) };
3224 let hk = Hkdf::<Sha256>::new(salt_opt, ikm);
3225 let mut out = vec![0u8; out_len];
3226 match hk.expand(info, &mut out) {
3227 Ok(()) => ok_v(Value::Bytes(out)),
3228 Err(e) => err_v(Value::Str(format!("{op}: {e}").into())),
3229 }
3230}
3231
3232fn argon2id_impl(args: &[Value]) -> Value {
3233 use argon2::{Algorithm, Argon2, Params, Version};
3234 let op = "argon2id";
3235 let (password, salt, t_cost, m_cost, len) = match unpack_argon5(args, op) {
3236 Ok(t) => t,
3237 Err(e) => return err_v(Value::Str(e.into())),
3238 };
3239 let out_len = match check_len(op, len) {
3240 Ok(n) => n,
3241 Err(e) => return err_v(Value::Str(e.into())),
3242 };
3243 let t = match u32::try_from(t_cost) {
3244 Ok(n) if n >= 1 => n,
3245 _ => return err_v(Value::Str(format!(
3246 "{op}: t_cost must be a u32 >= 1, got {t_cost}"
3247 ).into())),
3248 };
3249 let m = match u32::try_from(m_cost) {
3250 Ok(n) if n >= Params::MIN_M_COST => n,
3251 _ => return err_v(Value::Str(format!(
3252 "{op}: m_cost must be a u32 >= {}, got {m_cost}",
3253 Params::MIN_M_COST
3254 ).into())),
3255 };
3256 let params = match Params::new(m, t, 1, Some(out_len)) {
3261 Ok(p) => p,
3262 Err(e) => return err_v(Value::Str(format!("{op}: {e}").into())),
3263 };
3264 let hasher = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
3265 let mut out = vec![0u8; out_len];
3266 if let Err(e) = hasher.hash_password_into(password, salt, &mut out) {
3267 return err_v(Value::Str(format!("{op}: {e}").into()));
3268 }
3269 ok_v(Value::Bytes(out))
3270}