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 ("int", "abs") => Ok(Value::Int(expect_int(args.first())?.abs())),
216 ("int", "min") => Ok(Value::Int(expect_int(args.first())?.min(expect_int(args.get(1))?))),
217 ("int", "max") => Ok(Value::Int(expect_int(args.first())?.max(expect_int(args.get(1))?))),
218 ("float", "to_int") => Ok(Value::Int(expect_float(args.first())? as i64)),
219 ("float", "to_str") => Ok(Value::Str(expect_float(args.first())?.to_string().into())),
220 ("str", "to_float") => {
221 let s = expect_str(args.first())?;
222 match s.parse::<f64>() {
223 Ok(f) => Ok(some(Value::Float(f))),
224 Err(_) => Ok(none()),
225 }
226 }
227
228 ("list", "len") => Ok(Value::Int(expect_list(args.first())?.len() as i64)),
230 ("list", "is_empty") => Ok(Value::Bool(expect_list(args.first())?.is_empty())),
231 ("list", "head") => {
232 let xs = expect_list(args.first())?;
233 match xs.front() {
234 Some(v) => Ok(some(v.clone())),
235 None => Ok(none()),
236 }
237 }
238 ("list", "tail") => {
239 let xs = expect_list(args.first())?;
240 if xs.is_empty() { Ok(Value::List(std::collections::VecDeque::new())) }
241 else { Ok(Value::List(xs.iter().skip(1).cloned().collect::<std::collections::VecDeque<_>>())) }
242 }
243 ("list", "range") => {
244 let lo = expect_int(args.first())?;
245 let hi = expect_int(args.get(1))?;
246 Ok(Value::List((lo..hi).map(Value::Int).collect::<std::collections::VecDeque<_>>()))
247 }
248 ("list", "concat") => {
249 let mut out = expect_list(args.first())?.clone();
250 out.extend(expect_list(args.get(1))?.iter().cloned());
251 Ok(Value::List(out))
252 }
253 ("list", "reverse") => {
254 let out = expect_list(args.first())?.clone();
255 let rev: std::collections::VecDeque<Value> = out.into_iter().rev().collect();
256 Ok(Value::List(rev))
257 }
258 ("list", "cons") => {
262 let head = args.first().cloned().unwrap_or(Value::Unit);
263 let mut out: std::collections::VecDeque<Value> =
264 expect_list(args.get(1))?.iter().cloned().collect();
265 out.push_front(head);
266 Ok(Value::List(out))
267 }
268 ("list", "enumerate") => {
269 let xs = expect_list(args.first())?;
270 let pairs = xs.iter().cloned().enumerate()
271 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v]))
272 .collect::<std::collections::VecDeque<_>>();
273 Ok(Value::List(pairs))
274 }
275
276 ("tuple", "fst") => tuple_index(first_arg(args)?, 0),
281 ("tuple", "snd") => tuple_index(first_arg(args)?, 1),
282 ("tuple", "third") => tuple_index(first_arg(args)?, 2),
283 ("tuple", "len") => match first_arg(args)? {
284 Value::Tuple(items) => Ok(Value::Int(items.len() as i64)),
285 other => Err(format!("tuple.len: expected Tuple, got {other:?}")),
286 },
287
288 ("option", "unwrap_or") => {
290 let opt = first_arg(args)?;
291 let default = args.get(1).cloned().unwrap_or(Value::Unit);
292 match opt {
293 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
294 Value::Variant { name, .. } if name == "None" => Ok(default),
295 other => Err(format!("option.unwrap_or expected Option, got {other:?}")),
296 }
297 }
298 ("option", "unwrap_or_else") => {
303 let opt = first_arg(args)?;
304 match opt {
305 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
306 Value::Variant { name, .. } if name == "None" => {
307 Ok(args.get(1).cloned().unwrap_or(Value::Unit))
311 }
312 other => Err(format!("option.unwrap_or_else expected Option, got {other:?}")),
313 }
314 }
315 ("option", "is_some") => match first_arg(args)? {
316 Value::Variant { name, .. } => Ok(Value::Bool(name == "Some")),
317 other => Err(format!("option.is_some expected Option, got {other:?}")),
318 },
319 ("option", "is_none") => match first_arg(args)? {
320 Value::Variant { name, .. } => Ok(Value::Bool(name == "None")),
321 other => Err(format!("option.is_none expected Option, got {other:?}")),
322 },
323 ("option", "ok_or") => {
325 let opt = first_arg(args)?;
326 let err_val = args.get(1).cloned().unwrap_or(Value::Unit);
327 match opt {
328 Value::Variant { name, args } if name == "Some" && !args.is_empty() =>
329 Ok(ok_v(args[0].clone())),
330 Value::Variant { name, .. } if name == "None" => Ok(err_v(err_val)),
331 other => Err(format!("option.ok_or expected Option, got {other:?}")),
332 }
333 }
334
335 ("result", "is_ok") => match first_arg(args)? {
337 Value::Variant { name, .. } => Ok(Value::Bool(name == "Ok")),
338 other => Err(format!("result.is_ok expected Result, got {other:?}")),
339 },
340 ("result", "is_err") => match first_arg(args)? {
341 Value::Variant { name, .. } => Ok(Value::Bool(name == "Err")),
342 other => Err(format!("result.is_err expected Result, got {other:?}")),
343 },
344 ("result", "unwrap_or") => {
345 let res = first_arg(args)?;
346 let default = args.get(1).cloned().unwrap_or(Value::Unit);
347 match res {
348 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
349 Value::Variant { name, .. } if name == "Err" => Ok(default),
350 other => Err(format!("result.unwrap_or expected Result, got {other:?}")),
351 }
352 }
353 ("result", "unwrap_or_else") => {
358 let res = first_arg(args)?;
359 match res {
360 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
361 Value::Variant { name, .. } if name == "Err" =>
362 Ok(args.get(1).cloned().unwrap_or(Value::Unit)),
363 other => Err(format!("result.unwrap_or_else expected Result, got {other:?}")),
364 }
365 }
366
367 ("json", "stringify") => {
369 let v = first_arg(args)?;
370 Ok(Value::Str(serde_json::to_string(&value_to_json(v)).unwrap_or_default().into()))
371 }
372 ("json", "parse") => {
373 let s = expect_str(args.first())?;
374 match serde_json::from_str::<serde_json::Value>(&s) {
375 Ok(v) => Ok(ok_v(json_to_value(&v))),
376 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
377 }
378 }
379 ("json", "parse_strict") => {
382 let s = expect_str(args.first())?;
383 let required = required_field_names(args.get(1))?;
384 let schema = extract_type_schema(args.get(2));
385 match serde_json::from_str::<serde_json::Value>(&s) {
386 Ok(v) => {
387 if let Err(e) = check_required_fields(&v, &required) {
388 return Ok(err_v(Value::Str(e.into())));
389 }
390 if let Err(e) = validate_field_types(&v, &schema) {
391 return Ok(err_v(Value::Str(e.into())));
392 }
393 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
394 }
395 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
396 }
397 }
398
399 ("toml", "parse") => {
404 let s = expect_str(args.first())?;
405 match toml::from_str::<serde_json::Value>(&s) {
406 Ok(mut v) => {
407 unwrap_toml_datetime_markers(&mut v);
408 Ok(ok_v(json_to_value(&v)))
409 }
410 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
411 }
412 }
413 ("json", "parse_strict_typed") => {
417 let s = expect_str(args.first())?;
418 let required = required_field_names(args.get(1))?;
419 let schema = extract_type_schema(args.get(2));
420 match serde_json::from_str::<serde_json::Value>(&s) {
421 Ok(v) => {
422 if let Err(e) = check_required_fields(&v, &required) {
423 return Ok(err_v(Value::Str(e.into())));
424 }
425 if let Err(e) = validate_field_types(&v, &schema) {
426 return Ok(err_v(Value::Str(e.into())));
427 }
428 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
429 }
430 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
431 }
432 }
433
434 ("toml", "parse_strict") => {
437 let s = expect_str(args.first())?;
438 let required = required_field_names(args.get(1))?;
439 let schema = extract_type_schema(args.get(2));
440 match toml::from_str::<serde_json::Value>(&s) {
441 Ok(mut v) => {
442 unwrap_toml_datetime_markers(&mut v);
443 if let Err(e) = check_required_fields(&v, &required) {
444 return Ok(err_v(Value::Str(e.into())));
445 }
446 if let Err(e) = validate_field_types(&v, &schema) {
447 return Ok(err_v(Value::Str(e.into())));
448 }
449 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
450 }
451 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
452 }
453 }
454 ("toml", "parse_strict_typed") => {
455 let s = expect_str(args.first())?;
456 let required = required_field_names(args.get(1))?;
457 let schema = extract_type_schema(args.get(2));
458 match toml::from_str::<serde_json::Value>(&s) {
459 Ok(mut v) => {
460 unwrap_toml_datetime_markers(&mut v);
461 if let Err(e) = check_required_fields(&v, &required) {
462 return Ok(err_v(Value::Str(e.into())));
463 }
464 if let Err(e) = validate_field_types(&v, &schema) {
465 return Ok(err_v(Value::Str(e.into())));
466 }
467 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
468 }
469 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
470 }
471 }
472 ("toml", "stringify") => {
473 let v = first_arg(args)?;
474 let json = value_to_json(v);
480 match toml::to_string(&json) {
481 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
482 Err(e) => Ok(err_v(Value::Str(format!("toml.stringify: {e}").into()))),
483 }
484 }
485
486 ("yaml", "parse") => {
492 let s = expect_str(args.first())?;
493 match serde_yaml::from_str::<serde_json::Value>(&s) {
494 Ok(v) => Ok(ok_v(json_to_value(&v))),
495 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
496 }
497 }
498 ("yaml", "parse_strict") => {
501 let s = expect_str(args.first())?;
502 let required = required_field_names(args.get(1))?;
503 let schema = extract_type_schema(args.get(2));
504 match serde_yaml::from_str::<serde_json::Value>(&s) {
505 Ok(v) => {
506 if let Err(e) = check_required_fields(&v, &required) {
507 return Ok(err_v(Value::Str(e.into())));
508 }
509 if let Err(e) = validate_field_types(&v, &schema) {
510 return Ok(err_v(Value::Str(e.into())));
511 }
512 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
513 }
514 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
515 }
516 }
517 ("yaml", "parse_strict_typed") => {
518 let s = expect_str(args.first())?;
519 let required = required_field_names(args.get(1))?;
520 let schema = extract_type_schema(args.get(2));
521 match serde_yaml::from_str::<serde_json::Value>(&s) {
522 Ok(v) => {
523 if let Err(e) = check_required_fields(&v, &required) {
524 return Ok(err_v(Value::Str(e.into())));
525 }
526 if let Err(e) = validate_field_types(&v, &schema) {
527 return Ok(err_v(Value::Str(e.into())));
528 }
529 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
530 }
531 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
532 }
533 }
534 ("yaml", "stringify") => {
535 let v = first_arg(args)?;
536 let json = value_to_json(v);
537 match serde_yaml::to_string(&json) {
538 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
539 Err(e) => Ok(err_v(Value::Str(format!("yaml.stringify: {e}").into()))),
540 }
541 }
542
543 ("dotenv", "parse") => {
551 use std::collections::BTreeMap;
552 use lex_bytecode::MapKey;
553 let s = expect_str(args.first())?;
554 match parse_dotenv(&s) {
555 Ok(map) => {
556 let mut bt: BTreeMap<MapKey, Value> = BTreeMap::new();
557 for (k, v) in map {
558 bt.insert(MapKey::Str(k), Value::Str(v.into()));
559 }
560 Ok(ok_v(Value::Map(bt)))
561 }
562 Err(e) => Ok(err_v(Value::Str(e.into()))),
563 }
564 }
565
566 ("csv", "parse") => {
571 let s = expect_str(args.first())?;
572 let mut rdr = csv::ReaderBuilder::new()
573 .has_headers(false)
574 .flexible(true)
575 .from_reader(s.as_bytes());
576 let mut rows: std::collections::VecDeque<Value> = std::collections::VecDeque::new();
577 for r in rdr.records() {
578 match r {
579 Ok(rec) => {
580 let row: std::collections::VecDeque<Value> = rec.iter()
581 .map(|f| Value::Str(f.into()))
582 .collect();
583 rows.push_back(Value::List(row));
584 }
585 Err(e) => return Ok(err_v(Value::Str(format!("csv.parse: {e}").into()))),
586 }
587 }
588 Ok(ok_v(Value::List(rows)))
589 }
590 ("csv", "stringify") => {
591 let v = first_arg(args)?;
596 let rows = match v {
597 Value::List(rs) => rs,
598 _ => return Ok(err_v(Value::Str("csv.stringify expects List[List[Str]]".into()))),
599 };
600 let mut out = Vec::new();
601 {
602 let mut wtr = csv::WriterBuilder::new()
603 .has_headers(false)
604 .from_writer(&mut out);
605 for row in rows {
606 let cells = match row {
607 Value::List(cs) => cs,
608 _ => return Ok(err_v(Value::Str("csv.stringify row must be List[Str]".into()))),
609 };
610 let strs: Vec<String> = cells.iter().map(|c| match c {
611 Value::Str(s) => s.to_string(),
612 other => serde_json::to_string(&other.to_json())
613 .unwrap_or_else(|_| String::new()),
614 }).collect();
615 if let Err(e) = wtr.write_record(&strs) {
616 return Ok(err_v(Value::Str(format!("csv.stringify: {e}").into())));
617 }
618 }
619 if let Err(e) = wtr.flush() {
620 return Ok(err_v(Value::Str(format!("csv.stringify flush: {e}").into())));
621 }
622 }
623 match String::from_utf8(out) {
624 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
625 Err(e) => Ok(err_v(Value::Str(format!("csv.stringify utf8: {e}").into()))),
626 }
627 }
628
629 ("test", "assert_eq") => {
636 let a = first_arg(args)?;
637 let b = args.get(1).ok_or("test.assert_eq: missing second arg")?;
638 if a == b {
639 Ok(ok_v(Value::Unit))
640 } else {
641 Ok(err_v(Value::Str(format!("assert_eq: lhs {} != rhs {}",
642 value_to_json(a), value_to_json(b)).into())))
643 }
644 }
645 ("test", "assert_ne") => {
646 let a = first_arg(args)?;
647 let b = args.get(1).ok_or("test.assert_ne: missing second arg")?;
648 if a != b {
649 Ok(ok_v(Value::Unit))
650 } else {
651 Ok(err_v(Value::Str(format!("assert_ne: both sides are {}",
652 value_to_json(a)).into())))
653 }
654 }
655 ("test", "assert_true") => {
656 match first_arg(args)? {
657 Value::Bool(true) => Ok(ok_v(Value::Unit)),
658 Value::Bool(false) => Ok(err_v(Value::Str("assert_true: was false".into()))),
659 other => Err(format!("test.assert_true expects Bool, got {other:?}")),
660 }
661 }
662 ("test", "assert_false") => {
663 match first_arg(args)? {
664 Value::Bool(false) => Ok(ok_v(Value::Unit)),
665 Value::Bool(true) => Ok(err_v(Value::Str("assert_false: was true".into()))),
666 other => Err(format!("test.assert_false expects Bool, got {other:?}")),
667 }
668 }
669
670 ("bytes", "len") => {
672 let b = expect_bytes(args.first())?;
673 Ok(Value::Int(b.len() as i64))
674 }
675 ("bytes", "eq") => {
676 let a = expect_bytes(args.first())?;
677 let b = expect_bytes(args.get(1))?;
678 Ok(Value::Bool(a == b))
679 }
680 ("bytes", "from_str") => {
681 let s = expect_str(args.first())?;
682 Ok(Value::Bytes(s.into_bytes()))
683 }
684 ("bytes", "to_str") => {
685 let b = expect_bytes(args.first())?;
686 match String::from_utf8(b.to_vec()) {
687 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
688 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
689 }
690 }
691 ("bytes", "slice") => {
692 let b = expect_bytes(args.first())?;
693 let lo = expect_int(args.get(1))? as usize;
694 let hi = expect_int(args.get(2))? as usize;
695 if lo > hi || hi > b.len() {
696 return Err(format!("bytes.slice: out of range [{lo}..{hi}] of {}", b.len()));
697 }
698 Ok(Value::Bytes(b[lo..hi].to_vec()))
699 }
700 ("bytes", "is_empty") => {
701 let b = expect_bytes(args.first())?;
702 Ok(Value::Bool(b.is_empty()))
703 }
704 ("bytes", "concat") => {
705 let a = expect_bytes(args.first())?;
706 let b = expect_bytes(args.get(1))?;
707 let mut out = a.clone();
708 out.extend_from_slice(b);
709 Ok(Value::Bytes(out))
710 }
711 ("bytes", "concat_all") => {
712 let items = match args.first() {
713 Some(Value::List(xs)) => xs,
714 other => return Err(format!("bytes.concat_all expects a List[Bytes], got {other:?}")),
715 };
716 let mut out = Vec::new();
717 for item in items {
718 match item {
719 Value::Bytes(b) => out.extend_from_slice(b),
720 other => return Err(format!("bytes.concat_all: list element is not Bytes, got {other:?}")),
721 }
722 }
723 Ok(Value::Bytes(out))
724 }
725 ("bytes", "u8") => {
726 let n = expect_int(args.first())?;
727 Ok(Value::Bytes(vec![n as u8]))
728 }
729 ("bytes", "u16_le") => {
730 let n = expect_int(args.first())?;
731 Ok(Value::Bytes((n as u16).to_le_bytes().to_vec()))
732 }
733 ("bytes", "u32_le") => {
734 let n = expect_int(args.first())?;
735 Ok(Value::Bytes((n as u32).to_le_bytes().to_vec()))
736 }
737 ("bytes", "u64_le") => {
738 let n = expect_int(args.first())?;
739 Ok(Value::Bytes((n as u64).to_le_bytes().to_vec()))
740 }
741 ("bytes", "u8_at") => {
742 let b = expect_bytes(args.first())?;
743 let off = expect_int(args.get(1))? as usize;
744 match b.get(off) {
745 Some(&byte) => Ok(ok_v(Value::Int(byte as i64))),
746 None => Ok(err_v(Value::Str(format!("bytes.u8_at: offset {off} out of range of {} bytes", b.len()).into()))),
747 }
748 }
749 ("bytes", "u16_le_at") => {
750 let b = expect_bytes(args.first())?;
751 let off = expect_int(args.get(1))? as usize;
752 match b.get(off..off + 2) {
753 Some(slice) => {
754 let arr: [u8; 2] = slice.try_into().unwrap();
755 Ok(ok_v(Value::Int(u16::from_le_bytes(arr) as i64)))
756 }
757 None => Ok(err_v(Value::Str(format!("bytes.u16_le_at: offset {off} out of range of {} bytes", b.len()).into()))),
758 }
759 }
760 ("bytes", "u32_le_at") => {
761 let b = expect_bytes(args.first())?;
762 let off = expect_int(args.get(1))? as usize;
763 match b.get(off..off + 4) {
764 Some(slice) => {
765 let arr: [u8; 4] = slice.try_into().unwrap();
766 Ok(ok_v(Value::Int(u32::from_le_bytes(arr) as i64)))
767 }
768 None => Ok(err_v(Value::Str(format!("bytes.u32_le_at: offset {off} out of range of {} bytes", b.len()).into()))),
769 }
770 }
771 ("bytes", "u64_le_at") => {
772 let b = expect_bytes(args.first())?;
773 let off = expect_int(args.get(1))? as usize;
774 match b.get(off..off + 8) {
775 Some(slice) => {
776 let arr: [u8; 8] = slice.try_into().unwrap();
777 Ok(ok_v(Value::Int(u64::from_le_bytes(arr) as i64)))
778 }
779 None => Ok(err_v(Value::Str(format!("bytes.u64_le_at: offset {off} out of range of {} bytes", b.len()).into()))),
780 }
781 }
782
783 ("math", "exp") => Ok(Value::Float(expect_float(args.first())?.exp())),
790 ("math", "log") => Ok(Value::Float(expect_float(args.first())?.ln())),
791 ("math", "log2") => Ok(Value::Float(expect_float(args.first())?.log2())),
792 ("math", "log10") => Ok(Value::Float(expect_float(args.first())?.log10())),
793 ("math", "sqrt") => Ok(Value::Float(expect_float(args.first())?.sqrt())),
794 ("math", "abs") => Ok(Value::Float(expect_float(args.first())?.abs())),
795 ("math", "sin") => Ok(Value::Float(expect_float(args.first())?.sin())),
796 ("math", "cos") => Ok(Value::Float(expect_float(args.first())?.cos())),
797 ("math", "tan") => Ok(Value::Float(expect_float(args.first())?.tan())),
798 ("math", "asin") => Ok(Value::Float(expect_float(args.first())?.asin())),
799 ("math", "acos") => Ok(Value::Float(expect_float(args.first())?.acos())),
800 ("math", "atan") => Ok(Value::Float(expect_float(args.first())?.atan())),
801 ("math", "floor") => Ok(Value::Float(expect_float(args.first())?.floor())),
802 ("math", "ceil") => Ok(Value::Float(expect_float(args.first())?.ceil())),
803 ("math", "round") => Ok(Value::Float(expect_float(args.first())?.round())),
804 ("math", "trunc") => Ok(Value::Float(expect_float(args.first())?.trunc())),
805 ("math", "pow") => {
806 let a = expect_float(args.first())?;
807 let b = expect_float(args.get(1))?;
808 Ok(Value::Float(a.powf(b)))
809 }
810 ("math", "atan2") => {
811 let y = expect_float(args.first())?;
812 let x = expect_float(args.get(1))?;
813 Ok(Value::Float(y.atan2(x)))
814 }
815 ("math", "min") => {
816 let a = expect_float(args.first())?;
817 let b = expect_float(args.get(1))?;
818 Ok(Value::Float(a.min(b)))
819 }
820 ("math", "max") => {
821 let a = expect_float(args.first())?;
822 let b = expect_float(args.get(1))?;
823 Ok(Value::Float(a.max(b)))
824 }
825 ("math", "zeros") => {
826 let r = expect_int(args.first())?;
827 let c = expect_int(args.get(1))?;
828 if r < 0 || c < 0 {
829 return Err(format!("math.zeros: negative dim {r}x{c}"));
830 }
831 let r = r as usize; let c = c as usize;
832 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![0.0; r * c] })
833 }
834 ("math", "ones") => {
835 let r = expect_int(args.first())?;
836 let c = expect_int(args.get(1))?;
837 if r < 0 || c < 0 {
838 return Err(format!("math.ones: negative dim {r}x{c}"));
839 }
840 let r = r as usize; let c = c as usize;
841 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![1.0; r * c] })
842 }
843 ("math", "from_lists") => {
844 let rows = expect_list(args.first())?;
845 let r = rows.len();
846 if r == 0 {
847 return Ok(Value::F64Array { rows: 0, cols: 0, data: Vec::new() });
848 }
849 let first_row = match &rows[0] {
850 Value::List(xs) => xs,
851 other => return Err(format!("math.from_lists: row 0 not List, got {other:?}")),
852 };
853 let c = first_row.len();
854 let mut data = Vec::with_capacity(r * c);
855 for (i, row) in rows.iter().enumerate() {
856 let row = match row {
857 Value::List(xs) => xs,
858 other => return Err(format!("math.from_lists: row {i} not List, got {other:?}")),
859 };
860 if row.len() != c {
861 return Err(format!("math.from_lists: row {i} has {} cols, expected {c}", row.len()));
862 }
863 for (j, v) in row.iter().enumerate() {
864 let f = match v {
865 Value::Float(f) => *f,
866 Value::Int(n) => *n as f64,
867 other => return Err(format!("math.from_lists: ({i},{j}) not numeric, got {other:?}")),
868 };
869 data.push(f);
870 }
871 }
872 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
873 }
874 ("math", "from_flat") => {
875 let r = expect_int(args.first())?;
876 let c = expect_int(args.get(1))?;
877 let xs = expect_list(args.get(2))?;
878 if r < 0 || c < 0 {
879 return Err(format!("math.from_flat: negative dim {r}x{c}"));
880 }
881 let r = r as usize; let c = c as usize;
882 if xs.len() != r * c {
883 return Err(format!("math.from_flat: list len {} != {}*{}", xs.len(), r, c));
884 }
885 let mut data = Vec::with_capacity(r * c);
886 for v in xs {
887 data.push(match v {
888 Value::Float(f) => *f,
889 Value::Int(n) => *n as f64,
890 other => return Err(format!("math.from_flat: non-numeric element {other:?}")),
891 });
892 }
893 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
894 }
895 ("math", "rows") => {
896 let (r, _, _) = unpack_matrix(first_arg(args)?)?;
897 Ok(Value::Int(r as i64))
898 }
899 ("math", "cols") => {
900 let (_, c, _) = unpack_matrix(first_arg(args)?)?;
901 Ok(Value::Int(c as i64))
902 }
903 ("math", "get") => {
904 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
905 let i = expect_int(args.get(1))? as usize;
906 let j = expect_int(args.get(2))? as usize;
907 if i >= r || j >= c {
908 return Err(format!("math.get: ({i},{j}) out of {r}x{c}"));
909 }
910 Ok(Value::Float(data[i * c + j]))
911 }
912 ("math", "to_flat") => {
913 let (_, _, data) = unpack_matrix(first_arg(args)?)?;
914 Ok(Value::List(data.into_iter().map(Value::Float).collect()))
915 }
916 ("math", "transpose") => {
917 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
918 let mut out = vec![0.0; r * c];
919 for i in 0..r {
920 for j in 0..c {
921 out[j * r + i] = data[i * c + j];
922 }
923 }
924 Ok(Value::F64Array { rows: c as u32, cols: r as u32, data: out })
925 }
926 ("math", "matmul") => {
927 let (m, k1, a) = unpack_matrix(first_arg(args)?)?;
928 let (k2, n, b) = unpack_matrix(args.get(1).ok_or("math.matmul: missing arg 1")?)?;
929 if k1 != k2 {
930 return Err(format!("math.matmul: dim mismatch {m}x{k1} · {k2}x{n}"));
931 }
932 let mut c = vec![0.0; m * n];
936 for i in 0..m {
937 for kk in 0..k1 {
938 let aik = a[i * k1 + kk];
939 for j in 0..n {
940 c[i * n + j] += aik * b[kk * n + j];
941 }
942 }
943 }
944 Ok(Value::F64Array { rows: m as u32, cols: n as u32, data: c })
945 }
946 ("math", "scale") => {
947 let s = expect_float(args.first())?;
948 let (r, c, mut data) = unpack_matrix(args.get(1).ok_or("math.scale: missing arg 1")?)?;
949 for x in &mut data { *x *= s; }
950 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
951 }
952 ("math", "add") | ("math", "sub") => {
953 let (ar, ac, a) = unpack_matrix(first_arg(args)?)?;
954 let (br, bc, b) = unpack_matrix(args.get(1).ok_or("math.add/sub: missing arg 1")?)?;
955 if ar != br || ac != bc {
956 return Err(format!("math.{op}: shape mismatch {ar}x{ac} vs {br}x{bc}"));
957 }
958 let neg = op == "sub";
959 let mut out = a;
960 for (i, x) in out.iter_mut().enumerate() {
961 if neg { *x -= b[i] } else { *x += b[i] }
962 }
963 Ok(Value::F64Array { rows: ar as u32, cols: ac as u32, data: out })
964 }
965 ("math", "sigmoid") => {
966 let (r, c, mut data) = unpack_matrix(first_arg(args)?)?;
967 for x in &mut data { *x = 1.0 / (1.0 + (-*x).exp()); }
968 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
969 }
970
971 ("map", "new") => Ok(Value::Map(BTreeMap::new())),
973 ("map", "size") => Ok(Value::Int(expect_map(args.first())?.len() as i64)),
974 ("map", "has") => {
975 let m = expect_map(args.first())?;
976 let k = MapKey::from_value(args.get(1).ok_or("map.has: missing key")?)?;
977 Ok(Value::Bool(m.contains_key(&k)))
978 }
979 ("map", "get") => {
980 let m = expect_map(args.first())?;
981 let k = MapKey::from_value(args.get(1).ok_or("map.get: missing key")?)?;
982 Ok(match m.get(&k) {
983 Some(v) => some(v.clone()),
984 None => none(),
985 })
986 }
987 ("map", "set") => {
988 let mut m = expect_map(args.first())?.clone();
989 let k = MapKey::from_value(args.get(1).ok_or("map.set: missing key")?)?;
990 let v = args.get(2).ok_or("map.set: missing value")?.clone();
991 m.insert(k, v);
992 Ok(Value::Map(m))
993 }
994 ("map", "delete") => {
995 let mut m = expect_map(args.first())?.clone();
996 let k = MapKey::from_value(args.get(1).ok_or("map.delete: missing key")?)?;
997 m.remove(&k);
998 Ok(Value::Map(m))
999 }
1000 ("map", "keys") => {
1001 let m = expect_map(args.first())?;
1002 Ok(Value::List(m.keys().cloned().map(MapKey::into_value).collect()))
1003 }
1004 ("map", "values") => {
1005 let m = expect_map(args.first())?;
1006 Ok(Value::List(m.values().cloned().collect()))
1007 }
1008 ("map", "entries") => {
1009 let m = expect_map(args.first())?;
1010 Ok(Value::List(m.iter()
1011 .map(|(k, v)| Value::Tuple(vec![k.as_value(), v.clone()]))
1012 .collect()))
1013 }
1014 ("map", "from_list") => {
1015 let pairs = expect_list(args.first())?;
1016 let mut m = BTreeMap::new();
1017 for p in pairs {
1018 let items = match p {
1019 Value::Tuple(items) if items.len() == 2 => items,
1020 other => return Err(format!(
1021 "map.from_list element must be a 2-tuple, got {other:?}")),
1022 };
1023 let k = MapKey::from_value(&items[0])?;
1024 m.insert(k, items[1].clone());
1025 }
1026 Ok(Value::Map(m))
1027 }
1028
1029 ("set", "new") => Ok(Value::Set(BTreeSet::new())),
1031 ("set", "size") => Ok(Value::Int(expect_set(args.first())?.len() as i64)),
1032 ("set", "has") => {
1033 let s = expect_set(args.first())?;
1034 let k = MapKey::from_value(args.get(1).ok_or("set.has: missing element")?)?;
1035 Ok(Value::Bool(s.contains(&k)))
1036 }
1037 ("set", "add") => {
1038 let mut s = expect_set(args.first())?.clone();
1039 let k = MapKey::from_value(args.get(1).ok_or("set.add: missing element")?)?;
1040 s.insert(k);
1041 Ok(Value::Set(s))
1042 }
1043 ("set", "delete") => {
1044 let mut s = expect_set(args.first())?.clone();
1045 let k = MapKey::from_value(args.get(1).ok_or("set.delete: missing element")?)?;
1046 s.remove(&k);
1047 Ok(Value::Set(s))
1048 }
1049 ("set", "to_list") => {
1050 let s = expect_set(args.first())?;
1051 Ok(Value::List(s.iter().cloned().map(MapKey::into_value).collect()))
1052 }
1053 ("set", "from_list") => {
1054 let xs = expect_list(args.first())?;
1055 let mut s = BTreeSet::new();
1056 for x in xs {
1057 s.insert(MapKey::from_value(x)?);
1058 }
1059 Ok(Value::Set(s))
1060 }
1061 ("set", "union") => {
1062 let a = expect_set(args.first())?;
1063 let b = expect_set(args.get(1))?;
1064 Ok(Value::Set(a.union(b).cloned().collect()))
1065 }
1066 ("set", "intersect") => {
1067 let a = expect_set(args.first())?;
1068 let b = expect_set(args.get(1))?;
1069 Ok(Value::Set(a.intersection(b).cloned().collect()))
1070 }
1071 ("set", "diff") => {
1072 let a = expect_set(args.first())?;
1073 let b = expect_set(args.get(1))?;
1074 Ok(Value::Set(a.difference(b).cloned().collect()))
1075 }
1076 ("set", "is_empty") => Ok(Value::Bool(expect_set(args.first())?.is_empty())),
1077 ("set", "is_subset") => {
1078 let a = expect_set(args.first())?;
1079 let b = expect_set(args.get(1))?;
1080 Ok(Value::Bool(a.is_subset(b)))
1081 }
1082
1083 ("map", "merge") => {
1085 let a = expect_map(args.first())?.clone();
1088 let b = expect_map(args.get(1))?;
1089 let mut out = a;
1090 for (k, v) in b {
1091 out.insert(k.clone(), v.clone());
1092 }
1093 Ok(Value::Map(out))
1094 }
1095 ("map", "is_empty") => Ok(Value::Bool(expect_map(args.first())?.is_empty())),
1096
1097 ("deque", "new") => Ok(Value::Deque(std::collections::VecDeque::new())),
1099 ("deque", "size") => Ok(Value::Int(expect_deque(args.first())?.len() as i64)),
1100 ("deque", "is_empty") => Ok(Value::Bool(expect_deque(args.first())?.is_empty())),
1101 ("deque", "push_back") => {
1102 let mut d = expect_deque(args.first())?.clone();
1103 let x = args.get(1).ok_or("deque.push_back: missing value")?.clone();
1104 d.push_back(x);
1105 Ok(Value::Deque(d))
1106 }
1107 ("deque", "push_front") => {
1108 let mut d = expect_deque(args.first())?.clone();
1109 let x = args.get(1).ok_or("deque.push_front: missing value")?.clone();
1110 d.push_front(x);
1111 Ok(Value::Deque(d))
1112 }
1113 ("deque", "pop_back") => {
1114 let mut d = expect_deque(args.first())?.clone();
1115 match d.pop_back() {
1116 Some(x) => Ok(Value::Variant {
1117 name: "Some".into(),
1118 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
1119 }),
1120 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1121 }
1122 }
1123 ("deque", "pop_front") => {
1124 let mut d = expect_deque(args.first())?.clone();
1125 match d.pop_front() {
1126 Some(x) => Ok(Value::Variant {
1127 name: "Some".into(),
1128 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
1129 }),
1130 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1131 }
1132 }
1133 ("deque", "peek_back") => {
1134 let d = expect_deque(args.first())?;
1135 match d.back() {
1136 Some(x) => Ok(Value::Variant {
1137 name: "Some".into(),
1138 args: vec![x.clone()],
1139 }),
1140 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1141 }
1142 }
1143 ("deque", "peek_front") => {
1144 let d = expect_deque(args.first())?;
1145 match d.front() {
1146 Some(x) => Ok(Value::Variant {
1147 name: "Some".into(),
1148 args: vec![x.clone()],
1149 }),
1150 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1151 }
1152 }
1153 ("deque", "from_list") => {
1154 let xs = expect_list(args.first())?;
1155 Ok(Value::Deque(xs.iter().cloned().collect()))
1156 }
1157 ("deque", "to_list") => {
1158 let d = expect_deque(args.first())?;
1159 Ok(Value::List(d.iter().cloned().collect()))
1160 }
1161
1162 ("crypto", "sha256") => {
1165 use sha2::{Digest, Sha256};
1166 let data = expect_bytes(args.first())?;
1167 let mut h = Sha256::new();
1168 h.update(data);
1169 Ok(Value::Bytes(h.finalize().to_vec()))
1170 }
1171 ("crypto", "sha512") => {
1172 use sha2::{Digest, Sha512};
1173 let data = expect_bytes(args.first())?;
1174 let mut h = Sha512::new();
1175 h.update(data);
1176 Ok(Value::Bytes(h.finalize().to_vec()))
1177 }
1178 ("crypto", "md5") => {
1179 use md5::{Digest, Md5};
1180 let data = expect_bytes(args.first())?;
1181 let mut h = Md5::new();
1182 h.update(data);
1183 Ok(Value::Bytes(h.finalize().to_vec()))
1184 }
1185 ("crypto", "blake2b") => {
1189 use blake2::{Blake2b512, Digest};
1190 let data = expect_bytes(args.first())?;
1191 let mut h = Blake2b512::new();
1192 h.update(data);
1193 Ok(Value::Bytes(h.finalize().to_vec()))
1194 }
1195 ("crypto", "keccak256") => {
1200 use sha3::{Digest, Keccak256};
1201 let data = expect_bytes(args.first())?;
1202 let mut h = Keccak256::new();
1203 h.update(data);
1204 Ok(Value::Bytes(h.finalize().to_vec()))
1205 }
1206 ("crypto", "sha256_str") => {
1210 use sha2::{Digest, Sha256};
1211 let s = expect_str(args.first())?;
1212 let mut h = Sha256::new();
1213 h.update(s.as_bytes());
1214 Ok(Value::Str(hex::encode(h.finalize()).into()))
1215 }
1216 ("crypto", "sha512_str") => {
1217 use sha2::{Digest, Sha512};
1218 let s = expect_str(args.first())?;
1219 let mut h = Sha512::new();
1220 h.update(s.as_bytes());
1221 Ok(Value::Str(hex::encode(h.finalize()).into()))
1222 }
1223 ("crypto", "hmac_sha256") => {
1224 use hmac::{Hmac, KeyInit, Mac};
1225 type HmacSha256 = Hmac<sha2::Sha256>;
1226 let key = expect_bytes(args.first())?;
1227 let data = expect_bytes(args.get(1))?;
1228 let mut mac = HmacSha256::new_from_slice(key)
1229 .map_err(|e| format!("hmac_sha256 key: {e}"))?;
1230 mac.update(data);
1231 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1232 }
1233 ("crypto", "hmac_sha512") => {
1234 use hmac::{Hmac, KeyInit, Mac};
1235 type HmacSha512 = Hmac<sha2::Sha512>;
1236 let key = expect_bytes(args.first())?;
1237 let data = expect_bytes(args.get(1))?;
1238 let mut mac = HmacSha512::new_from_slice(key)
1239 .map_err(|e| format!("hmac_sha512 key: {e}"))?;
1240 mac.update(data);
1241 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1242 }
1243 ("crypto", "ed25519_public_key") => {
1247 use ed25519_dalek::SigningKey;
1248 let secret = expect_bytes(args.first())?;
1249 let seed: [u8; 32] = match secret.as_slice().try_into() {
1250 Ok(s) => s,
1251 Err(_) => return Ok(err_v(Value::Str("ed25519_public_key: secret must be 32 bytes".into()))),
1252 };
1253 let sk = SigningKey::from_bytes(&seed);
1254 Ok(ok_v(Value::Bytes(sk.verifying_key().to_bytes().to_vec())))
1255 }
1256 ("crypto", "ed25519_sign") => {
1257 use ed25519_dalek::{Signer, SigningKey};
1258 let secret = expect_bytes(args.first())?;
1259 let message = expect_bytes(args.get(1))?;
1260 let seed: [u8; 32] = match secret.as_slice().try_into() {
1261 Ok(s) => s,
1262 Err(_) => return Ok(err_v(Value::Str("ed25519_sign: secret must be 32 bytes".into()))),
1263 };
1264 let sk = SigningKey::from_bytes(&seed);
1265 Ok(ok_v(Value::Bytes(sk.sign(message).to_bytes().to_vec())))
1266 }
1267 ("crypto", "ed25519_verify") => {
1268 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
1269 let public = expect_bytes(args.first())?;
1270 let message = expect_bytes(args.get(1))?;
1271 let sig_bytes = expect_bytes(args.get(2))?;
1272 let pk_arr: [u8; 32] = match public.as_slice().try_into() {
1273 Ok(p) => p,
1274 Err(_) => return Ok(Value::Bool(false)),
1275 };
1276 let sig_arr: [u8; 64] = match sig_bytes.as_slice().try_into() {
1277 Ok(s) => s,
1278 Err(_) => return Ok(Value::Bool(false)),
1279 };
1280 let vk = match VerifyingKey::from_bytes(&pk_arr) {
1281 Ok(v) => v,
1282 Err(_) => return Ok(Value::Bool(false)),
1283 };
1284 let sig = Signature::from_bytes(&sig_arr);
1285 Ok(Value::Bool(vk.verify(message, &sig).is_ok()))
1286 }
1287 ("crypto", "ed25519_is_valid_point") => {
1288 use ed25519_dalek::VerifyingKey;
1289 let candidate = expect_bytes(args.first())?;
1290 let arr: [u8; 32] = match candidate.as_slice().try_into() {
1291 Ok(a) => a,
1292 Err(_) => return Ok(Value::Bool(false)),
1293 };
1294 Ok(Value::Bool(VerifyingKey::from_bytes(&arr).is_ok()))
1295 }
1296 ("crypto", "p256_public_key") => {
1308 use p256::ecdsa::SigningKey;
1309 let secret = expect_bytes(args.first())?;
1310 let sk = match SigningKey::from_slice(secret) {
1311 Ok(k) => k,
1312 Err(_) => return Ok(err_v(Value::Str(
1313 "p256_public_key: secret must be a 32-byte P-256 scalar".into()))),
1314 };
1315 let point = sk.verifying_key().to_encoded_point(true);
1316 Ok(ok_v(Value::Bytes(point.as_bytes().to_vec())))
1317 }
1318 ("crypto", "p256_sign") => {
1319 use p256::ecdsa::{signature::Signer, Signature, SigningKey};
1320 let secret = expect_bytes(args.first())?;
1321 let message = expect_bytes(args.get(1))?;
1322 let sk = match SigningKey::from_slice(secret) {
1323 Ok(k) => k,
1324 Err(_) => return Ok(err_v(Value::Str(
1325 "p256_sign: secret must be a 32-byte P-256 scalar".into()))),
1326 };
1327 let sig: Signature = sk.sign(message);
1328 Ok(ok_v(Value::Bytes(sig.to_der().as_bytes().to_vec())))
1329 }
1330 ("crypto", "p256_verify") => {
1331 use p256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
1332 let public = expect_bytes(args.first())?;
1333 let message = expect_bytes(args.get(1))?;
1334 let sig_bytes = expect_bytes(args.get(2))?;
1335 let vk = match VerifyingKey::from_sec1_bytes(public) {
1336 Ok(v) => v,
1337 Err(_) => return Ok(Value::Bool(false)),
1338 };
1339 let sig = match Signature::from_der(sig_bytes) {
1340 Ok(s) => s,
1341 Err(_) => return Ok(Value::Bool(false)),
1342 };
1343 Ok(Value::Bool(vk.verify(message, &sig).is_ok()))
1344 }
1345 ("crypto", "secp256k1_public_key") => {
1356 use k256::ecdsa::SigningKey;
1357 let secret = expect_bytes(args.first())?;
1358 let sk = match SigningKey::from_slice(secret) {
1359 Ok(k) => k,
1360 Err(_) => return Ok(err_v(Value::Str(
1361 "secp256k1_public_key: secret must be a 32-byte secp256k1 scalar".into()))),
1362 };
1363 let point = sk.verifying_key().to_encoded_point(false);
1366 Ok(ok_v(Value::Bytes(point.as_bytes().to_vec())))
1367 }
1368 ("crypto", "secp256k1_sign_digest") => {
1369 use k256::ecdsa::SigningKey;
1370 let secret = expect_bytes(args.first())?;
1371 let digest = expect_bytes(args.get(1))?;
1372 if digest.len() != 32 {
1373 return Ok(err_v(Value::Str(
1374 "secp256k1_sign_digest: digest must be exactly 32 bytes".into())));
1375 }
1376 let sk = match SigningKey::from_slice(secret) {
1377 Ok(k) => k,
1378 Err(_) => return Ok(err_v(Value::Str(
1379 "secp256k1_sign_digest: secret must be a 32-byte secp256k1 scalar".into()))),
1380 };
1381 match sk.sign_prehash_recoverable(digest) {
1384 Ok((sig, recid)) => {
1385 let mut out = sig.to_bytes().to_vec(); out.push(27u8 + recid.to_byte());
1387 Ok(ok_v(Value::Bytes(out)))
1388 }
1389 Err(e) => Ok(err_v(Value::Str(
1390 format!("secp256k1_sign_digest: {e}").into()))),
1391 }
1392 }
1393 ("crypto", "secp256k1_recover") => {
1394 use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
1395 let digest = expect_bytes(args.first())?;
1396 let sig_bytes = expect_bytes(args.get(1))?;
1397 if digest.len() != 32 {
1398 return Ok(err_v(Value::Str(
1399 "secp256k1_recover: digest must be exactly 32 bytes".into())));
1400 }
1401 if sig_bytes.len() != 65 {
1402 return Ok(err_v(Value::Str(
1403 "secp256k1_recover: signature must be 65 bytes (r‖s‖v)".into())));
1404 }
1405 let sig = match Signature::from_slice(&sig_bytes[..64]) {
1406 Ok(s) => s,
1407 Err(_) => return Ok(err_v(Value::Str(
1408 "secp256k1_recover: malformed r‖s".into()))),
1409 };
1410 let v = sig_bytes[64];
1412 let recid_byte = if v >= 27 { v - 27 } else { v };
1413 let recid = match RecoveryId::from_byte(recid_byte) {
1414 Some(r) => r,
1415 None => return Ok(err_v(Value::Str(
1416 "secp256k1_recover: invalid recovery id".into()))),
1417 };
1418 match VerifyingKey::recover_from_prehash(digest, &sig, recid) {
1419 Ok(vk) => Ok(ok_v(Value::Bytes(
1420 vk.to_encoded_point(false).as_bytes().to_vec()))),
1421 Err(e) => Ok(err_v(Value::Str(
1422 format!("secp256k1_recover: {e}").into()))),
1423 }
1424 }
1425 ("crypto", "secp256k1_verify") => {
1426 use k256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};
1427 let public = expect_bytes(args.first())?;
1428 let digest = expect_bytes(args.get(1))?;
1429 let sig_bytes = expect_bytes(args.get(2))?;
1430 if digest.len() != 32 {
1431 return Ok(Value::Bool(false));
1432 }
1433 let vk = match VerifyingKey::from_sec1_bytes(public) {
1434 Ok(v) => v,
1435 Err(_) => return Ok(Value::Bool(false)),
1436 };
1437 let rs = if sig_bytes.len() == 65 { &sig_bytes[..64] } else { sig_bytes.as_slice() };
1439 let sig = match Signature::from_slice(rs) {
1440 Ok(s) => s,
1441 Err(_) => return Ok(Value::Bool(false)),
1442 };
1443 Ok(Value::Bool(vk.verify_prehash(digest, &sig).is_ok()))
1444 }
1445 ("crypto", "base64_encode") => {
1446 use base64::{Engine, engine::general_purpose::STANDARD};
1447 let data = expect_bytes(args.first())?;
1448 Ok(Value::Str(STANDARD.encode(data).into()))
1449 }
1450 ("crypto", "base64_decode") => {
1451 use base64::{Engine, engine::general_purpose::STANDARD};
1452 let s = expect_str(args.first())?;
1453 match STANDARD.decode(s) {
1454 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1455 Err(e) => Ok(err_v(Value::Str(format!("base64: {e}").into()))),
1456 }
1457 }
1458 ("crypto", "base64url_encode") => {
1462 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
1463 let data = expect_bytes(args.first())?;
1464 Ok(Value::Str(URL_SAFE_NO_PAD.encode(data).into()))
1465 }
1466 ("crypto", "base64url_decode") => {
1467 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
1468 let s = expect_str(args.first())?;
1469 match URL_SAFE_NO_PAD.decode(s) {
1470 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1471 Err(e) => Ok(err_v(Value::Str(format!("base64url: {e}").into()))),
1472 }
1473 }
1474 ("crypto", "hex_encode") => {
1475 let data = expect_bytes(args.first())?;
1476 Ok(Value::Str(hex::encode(data).into()))
1477 }
1478 ("crypto", "hex_decode") => {
1479 let s = expect_str(args.first())?;
1480 match hex::decode(s) {
1481 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1482 Err(e) => Ok(err_v(Value::Str(format!("hex: {e}").into()))),
1483 }
1484 }
1485 ("crypto", "base58_encode") => {
1489 let data = expect_bytes(args.first())?;
1490 Ok(Value::Str(bs58::encode(data).into_string().into()))
1491 }
1492 ("crypto", "base58_decode") => {
1493 let s = expect_str(args.first())?;
1494 match bs58::decode(s).into_vec() {
1495 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1496 Err(e) => Ok(err_v(Value::Str(format!("base58: {e}").into()))),
1497 }
1498 }
1499 ("crypto", "constant_time_eq") | ("crypto", "eq") => {
1500 use subtle::ConstantTimeEq;
1501 let a = expect_bytes(args.first())?;
1502 let b = expect_bytes(args.get(1))?;
1503 let eq = if a.len() == b.len() {
1512 a.ct_eq(b).into()
1513 } else {
1514 false
1515 };
1516 Ok(Value::Bool(eq))
1517 }
1518 ("crypto", "eq_str") => {
1521 use subtle::ConstantTimeEq;
1522 let a = expect_str(args.first())?;
1523 let b = expect_str(args.get(1))?;
1524 let eq = if a.len() == b.len() {
1525 a.as_bytes().ct_eq(b.as_bytes()).into()
1526 } else {
1527 false
1528 };
1529 Ok(Value::Bool(eq))
1530 }
1531
1532 ("crypto", "aes_gcm_seal") => Ok(aes_gcm_seal_impl(args)),
1542 ("crypto", "aes_gcm_open") => Ok(aes_gcm_open_impl(args)),
1543 ("crypto", "chacha20_poly1305_seal") => Ok(chacha20_seal_impl(args)),
1544 ("crypto", "chacha20_poly1305_open") => Ok(chacha20_open_impl(args)),
1545 ("crypto", "pbkdf2_sha256") => Ok(pbkdf2_sha256_impl(args)),
1546 ("crypto", "hkdf_sha256") => Ok(hkdf_sha256_impl(args)),
1547 ("crypto", "argon2id") => Ok(argon2id_impl(args)),
1548
1549 ("random", "seed") => {
1556 let s = args.first().ok_or("random.seed: missing arg")?.as_int();
1557 let mixed = splitmix64(s as u64).0;
1562 Ok(rng_value(mixed))
1563 }
1564 ("random", "int") => {
1565 let state = rng_decode(args.first())?;
1566 let lo = args.get(1).ok_or("random.int: missing lo")?.as_int();
1567 let hi = args.get(2).ok_or("random.int: missing hi")?.as_int();
1568 if hi < lo {
1569 return Err(format!(
1570 "random.int: hi ({hi}) must be >= lo ({lo})"));
1571 }
1572 let span = (hi as i128) - (lo as i128) + 1;
1573 let (raw, next_state) = splitmix64(state);
1574 let drawn = lo as i128 + (raw as u128 % span as u128) as i128;
1579 Ok(Value::Tuple(vec![
1580 Value::Int(drawn as i64),
1581 rng_value(next_state),
1582 ]))
1583 }
1584 ("random", "float") => {
1585 let state = rng_decode(args.first())?;
1586 let (raw, next_state) = splitmix64(state);
1587 let f = ((raw >> 11) as f64) / ((1u64 << 53) as f64);
1590 Ok(Value::Tuple(vec![Value::Float(f), rng_value(next_state)]))
1591 }
1592 ("random", "choose") => {
1593 let state = rng_decode(args.first())?;
1594 let xs = match args.get(1) {
1595 Some(Value::List(xs)) => xs,
1596 _ => return Err("random.choose: expected List".into()),
1597 };
1598 if xs.is_empty() {
1599 return Ok(Value::Variant {
1600 name: "None".into(), args: vec![],
1601 });
1602 }
1603 let (raw, next_state) = splitmix64(state);
1604 let idx = (raw as usize) % xs.len();
1605 let pick = xs[idx].clone();
1606 Ok(Value::Variant {
1607 name: "Some".into(),
1608 args: vec![Value::Tuple(vec![pick, rng_value(next_state)])],
1609 })
1610 }
1611
1612 ("parser", "char") => {
1617 let s = expect_str(args.first())?;
1618 if s.chars().count() != 1 {
1619 return Err(format!(
1620 "parser.char: expected 1-character string, got {s:?}"));
1621 }
1622 Ok(parser_node("Char", &[("ch", Value::Str(s.into()))]))
1623 }
1624 ("parser", "string") => {
1625 let s = expect_str(args.first())?;
1626 Ok(parser_node("String", &[("s", Value::Str(s.into()))]))
1627 }
1628 ("parser", "digit") => Ok(parser_node("Digit", &[])),
1629 ("parser", "alpha") => Ok(parser_node("Alpha", &[])),
1630 ("parser", "whitespace") => Ok(parser_node("Whitespace", &[])),
1631 ("parser", "eof") => Ok(parser_node("Eof", &[])),
1632 ("parser", "seq") => {
1633 let a = args.first().cloned()
1634 .ok_or_else(|| "parser.seq: missing first parser".to_string())?;
1635 let b = args.get(1).cloned()
1636 .ok_or_else(|| "parser.seq: missing second parser".to_string())?;
1637 Ok(parser_node("Seq", &[("a", a), ("b", b)]))
1638 }
1639 ("parser", "alt") => {
1640 let a = args.first().cloned()
1641 .ok_or_else(|| "parser.alt: missing first parser".to_string())?;
1642 let b = args.get(1).cloned()
1643 .ok_or_else(|| "parser.alt: missing second parser".to_string())?;
1644 Ok(parser_node("Alt", &[("a", a), ("b", b)]))
1645 }
1646 ("parser", "many") => {
1647 let p = args.first().cloned()
1648 .ok_or_else(|| "parser.many: missing inner parser".to_string())?;
1649 Ok(parser_node("Many", &[("p", p)]))
1650 }
1651 ("parser", "optional") => {
1652 let p = args.first().cloned()
1653 .ok_or_else(|| "parser.optional: missing inner parser".to_string())?;
1654 Ok(parser_node("Optional", &[("p", p)]))
1655 }
1656 ("parser", "map") => {
1660 let p = args.first().cloned()
1661 .ok_or_else(|| "parser.map: missing parser".to_string())?;
1662 let f = args.get(1).cloned()
1663 .ok_or_else(|| "parser.map: missing closure".to_string())?;
1664 Ok(parser_node("Map", &[("p", p), ("f", f)]))
1665 }
1666 ("parser", "and_then") => {
1667 let p = args.first().cloned()
1668 .ok_or_else(|| "parser.and_then: missing parser".to_string())?;
1669 let f = args.get(1).cloned()
1670 .ok_or_else(|| "parser.and_then: missing closure".to_string())?;
1671 Ok(parser_node("AndThen", &[("p", p), ("f", f)]))
1672 }
1673 ("regex", "compile") => {
1683 let pat = expect_str(args.first())?;
1684 match get_or_compile_regex(&pat) {
1685 Ok(_) => Ok(ok_v(Value::Str(pat.into()))),
1686 Err(e) => Ok(err_v(Value::Str(e.into()))),
1687 }
1688 }
1689 ("regex", "is_match") => {
1690 let pat = expect_str(args.first())?;
1691 let s = expect_str(args.get(1))?;
1692 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.is_match: {e}"))?;
1693 Ok(Value::Bool(re.is_match(&s)))
1694 }
1695 ("regex", "is_match_str") => {
1700 let pat = expect_str(args.first())?;
1701 let s = expect_str(args.get(1))?;
1702 match get_or_compile_regex(&pat) {
1703 Ok(re) => Ok(Value::Bool(re.is_match(&s))),
1704 Err(_) => Ok(Value::Bool(false)),
1705 }
1706 }
1707 ("regex", "find") => {
1708 let pat = expect_str(args.first())?;
1709 let s = expect_str(args.get(1))?;
1710 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find: {e}"))?;
1711 match re.captures(&s) {
1712 Some(caps) => Ok(Value::Variant {
1713 name: "Some".into(),
1714 args: vec![match_value(&caps)],
1715 }),
1716 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1717 }
1718 }
1719 ("regex", "find_all") => {
1720 let pat = expect_str(args.first())?;
1721 let s = expect_str(args.get(1))?;
1722 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find_all: {e}"))?;
1723 let items: std::collections::VecDeque<Value> = re.captures_iter(&s).map(|caps| match_value(&caps)).collect();
1724 Ok(Value::List(items))
1725 }
1726 ("regex", "replace") => {
1727 let pat = expect_str(args.first())?;
1728 let s = expect_str(args.get(1))?;
1729 let rep = expect_str(args.get(2))?;
1730 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace: {e}"))?;
1731 Ok(Value::Str(re.replace(&s, rep.as_str()).into_owned().into()))
1732 }
1733 ("regex", "replace_all") => {
1734 let pat = expect_str(args.first())?;
1735 let s = expect_str(args.get(1))?;
1736 let rep = expect_str(args.get(2))?;
1737 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace_all: {e}"))?;
1738 Ok(Value::Str(re.replace_all(&s, rep.as_str()).into_owned().into()))
1739 }
1740 ("datetime", "parse_iso") => {
1743 let s = expect_str(args.first())?;
1744 match chrono::DateTime::parse_from_rfc3339(&s) {
1745 Ok(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1746 Err(e) => Ok(err_v(Value::Str(format!("parse_iso: {e}").into()))),
1747 }
1748 }
1749 ("datetime", "format_iso") => {
1750 let n = expect_int(args.first())?;
1751 Ok(Value::Str(format_iso(n).into()))
1752 }
1753 ("datetime", "parse") => {
1754 let s = expect_str(args.first())?;
1755 let fmt = expect_str(args.get(1))?;
1756 match chrono::NaiveDateTime::parse_from_str(&s, &fmt) {
1757 Ok(naive) => {
1758 use chrono::TimeZone;
1759 match chrono::Utc.from_local_datetime(&naive).single() {
1760 Some(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1761 None => Ok(err_v(Value::Str("parse: ambiguous local time".into()))),
1762 }
1763 }
1764 Err(e) => Ok(err_v(Value::Str(format!("parse: {e}").into()))),
1765 }
1766 }
1767 ("datetime", "format") => {
1768 let n = expect_int(args.first())?;
1769 let fmt = expect_str(args.get(1))?;
1770 let dt = chrono_from_instant(n);
1771 Ok(Value::Str(dt.format(&fmt).to_string().into()))
1772 }
1773 ("datetime", "to_components") => {
1774 let n = expect_int(args.first())?;
1775 let tz = match parse_tz_arg(args.get(1)) {
1776 Ok(t) => t,
1777 Err(e) => return Ok(err_v(Value::Str(e.into()))),
1778 };
1779 match resolve_tz_to_components(n, &tz) {
1780 Ok(rec) => Ok(ok_v(rec)),
1781 Err(e) => Ok(err_v(Value::Str(e.into()))),
1782 }
1783 }
1784 ("datetime", "from_components") => {
1785 let rec = match args.first() {
1786 Some(Value::Record { fields: r, .. }) => r.clone(),
1787 _ => return Err("from_components: expected DateTime record".into()),
1788 };
1789 match instant_from_components(&rec) {
1790 Ok(n) => Ok(ok_v(Value::Int(n))),
1791 Err(e) => Ok(err_v(Value::Str(e.into()))),
1792 }
1793 }
1794 ("datetime", "add") => {
1795 let a = expect_int(args.first())?;
1796 let d = expect_int(args.get(1))?;
1797 Ok(Value::Int(a.saturating_add(d)))
1798 }
1799 ("datetime", "diff") => {
1800 let a = expect_int(args.first())?;
1801 let b = expect_int(args.get(1))?;
1802 Ok(Value::Int(a.saturating_sub(b)))
1803 }
1804 ("datetime", "duration_seconds") => {
1805 let s = expect_float(args.first())?;
1806 let nanos = (s * 1_000_000_000.0) as i64;
1807 Ok(Value::Int(nanos))
1808 }
1809 ("datetime", "duration_minutes") => {
1810 let m = expect_int(args.first())?;
1811 Ok(Value::Int(m.saturating_mul(60_000_000_000)))
1812 }
1813 ("datetime", "duration_days") => {
1814 let d = expect_int(args.first())?;
1815 Ok(Value::Int(d.saturating_mul(86_400_000_000_000)))
1816 }
1817 ("datetime", "before") => {
1819 let a = expect_int(args.first())?;
1820 let b = expect_int(args.get(1))?;
1821 Ok(Value::Bool(a < b))
1822 }
1823 ("datetime", "after") => {
1824 let a = expect_int(args.first())?;
1825 let b = expect_int(args.get(1))?;
1826 Ok(Value::Bool(a > b))
1827 }
1828 ("datetime", "compare") => {
1829 let a = expect_int(args.first())?;
1830 let b = expect_int(args.get(1))?;
1831 Ok(Value::Int(a.cmp(&b) as i64))
1832 }
1833 ("duration", "millis") => Ok(Value::Int(expect_int(args.first())? / 1_000_000)),
1836 ("duration", "seconds") => Ok(Value::Int(expect_int(args.first())? / 1_000_000_000)),
1837 ("duration", "minutes") => Ok(Value::Int(expect_int(args.first())? / 60_000_000_000)),
1838 ("duration", "hours") => Ok(Value::Int(expect_int(args.first())? / 3_600_000_000_000)),
1839 ("duration", "days") => Ok(Value::Int(expect_int(args.first())? / 86_400_000_000_000)),
1840
1841 ("regex", "split") => {
1842 let pat = expect_str(args.first())?;
1843 let s = expect_str(args.get(1))?;
1844 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.split: {e}"))?;
1845 let parts: std::collections::VecDeque<Value> = re.split(&s).map(|p| Value::Str(p.into())).collect();
1846 Ok(Value::List(parts))
1847 }
1848
1849 ("http", "with_header") => {
1852 let req = expect_record_pure(args.first())?.clone();
1853 let k = expect_str(args.get(1))?;
1854 let v = expect_str(args.get(2))?;
1855 Ok(Value::record_interned(http_set_header(req, &k, &v)))
1856 }
1857 ("http", "with_auth") => {
1858 let req = expect_record_pure(args.first())?.clone();
1859 let scheme = expect_str(args.get(1))?;
1860 let token = expect_str(args.get(2))?;
1861 let value = format!("{scheme} {token}");
1862 Ok(Value::record_interned(http_set_header(req, "Authorization", &value)))
1863 }
1864 ("http", "with_query") => {
1865 let req = expect_record_pure(args.first())?.clone();
1866 let params = match args.get(1) {
1867 Some(Value::Map(m)) => m.clone(),
1868 Some(other) => return Err(format!(
1869 "http.with_query: params must be Map[Str, Str], got {other:?}")),
1870 None => return Err("http.with_query: missing params argument".into()),
1871 };
1872 Ok(Value::record_interned(http_append_query(req, ¶ms)))
1873 }
1874 ("http", "with_timeout_ms") => {
1875 let req = expect_record_pure(args.first())?.clone();
1876 let ms = expect_int(args.get(1))?;
1877 let mut out = req;
1878 out.insert("timeout_ms".into(), Value::Variant {
1879 name: "Some".into(),
1880 args: vec![Value::Int(ms)],
1881 });
1882 Ok(Value::record_interned(out))
1883 }
1884 ("http", "json_body") => {
1885 let resp = expect_record_pure(args.first())?;
1886 let body = match resp.get("body") {
1887 Some(Value::Bytes(b)) => b.clone(),
1888 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1889 };
1890 let s = match std::str::from_utf8(&body) {
1891 Ok(s) => s,
1892 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1893 };
1894 match serde_json::from_str::<serde_json::Value>(s) {
1895 Ok(j) => Ok(ok_v(Value::from_json(&j))),
1896 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1897 }
1898 }
1899 ("http", "json_body_typed") => {
1907 let resp = expect_record_pure(args.first())?;
1908 let required = required_field_names(args.get(1))?;
1909 let schema = extract_type_schema(args.get(2));
1910 let body = match resp.get("body") {
1911 Some(Value::Bytes(b)) => b.clone(),
1912 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1913 };
1914 let s = match std::str::from_utf8(&body) {
1915 Ok(s) => s,
1916 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1917 };
1918 match serde_json::from_str::<serde_json::Value>(s) {
1919 Ok(j) => {
1920 if let Err(e) = check_required_fields(&j, &required) {
1921 return Ok(http_decode_err_pure(e));
1922 }
1923 if let Err(e) = validate_field_types(&j, &schema) {
1924 return Ok(http_decode_err_pure(e));
1925 }
1926 Ok(ok_v(apply_option_wrapping(json_to_value(&j), &j, &schema)))
1927 }
1928 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1929 }
1930 }
1931 ("http", "text_body") => {
1932 let resp = expect_record_pure(args.first())?;
1933 let body = match resp.get("body") {
1934 Some(Value::Bytes(b)) => b.clone(),
1935 _ => return Err("http.text_body: HttpResponse.body must be Bytes".into()),
1936 };
1937 match String::from_utf8(body) {
1938 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
1939 Err(e) => Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1940 }
1941 }
1942
1943 ("cli", "flag") => {
1947 let name = expect_str(args.first())?;
1948 let short = opt_str(args.get(1));
1949 let help = expect_str(args.get(2))?;
1950 Ok(value_from_json(crate::cli::flag_spec(&name, short.as_deref(), &help)))
1951 }
1952 ("cli", "option") => {
1953 let name = expect_str(args.first())?;
1954 let short = opt_str(args.get(1));
1955 let help = expect_str(args.get(2))?;
1956 let default = opt_str(args.get(3));
1957 Ok(value_from_json(crate::cli::option_spec(&name, short.as_deref(), &help, default.as_deref())))
1958 }
1959 ("cli", "positional") => {
1960 let name = expect_str(args.first())?;
1961 let help = expect_str(args.get(1))?;
1962 let required = expect_bool(args.get(2))?;
1963 Ok(value_from_json(crate::cli::positional_spec(&name, &help, required)))
1964 }
1965 ("cli", "spec") => {
1966 let name = expect_str(args.first())?;
1967 let help = expect_str(args.get(1))?;
1968 let arg_specs: Vec<serde_json::Value> = expect_list(args.get(2))?
1969 .iter().map(value_to_json).collect();
1970 let subs: Vec<serde_json::Value> = expect_list(args.get(3))?
1971 .iter().map(value_to_json).collect();
1972 Ok(value_from_json(crate::cli::build_spec(&name, &help, arg_specs, subs)))
1973 }
1974 ("cli", "parse") => {
1975 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1976 let argv: Vec<String> = expect_list(args.get(1))?
1977 .iter().map(|v| match v {
1978 Value::Str(s) => Ok(s.to_string()),
1979 other => Err(format!("cli.parse: argv must be List[Str], got {other:?}")),
1980 }).collect::<Result<_, _>>()?;
1981 match crate::cli::parse(&spec, &argv) {
1982 Ok(parsed) => Ok(ok_v(value_from_json(parsed))),
1983 Err(msg) => Ok(err_v(Value::Str(msg.into()))),
1984 }
1985 }
1986 ("cli", "envelope") => {
1987 let ok = expect_bool(args.first())?;
1988 let cmd = expect_str(args.get(1))?;
1989 let data = value_to_json(args.get(2).unwrap_or(&Value::Unit));
1990 Ok(value_from_json(crate::cli::envelope(ok, &cmd, data)))
1991 }
1992 ("cli", "describe") => {
1993 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1994 Ok(value_from_json(crate::cli::describe(&spec)))
1995 }
1996 ("cli", "help") => {
1997 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1998 Ok(Value::Str(crate::cli::help_text(&spec).into()))
1999 }
2000
2001 ("arrow", op) => match crate::arrow::dispatch(op, args) {
2003 Some(r) => r,
2004 None => Err(format!("unknown pure builtin: arrow.{op}")),
2005 },
2006 #[cfg(feature = "df")]
2010 ("df", op) => match crate::df::dispatch(op, args) {
2011 Some(r) => r,
2012 None => Err(format!("unknown pure builtin: df.{op}")),
2013 },
2014 #[cfg(not(feature = "df"))]
2015 ("df", op) => Err(format!(
2016 "df.{op}: this build was compiled without the `df` feature; \
2017 Polars-backed dataframe query ops are unavailable"
2018 )),
2019
2020 ("decimal", "decimal") => {
2027 let coef = expect_int(args.first())?;
2028 let exp = expect_int(args.get(1))?;
2029 Ok(make_decimal(coef, exp))
2030 }
2031 ("decimal", "zero") => Ok(make_decimal(0, 0)),
2032 ("decimal", "one") => Ok(make_decimal(1, 0)),
2033 ("decimal", "from_int") => {
2034 Ok(make_decimal(expect_int(args.first())?, 0))
2035 }
2036 ("decimal", "pow10") => {
2037 Ok(Value::Int(decimal_pow10(expect_int(args.first())?)?))
2038 }
2039 ("decimal", "add") => {
2040 let (ca, ea) = expect_decimal(args.first())?;
2041 let (cb, eb) = expect_decimal(args.get(1))?;
2042 let (a2, b2, e) = decimal_align(ca, ea, cb, eb)?;
2043 Ok(make_decimal(
2044 a2.checked_add(b2).ok_or("decimal.add: overflow")?, e))
2045 }
2046 ("decimal", "sub") => {
2047 let (ca, ea) = expect_decimal(args.first())?;
2048 let (cb, eb) = expect_decimal(args.get(1))?;
2049 let (a2, b2, e) = decimal_align(ca, ea, cb, eb)?;
2050 Ok(make_decimal(
2051 a2.checked_sub(b2).ok_or("decimal.sub: overflow")?, e))
2052 }
2053 ("decimal", "mul") => {
2054 let (ca, ea) = expect_decimal(args.first())?;
2055 let (cb, eb) = expect_decimal(args.get(1))?;
2056 Ok(make_decimal(
2057 ca.checked_mul(cb).ok_or("decimal.mul: overflow")?,
2058 ea.checked_add(eb).ok_or("decimal.mul: exponent overflow")?,
2059 ))
2060 }
2061 ("decimal", "compare") => {
2062 let (ca, ea) = expect_decimal(args.first())?;
2063 let (cb, eb) = expect_decimal(args.get(1))?;
2064 let (a2, b2, _) = decimal_align(ca, ea, cb, eb)?;
2065 Ok(Value::Int(if a2 < b2 { -1 } else if a2 > b2 { 1 } else { 0 }))
2066 }
2067 ("decimal", "is_zero") => {
2068 let (c, _) = expect_decimal(args.first())?;
2069 Ok(Value::Bool(c == 0))
2070 }
2071 ("decimal", "is_positive") => {
2072 let (c, _) = expect_decimal(args.first())?;
2073 Ok(Value::Bool(c > 0))
2074 }
2075 ("decimal", "is_negative") => {
2076 let (c, _) = expect_decimal(args.first())?;
2077 Ok(Value::Bool(c < 0))
2078 }
2079 ("decimal", "negate") => {
2080 let (c, e) = expect_decimal(args.first())?;
2081 Ok(make_decimal(-c, e))
2082 }
2083 ("decimal", "abs") => {
2084 let (c, e) = expect_decimal(args.first())?;
2085 Ok(make_decimal(c.abs(), e))
2086 }
2087 ("decimal", "normalize") => {
2088 let (mut c, mut e) = expect_decimal(args.first())?;
2089 if c == 0 { return Ok(make_decimal(0, 0)); }
2090 while c % 10 == 0 { c /= 10; e += 1; }
2091 Ok(make_decimal(c, e))
2092 }
2093 ("decimal", "round_to") => {
2094 let (c, e) = expect_decimal(args.first())?;
2095 let target_e = expect_int(args.get(1))?;
2096 let mode = expect_str(args.get(2))?;
2097 Ok(make_decimal(decimal_round(c, e, target_e, &mode)?, target_e))
2098 }
2099 ("decimal", "to_str") => {
2100 let (c, e) = expect_decimal(args.first())?;
2101 Ok(Value::Str(decimal_to_str(c, e)?.into()))
2102 }
2103
2104 _ => Err(format!("unknown pure builtin: {kind}.{op}")),
2105 }
2106}
2107
2108fn expect_decimal(v: Option<&Value>) -> Result<(i64, i64), String> {
2112 match v {
2113 Some(Value::Record { fields, .. }) => {
2114 let coef = match fields.get("coefficient") {
2115 Some(Value::Int(n)) => *n,
2116 _ => return Err("decimal: missing or invalid 'coefficient' field".into()),
2117 };
2118 let exp = match fields.get("exponent") {
2119 Some(Value::Int(n)) => *n,
2120 _ => return Err("decimal: missing or invalid 'exponent' field".into()),
2121 };
2122 Ok((coef, exp))
2123 }
2124 Some(other) => Err(format!("decimal: expected {{ coefficient, exponent }} record, got {other:?}")),
2125 None => Err("decimal: missing argument".into()),
2126 }
2127}
2128
2129fn make_decimal(coefficient: i64, exponent: i64) -> Value {
2131 let mut fields = indexmap::IndexMap::new();
2132 fields.insert("coefficient".into(), Value::Int(coefficient));
2133 fields.insert("exponent".into(), Value::Int(exponent));
2134 Value::record_interned(fields)
2135}
2136
2137fn decimal_pow10(n: i64) -> Result<i64, String> {
2139 if n < 0 { return Err(format!("decimal.pow10: negative exponent {n}")); }
2140 if n > 18 { return Err(format!("decimal.pow10: exponent {n} exceeds max (18)")); }
2141 Ok(10i64.pow(n as u32))
2142}
2143
2144fn decimal_align(ca: i64, ea: i64, cb: i64, eb: i64) -> Result<(i64, i64, i64), String> {
2147 if ea == eb { return Ok((ca, cb, ea)); }
2148 if ea > eb {
2149 let scale = decimal_pow10(ea - eb)?;
2150 let ca2 = ca.checked_mul(scale)
2151 .ok_or_else(|| format!("decimal: overflow aligning (shift {})", ea - eb))?;
2152 Ok((ca2, cb, eb))
2153 } else {
2154 let scale = decimal_pow10(eb - ea)?;
2155 let cb2 = cb.checked_mul(scale)
2156 .ok_or_else(|| format!("decimal: overflow aligning (shift {})", eb - ea))?;
2157 Ok((ca, cb2, ea))
2158 }
2159}
2160
2161fn decimal_round(c: i64, e: i64, target_e: i64, mode: &str) -> Result<i64, String> {
2166 if e >= target_e {
2167 let shift = e - target_e;
2169 let scale = decimal_pow10(shift)?;
2170 return c.checked_mul(scale)
2171 .ok_or_else(|| format!("decimal.round_to: overflow scaling (shift {shift})"));
2172 }
2173 let shift = target_e - e;
2175 let divisor = decimal_pow10(shift)?;
2176 let q = c / divisor;
2177 let r = c % divisor; if r == 0 { return Ok(q); }
2180
2181 let abs_r = r.abs();
2182 let positive = r > 0; let rounded = match mode {
2185 "Down" => q,
2186 "Up" => if positive { q + 1 } else { q - 1 },
2187 "Floor" => if positive { q } else { q - 1 },
2188 "Ceiling" => if positive { q + 1 } else { q },
2189 "HalfUp" => {
2190 if abs_r * 2 >= divisor {
2191 if positive { q + 1 } else { q - 1 }
2192 } else { q }
2193 }
2194 "HalfDown" => {
2195 if abs_r * 2 > divisor {
2196 if positive { q + 1 } else { q - 1 }
2197 } else { q }
2198 }
2199 "HalfEven" => {
2200 if abs_r * 2 > divisor {
2201 if positive { q + 1 } else { q - 1 }
2202 } else if abs_r * 2 == divisor {
2203 if q % 2 == 0 { q } else { if positive { q + 1 } else { q - 1 } }
2205 } else { q }
2206 }
2207 other => return Err(format!(
2208 "decimal.round_to: unknown rounding mode {other:?}; \
2209 valid modes: HalfUp HalfDown HalfEven Down Up Ceiling Floor")),
2210 };
2211 Ok(rounded)
2212}
2213
2214fn decimal_to_str(c: i64, e: i64) -> Result<String, String> {
2217 if e == 0 { return Ok(c.to_string()); }
2218 if e > 0 {
2219 let scale = decimal_pow10(e)?;
2220 let val = c.checked_mul(scale)
2221 .ok_or("decimal.to_str: overflow")?;
2222 return Ok(val.to_string());
2223 }
2224 let scale = decimal_pow10(-e)?;
2226 let sign = if c < 0 { "-" } else { "" };
2227 let abs_c = c.abs();
2228 let int_part = abs_c / scale;
2229 let frac_part = abs_c % scale;
2230 let decimal_places = (-e) as usize;
2231 Ok(format!("{sign}{int_part}.{frac_part:0>decimal_places$}"))
2232}
2233
2234fn opt_str(arg: Option<&Value>) -> Option<String> {
2238 match arg {
2239 Some(Value::Variant { name, args }) if name == "Some" => {
2240 args.first().and_then(|v| match v {
2241 Value::Str(s) => Some(s.to_string()),
2242 _ => None,
2243 })
2244 }
2245 _ => None,
2246 }
2247}
2248
2249fn value_from_json(v: serde_json::Value) -> Value { Value::from_json(&v) }
2250
2251fn regex_cache() -> &'static Mutex<HashMap<String, regex::Regex>> {
2256 static CACHE: OnceLock<Mutex<HashMap<String, regex::Regex>>> = OnceLock::new();
2257 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
2258}
2259
2260fn get_or_compile_regex(pattern: &str) -> Result<regex::Regex, String> {
2261 let cache = regex_cache();
2262 {
2263 let guard = cache.lock().unwrap();
2264 if let Some(re) = guard.get(pattern) {
2265 return Ok(re.clone());
2266 }
2267 }
2268 let re = regex::Regex::new(pattern).map_err(|e| format!("invalid regex: {e}"))?;
2269 let mut guard = cache.lock().unwrap();
2270 guard.insert(pattern.to_string(), re.clone());
2271 Ok(re)
2272}
2273
2274fn match_value(caps: ®ex::Captures) -> Value {
2278 let m0 = caps.get(0).expect("regex match always has group 0");
2279 let mut rec = indexmap::IndexMap::new();
2280 rec.insert("text".into(), Value::Str(m0.as_str().into()));
2281 rec.insert("start".into(), Value::Int(m0.start() as i64));
2282 rec.insert("end".into(), Value::Int(m0.end() as i64));
2283 let groups: std::collections::VecDeque<Value> = (1..caps.len())
2284 .map(|i| {
2285 Value::Str(
2286 caps.get(i)
2287 .map(|m| m.as_str())
2288 .unwrap_or_default()
2289 .into(),
2290 )
2291 })
2292 .collect();
2293 rec.insert("groups".into(), Value::List(groups));
2294 Value::record_dynamic(rec)
2295}
2296
2297fn expect_map(v: Option<&Value>) -> Result<&BTreeMap<MapKey, Value>, String> {
2298 match v {
2299 Some(Value::Map(m)) => Ok(m),
2300 other => Err(format!("expected Map, got {other:?}")),
2301 }
2302}
2303
2304fn expect_set(v: Option<&Value>) -> Result<&BTreeSet<MapKey>, String> {
2305 match v {
2306 Some(Value::Set(s)) => Ok(s),
2307 other => Err(format!("expected Set, got {other:?}")),
2308 }
2309}
2310
2311fn unpack_matrix(v: &Value) -> Result<(usize, usize, Vec<f64>), String> {
2315 if let Value::F64Array { rows, cols, data } = v {
2316 return Ok((*rows as usize, *cols as usize, data.clone()));
2317 }
2318 let rec = match v {
2319 Value::Record { fields: r, .. } => r,
2320 other => return Err(format!("expected matrix, got {other:?}")),
2321 };
2322 let rows = match rec.get("rows") {
2323 Some(Value::Int(n)) => *n as usize,
2324 _ => return Err("matrix: missing/invalid `rows`".into()),
2325 };
2326 let cols = match rec.get("cols") {
2327 Some(Value::Int(n)) => *n as usize,
2328 _ => return Err("matrix: missing/invalid `cols`".into()),
2329 };
2330 let data = match rec.get("data") {
2331 Some(Value::List(items)) => {
2332 let mut out = Vec::with_capacity(items.len());
2333 for it in items {
2334 out.push(match it {
2335 Value::Float(f) => *f,
2336 Value::Int(n) => *n as f64,
2337 other => return Err(format!("matrix data: not numeric, got {other:?}")),
2338 });
2339 }
2340 out
2341 }
2342 _ => return Err("matrix: missing/invalid `data`".into()),
2343 };
2344 if data.len() != rows * cols {
2345 return Err(format!("matrix: data len {} != {rows}*{cols}", data.len()));
2346 }
2347 Ok((rows, cols, data))
2348}
2349
2350fn expect_bytes(v: Option<&Value>) -> Result<&Vec<u8>, String> {
2351 match v {
2352 Some(Value::Bytes(b)) => Ok(b),
2353 Some(other) => Err(format!("expected Bytes, got {other:?}")),
2354 None => Err("missing argument".into()),
2355 }
2356}
2357
2358fn first_arg(args: &[Value]) -> Result<&Value, String> {
2359 args.first().ok_or_else(|| "missing argument".into())
2360}
2361
2362fn tuple_index(v: &Value, i: usize) -> Result<Value, String> {
2363 match v {
2364 Value::Tuple(items) => items.get(i).cloned()
2365 .ok_or_else(|| format!("tuple index {i} out of range (len={})", items.len())),
2366 other => Err(format!("expected Tuple, got {other:?}")),
2367 }
2368}
2369
2370fn expect_str(v: Option<&Value>) -> Result<String, String> {
2371 match v {
2372 Some(Value::Str(s)) => Ok(s.to_string()),
2373 Some(other) => Err(format!("expected Str, got {other:?}")),
2374 None => Err("missing argument".into()),
2375 }
2376}
2377
2378fn expect_int(v: Option<&Value>) -> Result<i64, String> {
2379 match v {
2380 Some(Value::Int(n)) => Ok(*n),
2381 Some(other) => Err(format!("expected Int, got {other:?}")),
2382 None => Err("missing argument".into()),
2383 }
2384}
2385
2386fn expect_float(v: Option<&Value>) -> Result<f64, String> {
2387 match v {
2388 Some(Value::Float(f)) => Ok(*f),
2389 Some(other) => Err(format!("expected Float, got {other:?}")),
2390 None => Err("missing argument".into()),
2391 }
2392}
2393
2394fn expect_list(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
2395 match v {
2396 Some(Value::List(xs)) => Ok(xs),
2397 Some(other) => Err(format!("expected List, got {other:?}")),
2398 None => Err("missing argument".into()),
2399 }
2400}
2401
2402fn expect_bool(v: Option<&Value>) -> Result<bool, String> {
2403 match v {
2404 Some(Value::Bool(b)) => Ok(*b),
2405 Some(other) => Err(format!("expected Bool, got {other:?}")),
2406 None => Err("missing argument".into()),
2407 }
2408}
2409
2410fn expect_deque(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
2411 match v {
2412 Some(Value::Deque(d)) => Ok(d),
2413 Some(other) => Err(format!("expected Deque, got {other:?}")),
2414 None => Err("missing argument".into()),
2415 }
2416}
2417
2418fn some(v: Value) -> Value { Value::Variant { name: "Some".into(), args: vec![v] } }
2419fn none() -> Value { Value::Variant { name: "None".into(), args: Vec::new() } }
2420fn ok_v(v: Value) -> Value { Value::Variant { name: "Ok".into(), args: vec![v] } }
2421fn err_v(v: Value) -> Value { Value::Variant { name: "Err".into(), args: vec![v] } }
2422
2423fn parser_node(kind: &str, fields: &[(&str, Value)]) -> Value {
2431 let mut r = indexmap::IndexMap::new();
2432 r.insert("kind".into(), Value::Str(kind.into()));
2433 for (k, v) in fields {
2434 r.insert((*k).into(), v.clone());
2435 }
2436 Value::record_dynamic(r)
2437}
2438
2439fn splitmix64(state: u64) -> (u64, u64) {
2450 let next = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
2451 let mut z = next;
2452 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2453 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
2454 let z = z ^ (z >> 31);
2455 (z, next)
2456}
2457
2458fn rng_value(state: u64) -> Value {
2462 let mut fields = indexmap::IndexMap::new();
2463 fields.insert("state".into(), Value::Int(state as i64));
2464 Value::record_dynamic(fields)
2465}
2466
2467fn rng_decode(v: Option<&Value>) -> Result<u64, String> {
2469 let rec = match v {
2470 Some(Value::Record { fields: r, .. }) => r,
2471 Some(other) => return Err(format!("expected Rng, got {other:?}")),
2472 None => return Err("missing Rng arg".into()),
2473 };
2474 match rec.get("state") {
2475 Some(Value::Int(n)) => Ok(*n as u64),
2476 _ => Err("malformed Rng: missing `state :: Int`".into()),
2477 }
2478}
2479
2480fn expect_record_pure(v: Option<&Value>) -> Result<&indexmap::IndexMap<smol_str::SmolStr, Value>, String> {
2483 match v {
2484 Some(Value::Record { fields: r, .. }) => Ok(r),
2485 Some(other) => Err(format!("expected Record, got {other:?}")),
2486 None => Err("missing Record argument".into()),
2487 }
2488}
2489
2490fn http_decode_err_pure(msg: String) -> Value {
2491 let inner = Value::Variant {
2492 name: "DecodeError".into(),
2493 args: vec![Value::Str(msg.into())],
2494 };
2495 err_v(inner)
2496}
2497
2498fn http_set_header(
2503 mut req: indexmap::IndexMap<smol_str::SmolStr, Value>,
2504 name: &str,
2505 value: &str,
2506) -> indexmap::IndexMap<smol_str::SmolStr, Value> {
2507 use lex_bytecode::MapKey;
2508 let mut headers = match req.shift_remove("headers") {
2509 Some(Value::Map(m)) => m,
2510 _ => std::collections::BTreeMap::new(),
2511 };
2512 let key = MapKey::Str(name.to_lowercase());
2513 let lowered = name.to_lowercase();
2516 headers.retain(|k, _| match k {
2517 MapKey::Str(s) => s.to_lowercase() != lowered,
2518 _ => true,
2519 });
2520 headers.insert(key, Value::Str(value.into()));
2521 req.insert("headers".into(), Value::Map(headers));
2522 req
2523}
2524
2525fn http_append_query(
2531 mut req: indexmap::IndexMap<smol_str::SmolStr, Value>,
2532 params: &std::collections::BTreeMap<lex_bytecode::MapKey, Value>,
2533) -> indexmap::IndexMap<smol_str::SmolStr, Value> {
2534 use lex_bytecode::MapKey;
2535 let url = match req.get("url") {
2536 Some(Value::Str(s)) => s.clone(),
2537 _ => return req,
2538 };
2539 let mut pieces = Vec::new();
2540 for (k, v) in params {
2541 let kk = match k { MapKey::Str(s) => s.to_string(), _ => continue };
2542 let vv = match v { Value::Str(s) => s.to_string(), _ => continue };
2543 pieces.push(format!("{}={}", url_encode(&kk), url_encode(&vv)));
2544 }
2545 if pieces.is_empty() { return req; }
2546 let sep = if url.contains('?') { '&' } else { '?' };
2547 let new_url = format!("{url}{sep}{}", pieces.join("&"));
2548 req.insert("url".into(), Value::Str(new_url.into()));
2549 req
2550}
2551
2552fn url_encode(s: &str) -> String {
2557 let mut out = String::with_capacity(s.len());
2558 for b in s.bytes() {
2559 match b {
2560 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
2561 out.push(b as char);
2562 }
2563 _ => out.push_str(&format!("%{:02X}", b)),
2564 }
2565 }
2566 out
2567}
2568
2569fn value_to_json(v: &Value) -> serde_json::Value { v.to_json() }
2570
2571fn unwrap_toml_datetime_markers(v: &mut serde_json::Value) {
2579 use serde_json::Value as J;
2580 match v {
2581 J::Object(map) => {
2582 if map.len() == 1 {
2586 if let Some(J::String(s)) = map.get("$__toml_private_datetime") {
2587 let s = s.clone();
2588 *v = J::String(s);
2589 return;
2590 }
2591 }
2592 for (_, child) in map.iter_mut() {
2593 unwrap_toml_datetime_markers(child);
2594 }
2595 }
2596 J::Array(items) => {
2597 for item in items.iter_mut() {
2598 unwrap_toml_datetime_markers(item);
2599 }
2600 }
2601 _ => {}
2602 }
2603}
2604
2605fn json_to_value(v: &serde_json::Value) -> Value { Value::from_json(v) }
2606
2607fn required_field_names(arg: Option<&Value>) -> Result<Vec<String>, String> {
2612 let list = expect_list(arg)?;
2613 let mut out = Vec::with_capacity(list.len());
2614 for v in list {
2615 match v {
2616 Value::Str(s) => out.push(s.to_string()),
2617 other => return Err(format!(
2618 "parse_strict: required-fields list must contain Str, got {other:?}"
2619 )),
2620 }
2621 }
2622 Ok(out)
2623}
2624
2625fn check_required_fields(
2646 value: &serde_json::Value,
2647 required: &[String],
2648) -> Result<(), String> {
2649 if required.is_empty() {
2650 return Ok(());
2651 }
2652 if !matches!(value, serde_json::Value::Object(_)) {
2653 return Err(format!(
2654 "parse_strict: expected top-level object with fields {:?}, got {value}",
2655 required
2656 ));
2657 }
2658 let mut missing: Vec<String> = Vec::new();
2659 for path in required {
2660 if !path_exists(value, path) {
2661 missing.push(path.clone());
2662 }
2663 }
2664 if missing.is_empty() {
2665 Ok(())
2666 } else {
2667 Err(format!("missing required field(s): {}", missing.join(", ")))
2668 }
2669}
2670
2671fn path_exists(value: &serde_json::Value, path: &str) -> bool {
2676 let mut cursor = value;
2677 let segments = split_dotted_path(path);
2678 for seg in &segments {
2679 match cursor {
2680 serde_json::Value::Object(o) => match o.get(seg.as_str()) {
2681 Some(next) => cursor = next,
2682 None => return false,
2683 },
2684 _ => return false,
2685 }
2686 }
2687 true
2688}
2689
2690fn split_dotted_path(path: &str) -> Vec<String> {
2694 let mut out: Vec<String> = Vec::new();
2695 let mut cur = String::new();
2696 let mut iter = path.chars().peekable();
2697 while let Some(c) = iter.next() {
2698 if c == '\\' {
2699 if let Some(&'.') = iter.peek() {
2701 cur.push('.');
2702 iter.next();
2703 continue;
2704 }
2705 cur.push(c);
2706 } else if c == '.' {
2707 out.push(std::mem::take(&mut cur));
2708 } else {
2709 cur.push(c);
2710 }
2711 }
2712 out.push(cur);
2713 out
2714}
2715
2716fn extract_type_schema(v: Option<&Value>) -> Vec<(String, String)> {
2720 match v {
2721 Some(Value::List(pairs)) => pairs.iter().filter_map(|p| {
2722 if let Value::Tuple(items) = p {
2723 if items.len() == 2 {
2724 if let (Value::Str(name), Value::Str(tag)) = (&items[0], &items[1]) {
2725 return Some((name.to_string(), tag.to_string()));
2726 }
2727 }
2728 }
2729 None
2730 }).collect(),
2731 _ => vec![],
2732 }
2733}
2734
2735fn validate_field_types(
2741 json: &serde_json::Value,
2742 schema: &[(String, String)],
2743) -> Result<(), String> {
2744 if schema.is_empty() {
2745 return Ok(());
2746 }
2747 let obj = match json.as_object() {
2748 Some(o) => o,
2749 None => return Ok(()), };
2751 for (field, tag) in schema {
2752 if let Some(val) = obj.get(field) {
2753 if let Err(e) = check_json_type(val, tag) {
2754 return Err(format!("field `{field}`: {e}"));
2755 }
2756 }
2757 }
2758 Ok(())
2759}
2760
2761fn apply_option_wrapping(v: Value, json: &serde_json::Value, schema: &[(String, String)]) -> Value {
2766 if schema.is_empty() {
2767 return v;
2768 }
2769 let fields = match v {
2770 Value::Record { fields, .. } => *fields,
2771 other => return other,
2772 };
2773 let json_obj = match json.as_object() {
2774 Some(o) => o,
2775 None => return Value::record_interned(fields),
2776 };
2777 let mut new_fields = fields;
2778 for (field_name, tag) in schema {
2779 if tag.starts_with("Option[") && tag.ends_with(']') {
2780 let json_val = json_obj.get(field_name.as_str());
2781 let wrapped = match json_val {
2782 None | Some(serde_json::Value::Null) => none(),
2783 Some(_) => {
2784 let inner = new_fields
2785 .get(field_name.as_str())
2786 .cloned()
2787 .unwrap_or(Value::Unit);
2788 some(inner)
2789 }
2790 };
2791 new_fields.insert(smol_str::SmolStr::from(field_name.as_str()), wrapped);
2792 }
2793 }
2794 Value::record_interned(new_fields)
2795}
2796
2797fn check_json_type(val: &serde_json::Value, tag: &str) -> Result<(), String> {
2799 use serde_json::Value as J;
2800 match (tag, val) {
2801 ("Int", J::Number(n)) if n.is_i64() || n.is_u64() => Ok(()),
2802 ("Int", other) => Err(format!("expected Int, got {}", json_type_name(other))),
2803 ("Float", J::Number(_)) => Ok(()),
2804 ("Float", other) => Err(format!("expected Float, got {}", json_type_name(other))),
2805 ("Bool", J::Bool(_)) => Ok(()),
2806 ("Bool", other) => Err(format!("expected Bool, got {}", json_type_name(other))),
2807 ("Str", J::String(_)) => Ok(()),
2808 ("Str", other) => Err(format!("expected Str, got {}", json_type_name(other))),
2809 (tag, J::Null) if tag.starts_with("Option[") => Ok(()),
2811 (tag, val) if tag.starts_with("Option[") && tag.ends_with(']') => {
2812 let inner = &tag[7..tag.len() - 1]; check_json_type(val, inner)
2814 }
2815 (tag, J::Array(items)) if tag.starts_with("List[") && tag.ends_with(']') => {
2817 let inner = &tag[5..tag.len() - 1]; for (i, item) in items.iter().enumerate() {
2819 if let Err(e) = check_json_type(item, inner) {
2820 return Err(format!("[{i}]: {e}"));
2821 }
2822 }
2823 Ok(())
2824 }
2825 ("Record", _) => Ok(()), ("Any", _) => Ok(()), _ => Ok(()), }
2829}
2830
2831fn json_type_name(v: &serde_json::Value) -> &'static str {
2832 match v {
2833 serde_json::Value::Null => "null",
2834 serde_json::Value::Bool(_) => "Bool",
2835 serde_json::Value::Number(_) => "Number",
2836 serde_json::Value::String(_) => "Str",
2837 serde_json::Value::Array(_) => "Array",
2838 serde_json::Value::Object(_) => "Object",
2839 }
2840}
2841
2842fn parse_dotenv(src: &str) -> Result<indexmap::IndexMap<String, String>, String> {
2853 let mut out = indexmap::IndexMap::new();
2854 for (idx, raw) in src.lines().enumerate() {
2855 let line = raw.trim();
2856 if line.is_empty() || line.starts_with('#') {
2857 continue;
2858 }
2859 let after_export = line.strip_prefix("export ").unwrap_or(line);
2862 let (k, v) = match after_export.split_once('=') {
2863 Some(kv) => kv,
2864 None => return Err(format!("dotenv.parse line {}: missing `=`", idx + 1)),
2865 };
2866 let key = k.trim();
2867 if key.is_empty() {
2868 return Err(format!("dotenv.parse line {}: empty key", idx + 1));
2869 }
2870 let v_trim = v.trim();
2871 let value = if let Some(q) = v_trim.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
2872 q.to_string()
2873 } else if let Some(q) = v_trim.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
2874 q.to_string()
2875 } else {
2876 v_trim.to_string()
2877 };
2878 out.insert(key.to_string(), value);
2879 }
2880 Ok(out)
2881}
2882
2883fn instant_from_chrono<Tz: chrono::TimeZone>(dt: chrono::DateTime<Tz>) -> i64 {
2889 dt.timestamp_nanos_opt().unwrap_or(i64::MAX)
2890}
2891
2892fn chrono_from_instant(n: i64) -> chrono::DateTime<chrono::Utc> {
2893 let secs = n.div_euclid(1_000_000_000);
2894 let nanos = n.rem_euclid(1_000_000_000) as u32;
2895 use chrono::TimeZone;
2896 chrono::Utc
2897 .timestamp_opt(secs, nanos)
2898 .single()
2899 .unwrap_or_else(chrono::Utc::now)
2900}
2901
2902fn format_iso(n: i64) -> String {
2903 chrono_from_instant(n).to_rfc3339()
2904}
2905
2906enum TzArg {
2909 Utc,
2910 Local,
2911 Offset(i32),
2913 Iana(String),
2915}
2916
2917fn parse_tz_arg(v: Option<&Value>) -> Result<TzArg, String> {
2918 match v {
2919 Some(Value::Variant { name, args }) => match (name.as_str(), args.as_slice()) {
2920 ("Utc", []) => Ok(TzArg::Utc),
2921 ("Local", []) => Ok(TzArg::Local),
2922 ("Offset", [Value::Int(m)]) => {
2923 let m = i32::try_from(*m).map_err(|_| {
2924 format!("Tz::Offset: minutes out of range: {m}")
2925 })?;
2926 Ok(TzArg::Offset(m))
2927 }
2928 ("Iana", [Value::Str(s)]) => Ok(TzArg::Iana(s.to_string())),
2929 (other, _) => Err(format!(
2930 "expected Tz variant (Utc | Local | Offset(Int) | Iana(Str)), got `{other}` with {} arg(s)",
2931 args.len()
2932 )),
2933 },
2934 Some(other) => Err(format!("expected Tz variant, got {other:?}")),
2935 None => Err("missing Tz argument".into()),
2936 }
2937}
2938
2939fn resolve_tz_to_components(n: i64, tz: &TzArg) -> Result<Value, String> {
2940 use chrono::{TimeZone, Datelike, Timelike, Offset};
2941 let utc_dt = chrono_from_instant(n);
2942 let (y, m, d, hh, mm, ss, ns, off_min) = match tz {
2943 TzArg::Utc => {
2944 let d = utc_dt;
2945 (d.year(), d.month() as i32, d.day() as i32,
2946 d.hour() as i32, d.minute() as i32, d.second() as i32,
2947 d.nanosecond() as i32, 0)
2948 }
2949 TzArg::Local => {
2950 let d = utc_dt.with_timezone(&chrono::Local);
2951 let off = d.offset().fix().local_minus_utc() / 60;
2952 (d.year(), d.month() as i32, d.day() as i32,
2953 d.hour() as i32, d.minute() as i32, d.second() as i32,
2954 d.nanosecond() as i32, off)
2955 }
2956 TzArg::Offset(off_min) => {
2957 let off_secs = off_min.saturating_mul(60);
2958 let fixed = chrono::FixedOffset::east_opt(off_secs)
2959 .ok_or("to_components: offset out of range")?;
2960 let d = utc_dt.with_timezone(&fixed);
2961 (d.year(), d.month() as i32, d.day() as i32,
2962 d.hour() as i32, d.minute() as i32, d.second() as i32,
2963 d.nanosecond() as i32, *off_min)
2964 }
2965 TzArg::Iana(name) => {
2966 let tz: chrono_tz::Tz = name.parse()
2967 .map_err(|e| format!("to_components: unknown timezone `{name}`: {e}"))?;
2968 let d = utc_dt.with_timezone(&tz);
2969 let off = d.offset().fix().local_minus_utc() / 60;
2970 (d.year(), d.month() as i32, d.day() as i32,
2971 d.hour() as i32, d.minute() as i32, d.second() as i32,
2972 d.nanosecond() as i32, off)
2973 }
2974 };
2975 let mut rec = indexmap::IndexMap::new();
2976 rec.insert("year".into(), Value::Int(y as i64));
2977 rec.insert("month".into(), Value::Int(m as i64));
2978 rec.insert("day".into(), Value::Int(d as i64));
2979 rec.insert("hour".into(), Value::Int(hh as i64));
2980 rec.insert("minute".into(), Value::Int(mm as i64));
2981 rec.insert("second".into(), Value::Int(ss as i64));
2982 rec.insert("nano".into(), Value::Int(ns as i64));
2983 rec.insert("tz_offset_minutes".into(), Value::Int(off_min as i64));
2984 let _ = chrono::Utc.timestamp_opt(0, 0); Ok(Value::record_dynamic(rec))
2986}
2987
2988
2989fn instant_from_components(rec: &indexmap::IndexMap<smol_str::SmolStr, Value>) -> Result<i64, String> {
2990 use chrono::TimeZone;
2991 fn get_int(rec: &indexmap::IndexMap<smol_str::SmolStr, Value>, k: &str) -> Result<i64, String> {
2992 match rec.get(k) {
2993 Some(Value::Int(n)) => Ok(*n),
2994 other => Err(format!("from_components: missing or non-int field `{k}`: {other:?}")),
2995 }
2996 }
2997 let y = get_int(rec, "year")? as i32;
2998 let m = get_int(rec, "month")? as u32;
2999 let d = get_int(rec, "day")? as u32;
3000 let hh = get_int(rec, "hour")? as u32;
3001 let mm = get_int(rec, "minute")? as u32;
3002 let ss = get_int(rec, "second")? as u32;
3003 let ns = get_int(rec, "nano")? as u32;
3004 let off_min = get_int(rec, "tz_offset_minutes")? as i32;
3005 let off = chrono::FixedOffset::east_opt(off_min * 60)
3006 .ok_or("from_components: offset out of range")?;
3007 let dt = off
3008 .with_ymd_and_hms(y, m, d, hh, mm, ss)
3009 .single()
3010 .ok_or("from_components: invalid or ambiguous date/time")?;
3011 let dt = dt + chrono::Duration::nanoseconds(ns as i64);
3012 Ok(instant_from_chrono(dt))
3013}
3014
3015type Aead4<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>);
3032
3033type Aead5<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>);
3036
3037fn unpack4_bytes<'a>(
3038 args: &'a [Value],
3039 op: &str,
3040) -> Result<Aead4<'a>, String> {
3041 let pick = |i: usize, name: &str| -> Result<&'a Vec<u8>, String> {
3042 match args.get(i) {
3043 Some(Value::Bytes(b)) => Ok(b),
3044 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3045 None => Err(format!("{op}: missing {name} argument")),
3046 }
3047 };
3048 Ok((pick(0, "key")?, pick(1, "nonce")?, pick(2, "aad")?, pick(3, "plaintext")?))
3049}
3050
3051fn unpack5_bytes<'a>(
3052 args: &'a [Value],
3053 op: &str,
3054) -> Result<Aead5<'a>, String> {
3055 let pick = |i: usize, name: &str| -> Result<&'a Vec<u8>, String> {
3056 match args.get(i) {
3057 Some(Value::Bytes(b)) => Ok(b),
3058 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3059 None => Err(format!("{op}: missing {name} argument")),
3060 }
3061 };
3062 Ok((
3063 pick(0, "key")?,
3064 pick(1, "nonce")?,
3065 pick(2, "aad")?,
3066 pick(3, "ciphertext")?,
3067 pick(4, "tag")?,
3068 ))
3069}
3070
3071fn aead_result(ciphertext: Vec<u8>, tag: Vec<u8>) -> Value {
3072 let mut rec = indexmap::IndexMap::new();
3073 rec.insert("ciphertext".into(), Value::Bytes(ciphertext));
3074 rec.insert("tag".into(), Value::Bytes(tag));
3075 Value::record_dynamic(rec)
3076}
3077
3078fn aead_err(msg: impl Into<String>) -> Value {
3079 let s: String = msg.into();
3080 err_v(Value::Str(s.into()))
3081}
3082
3083fn aes_gcm_seal_impl(args: &[Value]) -> Value {
3084 use aes_gcm::aead::{Aead, KeyInit, Payload};
3085 use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
3086 let (key, nonce, aad, plaintext) = match unpack4_bytes(args, "aes_gcm_seal") {
3087 Ok(t) => t,
3088 Err(e) => return aead_err(e),
3089 };
3090 if nonce.len() != 12 {
3091 return aead_err(format!(
3092 "aes_gcm_seal: nonce must be exactly 12 bytes, got {}", nonce.len()
3093 ));
3094 }
3095 let n = Nonce::from_slice(nonce);
3096 let payload = Payload { msg: plaintext, aad };
3097 let combined = match key.len() {
3100 16 => {
3101 let cipher = Aes128Gcm::new_from_slice(key)
3102 .map_err(|e| e.to_string());
3103 match cipher {
3104 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("aes_gcm_seal: {e}")),
3105 Err(e) => Err(format!("aes_gcm_seal: {e}")),
3106 }
3107 }
3108 32 => {
3109 let cipher = Aes256Gcm::new_from_slice(key)
3110 .map_err(|e| e.to_string());
3111 match cipher {
3112 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("aes_gcm_seal: {e}")),
3113 Err(e) => Err(format!("aes_gcm_seal: {e}")),
3114 }
3115 }
3116 other => return aead_err(format!(
3119 "aes_gcm_seal: key must be 16 or 32 bytes, got {other}"
3120 )),
3121 };
3122 match combined {
3123 Ok(mut buf) => {
3124 let tag_start = buf.len() - 16;
3126 let tag = buf.split_off(tag_start);
3127 ok_v(aead_result(buf, tag))
3128 }
3129 Err(e) => aead_err(e),
3130 }
3131}
3132
3133fn aes_gcm_open_impl(args: &[Value]) -> Value {
3134 use aes_gcm::aead::{Aead, KeyInit, Payload};
3135 use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
3136 let (key, nonce, aad, ciphertext, tag) = match unpack5_bytes(args, "aes_gcm_open") {
3137 Ok(t) => t,
3138 Err(e) => return err_v(Value::Str(e.into())),
3139 };
3140 if nonce.len() != 12 {
3141 return err_v(Value::Str(format!(
3142 "aes_gcm_open: nonce must be exactly 12 bytes, got {}", nonce.len()
3143 ).into()));
3144 }
3145 if tag.len() != 16 {
3146 return err_v(Value::Str(format!(
3147 "aes_gcm_open: tag must be exactly 16 bytes, got {}", tag.len()
3148 ).into()));
3149 }
3150 let mut combined = Vec::with_capacity(ciphertext.len() + tag.len());
3152 combined.extend_from_slice(ciphertext);
3153 combined.extend_from_slice(tag);
3154 let n = Nonce::from_slice(nonce);
3155 let payload = Payload { msg: &combined, aad };
3156 let plaintext = match key.len() {
3157 16 => Aes128Gcm::new_from_slice(key)
3158 .map_err(|e| format!("aes_gcm_open: {e}"))
3159 .and_then(|c| c.decrypt(n, payload).map_err(|e| format!("aes_gcm_open: {e}"))),
3160 32 => Aes256Gcm::new_from_slice(key)
3161 .map_err(|e| format!("aes_gcm_open: {e}"))
3162 .and_then(|c| c.decrypt(n, payload).map_err(|e| format!("aes_gcm_open: {e}"))),
3163 other => return err_v(Value::Str(format!(
3164 "aes_gcm_open: key must be 16 or 32 bytes, got {other}"
3165 ).into())),
3166 };
3167 match plaintext {
3168 Ok(p) => ok_v(Value::Bytes(p)),
3169 Err(e) => err_v(Value::Str(e.into())),
3170 }
3171}
3172
3173fn chacha20_seal_impl(args: &[Value]) -> Value {
3174 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
3175 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
3176 let (key, nonce, aad, plaintext) = match unpack4_bytes(args, "chacha20_poly1305_seal") {
3177 Ok(t) => t,
3178 Err(e) => return aead_err(e),
3179 };
3180 if key.len() != 32 {
3181 return aead_err(format!(
3182 "chacha20_poly1305_seal: key must be exactly 32 bytes, got {}", key.len()
3183 ));
3184 }
3185 if nonce.len() != 12 {
3186 return aead_err(format!(
3187 "chacha20_poly1305_seal: nonce must be exactly 12 bytes, got {}", nonce.len()
3188 ));
3189 }
3190 let cipher = ChaCha20Poly1305::new_from_slice(key)
3191 .map_err(|e| format!("chacha20_poly1305_seal: {e}"));
3192 let n = Nonce::from_slice(nonce);
3193 let payload = Payload { msg: plaintext, aad };
3194 let combined = match cipher {
3195 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("chacha20_poly1305_seal: {e}")),
3196 Err(e) => Err(e),
3197 };
3198 match combined {
3199 Ok(mut buf) => {
3200 let tag_start = buf.len() - 16;
3201 let tag = buf.split_off(tag_start);
3202 ok_v(aead_result(buf, tag))
3203 }
3204 Err(e) => aead_err(e),
3205 }
3206}
3207
3208fn chacha20_open_impl(args: &[Value]) -> Value {
3209 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
3210 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
3211 let (key, nonce, aad, ciphertext, tag) = match unpack5_bytes(args, "chacha20_poly1305_open") {
3212 Ok(t) => t,
3213 Err(e) => return err_v(Value::Str(e.into())),
3214 };
3215 if key.len() != 32 {
3216 return err_v(Value::Str(format!(
3217 "chacha20_poly1305_open: key must be exactly 32 bytes, got {}", key.len()
3218 ).into()));
3219 }
3220 if nonce.len() != 12 {
3221 return err_v(Value::Str(format!(
3222 "chacha20_poly1305_open: nonce must be exactly 12 bytes, got {}", nonce.len()
3223 ).into()));
3224 }
3225 if tag.len() != 16 {
3226 return err_v(Value::Str(format!(
3227 "chacha20_poly1305_open: tag must be exactly 16 bytes, got {}", tag.len()
3228 ).into()));
3229 }
3230 let mut combined = Vec::with_capacity(ciphertext.len() + tag.len());
3231 combined.extend_from_slice(ciphertext);
3232 combined.extend_from_slice(tag);
3233 let cipher = ChaCha20Poly1305::new_from_slice(key)
3234 .map_err(|e| format!("chacha20_poly1305_open: {e}"));
3235 let n = Nonce::from_slice(nonce);
3236 let payload = Payload { msg: &combined, aad };
3237 match cipher.and_then(|c| c.decrypt(n, payload).map_err(|e| format!("chacha20_poly1305_open: {e}"))) {
3238 Ok(p) => ok_v(Value::Bytes(p)),
3239 Err(e) => err_v(Value::Str(e.into())),
3240 }
3241}
3242
3243type Kdf4<'a> = (&'a Vec<u8>, &'a Vec<u8>, i64, i64);
3253
3254type Hkdf4<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, i64);
3257
3258type Argon5<'a> = (&'a Vec<u8>, &'a Vec<u8>, i64, i64, i64);
3261
3262fn pick_bytes<'a>(args: &'a [Value], i: usize, op: &str, name: &str)
3263 -> Result<&'a Vec<u8>, String>
3264{
3265 match args.get(i) {
3266 Some(Value::Bytes(b)) => Ok(b),
3267 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3268 None => Err(format!("{op}: missing {name} argument")),
3269 }
3270}
3271
3272fn pick_int(args: &[Value], i: usize, op: &str, name: &str) -> Result<i64, String> {
3273 match args.get(i) {
3274 Some(Value::Int(n)) => Ok(*n),
3275 Some(other) => Err(format!("{op}: {name} must be Int, got {other:?}")),
3276 None => Err(format!("{op}: missing {name} argument")),
3277 }
3278}
3279
3280fn unpack_kdf4<'a>(args: &'a [Value], op: &str) -> Result<Kdf4<'a>, String> {
3281 Ok((
3282 pick_bytes(args, 0, op, "password")?,
3283 pick_bytes(args, 1, op, "salt")?,
3284 pick_int(args, 2, op, "iterations")?,
3285 pick_int(args, 3, op, "len")?,
3286 ))
3287}
3288
3289fn unpack_hkdf4<'a>(args: &'a [Value], op: &str) -> Result<Hkdf4<'a>, String> {
3290 Ok((
3291 pick_bytes(args, 0, op, "ikm")?,
3292 pick_bytes(args, 1, op, "salt")?,
3293 pick_bytes(args, 2, op, "info")?,
3294 pick_int(args, 3, op, "len")?,
3295 ))
3296}
3297
3298fn unpack_argon5<'a>(args: &'a [Value], op: &str) -> Result<Argon5<'a>, String> {
3299 Ok((
3300 pick_bytes(args, 0, op, "password")?,
3301 pick_bytes(args, 1, op, "salt")?,
3302 pick_int(args, 2, op, "t_cost")?,
3303 pick_int(args, 3, op, "m_cost")?,
3304 pick_int(args, 4, op, "len")?,
3305 ))
3306}
3307
3308const KDF_MAX_LEN: usize = 1024 * 1024;
3313
3314fn check_len(op: &str, len: i64) -> Result<usize, String> {
3315 if len <= 0 {
3316 return Err(format!("{op}: len must be > 0, got {len}"));
3317 }
3318 if (len as u64) > KDF_MAX_LEN as u64 {
3319 return Err(format!(
3320 "{op}: len must be <= {KDF_MAX_LEN}, got {len}"
3321 ));
3322 }
3323 Ok(len as usize)
3324}
3325
3326fn pbkdf2_sha256_impl(args: &[Value]) -> Value {
3327 use hmac::Hmac;
3328 use sha2::Sha256;
3329 let op = "pbkdf2_sha256";
3330 let (password, salt, iterations, len) = match unpack_kdf4(args, op) {
3331 Ok(t) => t,
3332 Err(e) => return err_v(Value::Str(e.into())),
3333 };
3334 if iterations <= 0 {
3335 return err_v(Value::Str(format!(
3336 "{op}: iterations must be > 0, got {iterations}"
3337 ).into()));
3338 }
3339 let out_len = match check_len(op, len) {
3340 Ok(n) => n,
3341 Err(e) => return err_v(Value::Str(e.into())),
3342 };
3343 let rounds = match u32::try_from(iterations) {
3344 Ok(r) => r,
3345 Err(_) => {
3346 return err_v(Value::Str(format!(
3347 "{op}: iterations must fit in u32, got {iterations}"
3348 ).into()))
3349 }
3350 };
3351 let mut out = vec![0u8; out_len];
3352 if let Err(e) = pbkdf2::pbkdf2::<Hmac<Sha256>>(password, salt, rounds, &mut out) {
3353 return err_v(Value::Str(format!("{op}: {e}").into()));
3354 }
3355 ok_v(Value::Bytes(out))
3356}
3357
3358fn hkdf_sha256_impl(args: &[Value]) -> Value {
3359 use hkdf::Hkdf;
3360 use sha2::Sha256;
3361 let op = "hkdf_sha256";
3362 let (ikm, salt, info, len) = match unpack_hkdf4(args, op) {
3363 Ok(t) => t,
3364 Err(e) => return err_v(Value::Str(e.into())),
3365 };
3366 let out_len = match check_len(op, len) {
3367 Ok(n) => n,
3368 Err(e) => return err_v(Value::Str(e.into())),
3369 };
3370 let salt_opt: Option<&[u8]> = if salt.is_empty() { None } else { Some(salt) };
3373 let hk = Hkdf::<Sha256>::new(salt_opt, ikm);
3374 let mut out = vec![0u8; out_len];
3375 match hk.expand(info, &mut out) {
3376 Ok(()) => ok_v(Value::Bytes(out)),
3377 Err(e) => err_v(Value::Str(format!("{op}: {e}").into())),
3378 }
3379}
3380
3381fn argon2id_impl(args: &[Value]) -> Value {
3382 use argon2::{Algorithm, Argon2, Params, Version};
3383 let op = "argon2id";
3384 let (password, salt, t_cost, m_cost, len) = match unpack_argon5(args, op) {
3385 Ok(t) => t,
3386 Err(e) => return err_v(Value::Str(e.into())),
3387 };
3388 let out_len = match check_len(op, len) {
3389 Ok(n) => n,
3390 Err(e) => return err_v(Value::Str(e.into())),
3391 };
3392 let t = match u32::try_from(t_cost) {
3393 Ok(n) if n >= 1 => n,
3394 _ => return err_v(Value::Str(format!(
3395 "{op}: t_cost must be a u32 >= 1, got {t_cost}"
3396 ).into())),
3397 };
3398 let m = match u32::try_from(m_cost) {
3399 Ok(n) if n >= Params::MIN_M_COST => n,
3400 _ => return err_v(Value::Str(format!(
3401 "{op}: m_cost must be a u32 >= {}, got {m_cost}",
3402 Params::MIN_M_COST
3403 ).into())),
3404 };
3405 let params = match Params::new(m, t, 1, Some(out_len)) {
3410 Ok(p) => p,
3411 Err(e) => return err_v(Value::Str(format!("{op}: {e}").into())),
3412 };
3413 let hasher = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
3414 let mut out = vec![0u8; out_len];
3415 if let Err(e) = hasher.hash_password_into(password, salt, &mut out) {
3416 return err_v(Value::Str(format!("{op}: {e}").into()));
3417 }
3418 ok_v(Value::Bytes(out))
3419}
3420
3421#[cfg(test)]
3422mod bytes_builtin_tests {
3423 use super::*;
3424
3425 fn call(op: &str, args: Vec<Value>) -> Result<Value, String> {
3426 dispatch("bytes", op, &args)
3427 }
3428
3429 #[test]
3430 fn concat_appends_in_order() {
3431 let a = Value::Bytes(b"AB".to_vec());
3432 let b = Value::Bytes(b"CD".to_vec());
3433 let got = call("concat", vec![a, b]).unwrap();
3434 assert_eq!(got, Value::Bytes(b"ABCD".to_vec()));
3435 }
3436
3437 #[test]
3438 fn concat_all_joins_a_list_in_order() {
3439 let items = Value::List(vec![
3440 Value::Bytes(b"Hi".to_vec()),
3441 Value::Bytes(b", ".to_vec()),
3442 Value::Bytes(b"there".to_vec()),
3443 ].into());
3444 let got = call("concat_all", vec![items]).unwrap();
3445 assert_eq!(got, Value::Bytes(b"Hi, there".to_vec()));
3446 }
3447
3448 #[test]
3449 fn concat_all_rejects_non_bytes_elements() {
3450 let items = Value::List(vec![Value::Int(1)].into());
3451 assert!(call("concat_all", vec![items]).is_err());
3452 }
3453
3454 #[test]
3455 fn u8_encodes_and_truncates_to_one_byte() {
3456 let got = call("u8", vec![Value::Int(65)]).unwrap();
3457 assert_eq!(got, Value::Bytes(vec![65]));
3458 let wrapped = call("u8", vec![Value::Int(256 + 65)]).unwrap();
3462 assert_eq!(wrapped, Value::Bytes(vec![65]));
3463 }
3464
3465 #[test]
3466 fn u16_le_round_trips() {
3467 let encoded = call("u16_le", vec![Value::Int(258)]).unwrap();
3468 assert_eq!(encoded, Value::Bytes(vec![2, 1]));
3469 let decoded = call("u16_le_at", vec![encoded, Value::Int(0)]).unwrap();
3470 assert_eq!(decoded, ok_v(Value::Int(258)));
3471 }
3472
3473 #[test]
3474 fn u32_le_round_trips() {
3475 let encoded = call("u32_le", vec![Value::Int(70000)]).unwrap();
3476 let decoded = call("u32_le_at", vec![encoded, Value::Int(0)]).unwrap();
3477 assert_eq!(decoded, ok_v(Value::Int(70000)));
3478 }
3479
3480 #[test]
3481 fn u64_le_round_trips_beyond_u32_range() {
3482 let n = 9_007_199_254_740_993_i64; let encoded = call("u64_le", vec![Value::Int(n)]).unwrap();
3484 let decoded = call("u64_le_at", vec![encoded, Value::Int(0)]).unwrap();
3485 assert_eq!(decoded, ok_v(Value::Int(n)));
3486 }
3487
3488 #[test]
3489 fn decoders_report_out_of_range_offsets_as_err_not_panic() {
3490 let one_byte = Value::Bytes(vec![5]);
3491 let got = call("u16_le_at", vec![one_byte, Value::Int(0)]).unwrap();
3492 match got {
3493 Value::Variant { name, .. } => assert_eq!(name, "Err"),
3494 other => panic!("expected an Err variant, got {other:?}"),
3495 }
3496 }
3497
3498 #[test]
3499 fn u16_le_at_reads_at_a_nonzero_offset() {
3500 let buf = Value::Bytes(vec![0xFF, 2, 1]);
3503 let decoded = call("u16_le_at", vec![buf, Value::Int(1)]).unwrap();
3504 assert_eq!(decoded, ok_v(Value::Int(258)));
3505 }
3506}
3507
3508#[cfg(test)]
3509mod ed25519_curve_tests {
3510 use super::*;
3511
3512 fn call(op: &str, args: Vec<Value>) -> Result<Value, String> {
3513 dispatch("crypto", op, &args)
3514 }
3515
3516 #[test]
3517 fn a_real_public_key_is_a_valid_point() {
3518 use ed25519_dalek::SigningKey;
3519 let seed = [7u8; 32];
3520 let sk = SigningKey::from_bytes(&seed);
3521 let pk = sk.verifying_key().to_bytes().to_vec();
3522 let got = call("ed25519_is_valid_point", vec![Value::Bytes(pk)]).unwrap();
3523 assert_eq!(got, Value::Bool(true));
3524 }
3525
3526 #[test]
3527 fn wrong_length_is_not_a_valid_point() {
3528 let got = call("ed25519_is_valid_point", vec![Value::Bytes(vec![1, 2, 3])]).unwrap();
3529 assert_eq!(got, Value::Bool(false));
3530 }
3531
3532 #[test]
3533 fn a_non_curve_point_is_rejected() {
3534 let mut candidate = [1u8; 32];
3539 candidate[31] = 0;
3540 let got = call("ed25519_is_valid_point", vec![Value::Bytes(candidate.to_vec())]).unwrap();
3541 assert_eq!(got, Value::Bool(false));
3542 }
3543}