1use lex_bytecode::{MapKey, Value};
6use smol_str::SmolStr;
7use std::collections::{BTreeMap, BTreeSet, HashMap};
8use std::sync::{Mutex, OnceLock};
9
10pub fn is_pure_call(kind: &str, op: &str) -> bool {
14 if let Some(def) = lex_types::stdlib_spec::lookup(kind, op) {
18 return def.kind != lex_types::stdlib_spec::BuiltinKind::Effect;
19 }
20 if !is_pure_module(kind) { return false; }
21 !matches!(
22 (kind, op),
23 ("crypto", "random")
24 | ("crypto", "random_str_hex")
25 | ("crypto", "p256_generate")
28 | ("crypto", "secp256k1_generate")
30 | ("datetime", "now")
31 | ("http", "send")
32 | ("http", "get")
33 | ("http", "post")
34 | ("http", "stream_lines")
35 | ("arrow", "read_csv")
37 | ("arrow", "read_parquet")
39 | ("arrow", "read_parquet_cols")
40 | ("arrow", "write_parquet")
41 | ("arrow", "write_csv")
42 )
43}
44
45pub fn call_pure_builtin(kind: &str, op: &str, args: Vec<Value>) -> Result<Value, String> {
52 if let Some(f) = crate::stdlib::lookup(kind, op) {
55 return f(args);
56 }
57 dispatch(kind, op, &args)
58}
59
60pub fn try_pure_builtin(kind: &str, op: &str, args: &[Value]) -> Option<Result<Value, String>> {
67 if !is_pure_call(kind, op) { return None; }
68 Some(dispatch(kind, op, args))
69}
70
71pub fn is_pure_module(kind: &str) -> bool {
74 matches!(kind, "str" | "int" | "float" | "bool" | "list" | "iter"
75 | "option" | "result" | "tuple" | "json" | "bytes" | "flow" | "math"
76 | "map" | "set" | "crypto" | "regex" | "deque" | "datetime" | "duration" | "http"
77 | "toml" | "yaml" | "dotenv" | "csv" | "test" | "random" | "parser"
78 | "cli" | "arrow" | "df" | "decimal")
79}
80
81fn dispatch(kind: &str, op: &str, args: &[Value]) -> Result<Value, String> {
82 if let Some(f) = crate::stdlib::lookup(kind, op) {
85 return f(args.to_vec());
86 }
87 match (kind, op) {
88
89 ("int", "to_str") => Ok(Value::Str(expect_int(args.first())?.to_string().into())),
91 ("int", "to_float") => Ok(Value::Float(expect_int(args.first())? as f64)),
92 ("int", "abs") => Ok(Value::Int(expect_int(args.first())?.abs())),
94 ("int", "min") => Ok(Value::Int(expect_int(args.first())?.min(expect_int(args.get(1))?))),
95 ("int", "max") => Ok(Value::Int(expect_int(args.first())?.max(expect_int(args.get(1))?))),
96 ("float", "to_int") => Ok(Value::Int(expect_float(args.first())? as i64)),
97 ("float", "to_str") => Ok(Value::Str(expect_float(args.first())?.to_string().into())),
98
99
100 ("tuple", "fst") => tuple_index(first_arg(args)?, 0),
105 ("tuple", "snd") => tuple_index(first_arg(args)?, 1),
106 ("tuple", "third") => tuple_index(first_arg(args)?, 2),
107 ("tuple", "len") => match first_arg(args)? {
108 Value::Tuple(items) => Ok(Value::Int(items.len() as i64)),
109 other => Err(format!("tuple.len: expected Tuple, got {other:?}")),
110 },
111
112 ("option", "unwrap_or") => {
114 let opt = first_arg(args)?;
115 let default = args.get(1).cloned().unwrap_or(Value::Unit);
116 match opt {
117 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
118 Value::Variant { name, .. } if name == "None" => Ok(default),
119 other => Err(format!("option.unwrap_or expected Option, got {other:?}")),
120 }
121 }
122 ("option", "unwrap_or_else") => {
127 let opt = first_arg(args)?;
128 match opt {
129 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
130 Value::Variant { name, .. } if name == "None" => {
131 Ok(args.get(1).cloned().unwrap_or(Value::Unit))
135 }
136 other => Err(format!("option.unwrap_or_else expected Option, got {other:?}")),
137 }
138 }
139 ("option", "is_some") => match first_arg(args)? {
140 Value::Variant { name, .. } => Ok(Value::Bool(name == "Some")),
141 other => Err(format!("option.is_some expected Option, got {other:?}")),
142 },
143 ("option", "is_none") => match first_arg(args)? {
144 Value::Variant { name, .. } => Ok(Value::Bool(name == "None")),
145 other => Err(format!("option.is_none expected Option, got {other:?}")),
146 },
147 ("option", "ok_or") => {
149 let opt = first_arg(args)?;
150 let err_val = args.get(1).cloned().unwrap_or(Value::Unit);
151 match opt {
152 Value::Variant { name, args } if name == "Some" && !args.is_empty() =>
153 Ok(ok_v(args[0].clone())),
154 Value::Variant { name, .. } if name == "None" => Ok(err_v(err_val)),
155 other => Err(format!("option.ok_or expected Option, got {other:?}")),
156 }
157 }
158
159 ("result", "is_ok") => match first_arg(args)? {
161 Value::Variant { name, .. } => Ok(Value::Bool(name == "Ok")),
162 other => Err(format!("result.is_ok expected Result, got {other:?}")),
163 },
164 ("result", "is_err") => match first_arg(args)? {
165 Value::Variant { name, .. } => Ok(Value::Bool(name == "Err")),
166 other => Err(format!("result.is_err expected Result, got {other:?}")),
167 },
168 ("result", "unwrap_or") => {
169 let res = first_arg(args)?;
170 let default = args.get(1).cloned().unwrap_or(Value::Unit);
171 match res {
172 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
173 Value::Variant { name, .. } if name == "Err" => Ok(default),
174 other => Err(format!("result.unwrap_or expected Result, got {other:?}")),
175 }
176 }
177 ("result", "unwrap_or_else") => {
182 let res = first_arg(args)?;
183 match res {
184 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
185 Value::Variant { name, .. } if name == "Err" =>
186 Ok(args.get(1).cloned().unwrap_or(Value::Unit)),
187 other => Err(format!("result.unwrap_or_else expected Result, got {other:?}")),
188 }
189 }
190
191 ("json", "stringify") => {
193 let v = first_arg(args)?;
194 Ok(Value::Str(serde_json::to_string(&value_to_json(v)).unwrap_or_default().into()))
195 }
196 ("json", "parse") => {
197 let s = expect_str(args.first())?;
198 match serde_json::from_str::<serde_json::Value>(s) {
199 Ok(v) => Ok(ok_v(json_to_value(&v))),
200 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
201 }
202 }
203 ("json", "parse_strict") => {
206 let s = expect_str(args.first())?;
207 let required = required_field_names(args.get(1))?;
208 let schema = extract_type_schema(args.get(2));
209 match serde_json::from_str::<serde_json::Value>(s) {
210 Ok(v) => {
211 if let Err(e) = check_required_fields(&v, &required) {
212 return Ok(err_v(Value::Str(e.into())));
213 }
214 if let Err(e) = validate_field_types(&v, &schema) {
215 return Ok(err_v(Value::Str(e.into())));
216 }
217 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
218 }
219 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
220 }
221 }
222 ("json", "decode") => {
228 let s = expect_str(args.first())?;
229 match serde_json::from_str::<serde_json::Value>(s) {
230 Ok(v) => Ok(ok_v(json_to_adt(&v))),
231 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
232 }
233 }
234 ("json", "encode") => {
235 let j = adt_to_json(first_arg(args)?)?;
236 Ok(Value::Str(serde_json::to_string(&j).unwrap_or_default().into()))
237 }
238 ("json", "encode_pretty") => {
239 let j = adt_to_json(args.first().ok_or("json.encode_pretty: missing arg")?)?;
240 let indent = expect_int(args.get(1))?.max(0) as usize;
241 let mut buf = Vec::new();
242 let space = vec![b' '; indent];
243 let fmt = serde_json::ser::PrettyFormatter::with_indent(&space);
244 let mut ser = serde_json::Serializer::with_formatter(&mut buf, fmt);
245 use serde::Serialize;
246 j.serialize(&mut ser).map_err(|e| format!("json.encode_pretty: {e}"))?;
247 Ok(Value::Str(String::from_utf8_lossy(&buf).into_owned().into()))
248 }
249
250 ("toml", "parse") => {
255 let s = expect_str(args.first())?;
256 match toml::from_str::<serde_json::Value>(s) {
257 Ok(mut v) => {
258 unwrap_toml_datetime_markers(&mut v);
259 Ok(ok_v(json_to_value(&v)))
260 }
261 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
262 }
263 }
264 ("json", "parse_strict_typed") => {
268 let s = expect_str(args.first())?;
269 let required = required_field_names(args.get(1))?;
270 let schema = extract_type_schema(args.get(2));
271 match serde_json::from_str::<serde_json::Value>(s) {
272 Ok(v) => {
273 if let Err(e) = check_required_fields(&v, &required) {
274 return Ok(err_v(Value::Str(e.into())));
275 }
276 if let Err(e) = validate_field_types(&v, &schema) {
277 return Ok(err_v(Value::Str(e.into())));
278 }
279 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
280 }
281 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
282 }
283 }
284
285 ("toml", "parse_strict") => {
288 let s = expect_str(args.first())?;
289 let required = required_field_names(args.get(1))?;
290 let schema = extract_type_schema(args.get(2));
291 match toml::from_str::<serde_json::Value>(s) {
292 Ok(mut v) => {
293 unwrap_toml_datetime_markers(&mut v);
294 if let Err(e) = check_required_fields(&v, &required) {
295 return Ok(err_v(Value::Str(e.into())));
296 }
297 if let Err(e) = validate_field_types(&v, &schema) {
298 return Ok(err_v(Value::Str(e.into())));
299 }
300 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
301 }
302 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
303 }
304 }
305 ("toml", "parse_strict_typed") => {
306 let s = expect_str(args.first())?;
307 let required = required_field_names(args.get(1))?;
308 let schema = extract_type_schema(args.get(2));
309 match toml::from_str::<serde_json::Value>(s) {
310 Ok(mut v) => {
311 unwrap_toml_datetime_markers(&mut v);
312 if let Err(e) = check_required_fields(&v, &required) {
313 return Ok(err_v(Value::Str(e.into())));
314 }
315 if let Err(e) = validate_field_types(&v, &schema) {
316 return Ok(err_v(Value::Str(e.into())));
317 }
318 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
319 }
320 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
321 }
322 }
323 ("toml", "stringify") => {
324 let v = first_arg(args)?;
325 let json = value_to_json(v);
331 match toml::to_string(&json) {
332 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
333 Err(e) => Ok(err_v(Value::Str(format!("toml.stringify: {e}").into()))),
334 }
335 }
336
337 ("yaml", "parse") => {
343 let s = expect_str(args.first())?;
344 match serde_yaml::from_str::<serde_json::Value>(s) {
345 Ok(v) => Ok(ok_v(json_to_value(&v))),
346 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
347 }
348 }
349 ("yaml", "parse_strict") => {
352 let s = expect_str(args.first())?;
353 let required = required_field_names(args.get(1))?;
354 let schema = extract_type_schema(args.get(2));
355 match serde_yaml::from_str::<serde_json::Value>(s) {
356 Ok(v) => {
357 if let Err(e) = check_required_fields(&v, &required) {
358 return Ok(err_v(Value::Str(e.into())));
359 }
360 if let Err(e) = validate_field_types(&v, &schema) {
361 return Ok(err_v(Value::Str(e.into())));
362 }
363 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
364 }
365 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
366 }
367 }
368 ("yaml", "parse_strict_typed") => {
369 let s = expect_str(args.first())?;
370 let required = required_field_names(args.get(1))?;
371 let schema = extract_type_schema(args.get(2));
372 match serde_yaml::from_str::<serde_json::Value>(s) {
373 Ok(v) => {
374 if let Err(e) = check_required_fields(&v, &required) {
375 return Ok(err_v(Value::Str(e.into())));
376 }
377 if let Err(e) = validate_field_types(&v, &schema) {
378 return Ok(err_v(Value::Str(e.into())));
379 }
380 Ok(ok_v(apply_option_wrapping(json_to_value(&v), &v, &schema)))
381 }
382 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
383 }
384 }
385 ("yaml", "stringify") => {
386 let v = first_arg(args)?;
387 let json = value_to_json(v);
388 match serde_yaml::to_string(&json) {
389 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
390 Err(e) => Ok(err_v(Value::Str(format!("yaml.stringify: {e}").into()))),
391 }
392 }
393
394 ("dotenv", "parse") => {
402 use std::collections::BTreeMap;
403 use lex_bytecode::MapKey;
404 let s = expect_str(args.first())?;
405 match parse_dotenv(s) {
406 Ok(map) => {
407 let mut bt: BTreeMap<MapKey, Value> = BTreeMap::new();
408 for (k, v) in map {
409 bt.insert(MapKey::Str(k), Value::Str(v.into()));
410 }
411 Ok(ok_v(Value::Map(bt)))
412 }
413 Err(e) => Ok(err_v(Value::Str(e.into()))),
414 }
415 }
416
417 ("csv", "parse") => {
422 let s = expect_str(args.first())?;
423 let mut rdr = csv::ReaderBuilder::new()
424 .has_headers(false)
425 .flexible(true)
426 .from_reader(s.as_bytes());
427 let mut rows: std::collections::VecDeque<Value> = std::collections::VecDeque::new();
428 for r in rdr.records() {
429 match r {
430 Ok(rec) => {
431 let row: std::collections::VecDeque<Value> = rec.iter()
432 .map(|f| Value::Str(f.into()))
433 .collect();
434 rows.push_back(Value::List(row.into()));
435 }
436 Err(e) => return Ok(err_v(Value::Str(format!("csv.parse: {e}").into()))),
437 }
438 }
439 Ok(ok_v(Value::List(rows.into())))
440 }
441 ("csv", "stringify") => {
442 let v = first_arg(args)?;
447 let rows = match v {
448 Value::List(rs) => rs,
449 _ => return Ok(err_v(Value::Str("csv.stringify expects List[List[Str]]".into()))),
450 };
451 let mut out = Vec::new();
452 {
453 let mut wtr = csv::WriterBuilder::new()
454 .has_headers(false)
455 .from_writer(&mut out);
456 for row in rows {
457 let cells = match row {
458 Value::List(cs) => cs,
459 _ => return Ok(err_v(Value::Str("csv.stringify row must be List[Str]".into()))),
460 };
461 let strs: Vec<String> = cells.iter().map(|c| match c {
462 Value::Str(s) => s.to_string(),
463 other => serde_json::to_string(&other.to_json())
464 .unwrap_or_else(|_| String::new()),
465 }).collect();
466 if let Err(e) = wtr.write_record(&strs) {
467 return Ok(err_v(Value::Str(format!("csv.stringify: {e}").into())));
468 }
469 }
470 if let Err(e) = wtr.flush() {
471 return Ok(err_v(Value::Str(format!("csv.stringify flush: {e}").into())));
472 }
473 }
474 match String::from_utf8(out) {
475 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
476 Err(e) => Ok(err_v(Value::Str(format!("csv.stringify utf8: {e}").into()))),
477 }
478 }
479
480 ("test", "assert_eq") => {
487 let a = first_arg(args)?;
488 let b = args.get(1).ok_or("test.assert_eq: missing second arg")?;
489 if a == b {
490 Ok(ok_v(Value::Unit))
491 } else {
492 Ok(err_v(Value::Str(format!("assert_eq: lhs {} != rhs {}",
493 value_to_json(a), value_to_json(b)).into())))
494 }
495 }
496 ("test", "assert_ne") => {
497 let a = first_arg(args)?;
498 let b = args.get(1).ok_or("test.assert_ne: missing second arg")?;
499 if a != b {
500 Ok(ok_v(Value::Unit))
501 } else {
502 Ok(err_v(Value::Str(format!("assert_ne: both sides are {}",
503 value_to_json(a)).into())))
504 }
505 }
506 ("test", "assert_true") => {
507 match first_arg(args)? {
508 Value::Bool(true) => Ok(ok_v(Value::Unit)),
509 Value::Bool(false) => Ok(err_v(Value::Str("assert_true: was false".into()))),
510 other => Err(format!("test.assert_true expects Bool, got {other:?}")),
511 }
512 }
513 ("test", "assert_false") => {
514 match first_arg(args)? {
515 Value::Bool(false) => Ok(ok_v(Value::Unit)),
516 Value::Bool(true) => Ok(err_v(Value::Str("assert_false: was true".into()))),
517 other => Err(format!("test.assert_false expects Bool, got {other:?}")),
518 }
519 }
520
521 ("bytes", "len") => {
523 let b = expect_bytes(args.first())?;
524 Ok(Value::Int(b.len() as i64))
525 }
526 ("bytes", "eq") => {
527 let a = expect_bytes(args.first())?;
528 let b = expect_bytes(args.get(1))?;
529 Ok(Value::Bool(a == b))
530 }
531 ("bytes", "from_str") => {
532 let s = expect_str(args.first())?;
533 Ok(Value::Bytes(s.as_bytes().to_vec()))
534 }
535 ("bytes", "to_str") => {
536 let b = expect_bytes(args.first())?;
537 match String::from_utf8(b.to_vec()) {
538 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
539 Err(e) => Ok(err_v(Value::Str(format!("{e}").into()))),
540 }
541 }
542 ("bytes", "slice") => {
543 let b = expect_bytes(args.first())?;
544 let lo = expect_int(args.get(1))? as usize;
545 let hi = expect_int(args.get(2))? as usize;
546 if lo > hi || hi > b.len() {
547 return Err(format!("bytes.slice: out of range [{lo}..{hi}] of {}", b.len()));
548 }
549 Ok(Value::Bytes(b[lo..hi].to_vec()))
550 }
551 ("bytes", "is_empty") => {
552 let b = expect_bytes(args.first())?;
553 Ok(Value::Bool(b.is_empty()))
554 }
555 ("bytes", "concat") => {
556 let a = expect_bytes(args.first())?;
557 let b = expect_bytes(args.get(1))?;
558 let mut out = a.clone();
559 out.extend_from_slice(b);
560 Ok(Value::Bytes(out))
561 }
562 ("bytes", "concat_all") => {
563 let items = match args.first() {
564 Some(Value::List(xs)) => xs,
565 other => return Err(format!("bytes.concat_all expects a List[Bytes], got {other:?}")),
566 };
567 let mut out = Vec::new();
568 for item in items {
569 match item {
570 Value::Bytes(b) => out.extend_from_slice(b),
571 other => return Err(format!("bytes.concat_all: list element is not Bytes, got {other:?}")),
572 }
573 }
574 Ok(Value::Bytes(out))
575 }
576 ("bytes", "u8") => {
577 let n = expect_int(args.first())?;
578 Ok(Value::Bytes(vec![n as u8]))
579 }
580 ("bytes", "u16_le") => {
581 let n = expect_int(args.first())?;
582 Ok(Value::Bytes((n as u16).to_le_bytes().to_vec()))
583 }
584 ("bytes", "u32_le") => {
585 let n = expect_int(args.first())?;
586 Ok(Value::Bytes((n as u32).to_le_bytes().to_vec()))
587 }
588 ("bytes", "u64_le") => {
589 let n = expect_int(args.first())?;
590 Ok(Value::Bytes((n as u64).to_le_bytes().to_vec()))
591 }
592 ("bytes", "u8_at") => {
593 let b = expect_bytes(args.first())?;
594 let off = expect_int(args.get(1))? as usize;
595 match b.get(off) {
596 Some(&byte) => Ok(ok_v(Value::Int(byte as i64))),
597 None => Ok(err_v(Value::Str(format!("bytes.u8_at: offset {off} out of range of {} bytes", b.len()).into()))),
598 }
599 }
600 ("bytes", "u16_le_at") => {
601 let b = expect_bytes(args.first())?;
602 let off = expect_int(args.get(1))? as usize;
603 match b.get(off..off + 2) {
604 Some(slice) => {
605 let arr: [u8; 2] = slice.try_into().unwrap();
606 Ok(ok_v(Value::Int(u16::from_le_bytes(arr) as i64)))
607 }
608 None => Ok(err_v(Value::Str(format!("bytes.u16_le_at: offset {off} out of range of {} bytes", b.len()).into()))),
609 }
610 }
611 ("bytes", "u32_le_at") => {
612 let b = expect_bytes(args.first())?;
613 let off = expect_int(args.get(1))? as usize;
614 match b.get(off..off + 4) {
615 Some(slice) => {
616 let arr: [u8; 4] = slice.try_into().unwrap();
617 Ok(ok_v(Value::Int(u32::from_le_bytes(arr) as i64)))
618 }
619 None => Ok(err_v(Value::Str(format!("bytes.u32_le_at: offset {off} out of range of {} bytes", b.len()).into()))),
620 }
621 }
622 ("bytes", "u64_le_at") => {
623 let b = expect_bytes(args.first())?;
624 let off = expect_int(args.get(1))? as usize;
625 match b.get(off..off + 8) {
626 Some(slice) => {
627 let arr: [u8; 8] = slice.try_into().unwrap();
628 Ok(ok_v(Value::Int(u64::from_le_bytes(arr) as i64)))
629 }
630 None => Ok(err_v(Value::Str(format!("bytes.u64_le_at: offset {off} out of range of {} bytes", b.len()).into()))),
631 }
632 }
633
634 ("math", "exp") => Ok(Value::Float(expect_float(args.first())?.exp())),
641 ("math", "log") => Ok(Value::Float(expect_float(args.first())?.ln())),
642 ("math", "log2") => Ok(Value::Float(expect_float(args.first())?.log2())),
643 ("math", "log10") => Ok(Value::Float(expect_float(args.first())?.log10())),
644 ("math", "sqrt") => Ok(Value::Float(expect_float(args.first())?.sqrt())),
645 ("math", "abs") => Ok(Value::Float(expect_float(args.first())?.abs())),
646 ("math", "sin") => Ok(Value::Float(expect_float(args.first())?.sin())),
647 ("math", "cos") => Ok(Value::Float(expect_float(args.first())?.cos())),
648 ("math", "tan") => Ok(Value::Float(expect_float(args.first())?.tan())),
649 ("math", "asin") => Ok(Value::Float(expect_float(args.first())?.asin())),
650 ("math", "acos") => Ok(Value::Float(expect_float(args.first())?.acos())),
651 ("math", "atan") => Ok(Value::Float(expect_float(args.first())?.atan())),
652 ("math", "floor") => Ok(Value::Float(expect_float(args.first())?.floor())),
653 ("math", "ceil") => Ok(Value::Float(expect_float(args.first())?.ceil())),
654 ("math", "round") => Ok(Value::Float(expect_float(args.first())?.round())),
655 ("math", "trunc") => Ok(Value::Float(expect_float(args.first())?.trunc())),
656 ("math", "pow") => {
657 let a = expect_float(args.first())?;
658 let b = expect_float(args.get(1))?;
659 Ok(Value::Float(a.powf(b)))
660 }
661 ("math", "atan2") => {
662 let y = expect_float(args.first())?;
663 let x = expect_float(args.get(1))?;
664 Ok(Value::Float(y.atan2(x)))
665 }
666 ("math", "min") => {
667 let a = expect_float(args.first())?;
668 let b = expect_float(args.get(1))?;
669 Ok(Value::Float(a.min(b)))
670 }
671 ("math", "max") => {
672 let a = expect_float(args.first())?;
673 let b = expect_float(args.get(1))?;
674 Ok(Value::Float(a.max(b)))
675 }
676 ("math", "zeros") => {
677 let r = expect_int(args.first())?;
678 let c = expect_int(args.get(1))?;
679 if r < 0 || c < 0 {
680 return Err(format!("math.zeros: negative dim {r}x{c}"));
681 }
682 let r = r as usize; let c = c as usize;
683 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![0.0; r * c] })
684 }
685 ("math", "ones") => {
686 let r = expect_int(args.first())?;
687 let c = expect_int(args.get(1))?;
688 if r < 0 || c < 0 {
689 return Err(format!("math.ones: negative dim {r}x{c}"));
690 }
691 let r = r as usize; let c = c as usize;
692 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![1.0; r * c] })
693 }
694 ("math", "from_lists") => {
695 let rows = expect_list(args.first())?;
696 let r = rows.len();
697 if r == 0 {
698 return Ok(Value::F64Array { rows: 0, cols: 0, data: Vec::new() });
699 }
700 let first_row = match &rows[0] {
701 Value::List(xs) => xs,
702 other => return Err(format!("math.from_lists: row 0 not List, got {other:?}")),
703 };
704 let c = first_row.len();
705 let mut data = Vec::with_capacity(r * c);
706 for (i, row) in rows.iter().enumerate() {
707 let row = match row {
708 Value::List(xs) => xs,
709 other => return Err(format!("math.from_lists: row {i} not List, got {other:?}")),
710 };
711 if row.len() != c {
712 return Err(format!("math.from_lists: row {i} has {} cols, expected {c}", row.len()));
713 }
714 for (j, v) in row.iter().enumerate() {
715 let f = match v {
716 Value::Float(f) => *f,
717 Value::Int(n) => *n as f64,
718 other => return Err(format!("math.from_lists: ({i},{j}) not numeric, got {other:?}")),
719 };
720 data.push(f);
721 }
722 }
723 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
724 }
725 ("math", "from_flat") => {
726 let r = expect_int(args.first())?;
727 let c = expect_int(args.get(1))?;
728 let xs = expect_list(args.get(2))?;
729 if r < 0 || c < 0 {
730 return Err(format!("math.from_flat: negative dim {r}x{c}"));
731 }
732 let r = r as usize; let c = c as usize;
733 if xs.len() != r * c {
734 return Err(format!("math.from_flat: list len {} != {}*{}", xs.len(), r, c));
735 }
736 let mut data = Vec::with_capacity(r * c);
737 for v in xs {
738 data.push(match v {
739 Value::Float(f) => *f,
740 Value::Int(n) => *n as f64,
741 other => return Err(format!("math.from_flat: non-numeric element {other:?}")),
742 });
743 }
744 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
745 }
746 ("math", "rows") => {
747 let (r, _, _) = unpack_matrix(first_arg(args)?)?;
748 Ok(Value::Int(r as i64))
749 }
750 ("math", "cols") => {
751 let (_, c, _) = unpack_matrix(first_arg(args)?)?;
752 Ok(Value::Int(c as i64))
753 }
754 ("math", "get") => {
755 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
756 let i = expect_int(args.get(1))? as usize;
757 let j = expect_int(args.get(2))? as usize;
758 if i >= r || j >= c {
759 return Err(format!("math.get: ({i},{j}) out of {r}x{c}"));
760 }
761 Ok(Value::Float(data[i * c + j]))
762 }
763 ("math", "to_flat") => {
764 let (_, _, data) = unpack_matrix(first_arg(args)?)?;
765 Ok(Value::List(data.into_iter().map(Value::Float).collect()))
766 }
767 ("math", "transpose") => {
768 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
769 let mut out = vec![0.0; r * c];
770 for i in 0..r {
771 for j in 0..c {
772 out[j * r + i] = data[i * c + j];
773 }
774 }
775 Ok(Value::F64Array { rows: c as u32, cols: r as u32, data: out })
776 }
777 ("math", "matmul") => {
778 let (m, k1, a) = unpack_matrix(first_arg(args)?)?;
779 let (k2, n, b) = unpack_matrix(args.get(1).ok_or("math.matmul: missing arg 1")?)?;
780 if k1 != k2 {
781 return Err(format!("math.matmul: dim mismatch {m}x{k1} · {k2}x{n}"));
782 }
783 let mut c = vec![0.0; m * n];
787 for i in 0..m {
788 for kk in 0..k1 {
789 let aik = a[i * k1 + kk];
790 for j in 0..n {
791 c[i * n + j] += aik * b[kk * n + j];
792 }
793 }
794 }
795 Ok(Value::F64Array { rows: m as u32, cols: n as u32, data: c })
796 }
797 ("math", "scale") => {
798 let s = expect_float(args.first())?;
799 let (r, c, mut data) = unpack_matrix(args.get(1).ok_or("math.scale: missing arg 1")?)?;
800 for x in &mut data { *x *= s; }
801 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
802 }
803 ("math", "add") | ("math", "sub") => {
804 let (ar, ac, a) = unpack_matrix(first_arg(args)?)?;
805 let (br, bc, b) = unpack_matrix(args.get(1).ok_or("math.add/sub: missing arg 1")?)?;
806 if ar != br || ac != bc {
807 return Err(format!("math.{op}: shape mismatch {ar}x{ac} vs {br}x{bc}"));
808 }
809 let neg = op == "sub";
810 let mut out = a;
811 for (i, x) in out.iter_mut().enumerate() {
812 if neg { *x -= b[i] } else { *x += b[i] }
813 }
814 Ok(Value::F64Array { rows: ar as u32, cols: ac as u32, data: out })
815 }
816 ("math", "sigmoid") => {
817 let (r, c, mut data) = unpack_matrix(first_arg(args)?)?;
818 for x in &mut data { *x = 1.0 / (1.0 + (-*x).exp()); }
819 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
820 }
821
822 ("map", "new") => Ok(Value::Map(BTreeMap::new())),
824 ("map", "size") => Ok(Value::Int(expect_map(args.first())?.len() as i64)),
825 ("map", "has") => {
826 let m = expect_map(args.first())?;
827 let k = MapKey::from_value(args.get(1).ok_or("map.has: missing key")?)?;
828 Ok(Value::Bool(m.contains_key(&k)))
829 }
830 ("map", "get") => {
831 let m = expect_map(args.first())?;
832 let k = MapKey::from_value(args.get(1).ok_or("map.get: missing key")?)?;
833 Ok(match m.get(&k) {
834 Some(v) => some(v.clone()),
835 None => none(),
836 })
837 }
838 ("map", "set") => {
839 let mut m = expect_map(args.first())?.clone();
840 let k = MapKey::from_value(args.get(1).ok_or("map.set: missing key")?)?;
841 let v = args.get(2).ok_or("map.set: missing value")?.clone();
842 m.insert(k, v);
843 Ok(Value::Map(m))
844 }
845 ("map", "delete") => {
846 let mut m = expect_map(args.first())?.clone();
847 let k = MapKey::from_value(args.get(1).ok_or("map.delete: missing key")?)?;
848 m.remove(&k);
849 Ok(Value::Map(m))
850 }
851 ("map", "keys") => {
852 let m = expect_map(args.first())?;
853 Ok(Value::List(m.keys().cloned().map(MapKey::into_value).collect()))
854 }
855 ("map", "values") => {
856 let m = expect_map(args.first())?;
857 Ok(Value::List(m.values().cloned().collect()))
858 }
859 ("map", "entries") => {
860 let m = expect_map(args.first())?;
861 Ok(Value::List(m.iter()
862 .map(|(k, v)| Value::Tuple(vec![k.as_value(), v.clone()]))
863 .collect()))
864 }
865 ("map", "from_list") => {
866 let pairs = expect_list(args.first())?;
867 let mut m = BTreeMap::new();
868 for p in pairs {
869 let items = match p {
870 Value::Tuple(items) if items.len() == 2 => items,
871 other => return Err(format!(
872 "map.from_list element must be a 2-tuple, got {other:?}")),
873 };
874 let k = MapKey::from_value(&items[0])?;
875 m.insert(k, items[1].clone());
876 }
877 Ok(Value::Map(m))
878 }
879
880 ("set", "new") => Ok(Value::Set(BTreeSet::new())),
882 ("set", "size") => Ok(Value::Int(expect_set(args.first())?.len() as i64)),
883 ("set", "has") => {
884 let s = expect_set(args.first())?;
885 let k = MapKey::from_value(args.get(1).ok_or("set.has: missing element")?)?;
886 Ok(Value::Bool(s.contains(&k)))
887 }
888 ("set", "add") => {
889 let mut s = expect_set(args.first())?.clone();
890 let k = MapKey::from_value(args.get(1).ok_or("set.add: missing element")?)?;
891 s.insert(k);
892 Ok(Value::Set(s))
893 }
894 ("set", "delete") => {
895 let mut s = expect_set(args.first())?.clone();
896 let k = MapKey::from_value(args.get(1).ok_or("set.delete: missing element")?)?;
897 s.remove(&k);
898 Ok(Value::Set(s))
899 }
900 ("set", "to_list") => {
901 let s = expect_set(args.first())?;
902 Ok(Value::List(s.iter().cloned().map(MapKey::into_value).collect()))
903 }
904 ("set", "from_list") => {
905 let xs = expect_list(args.first())?;
906 let mut s = BTreeSet::new();
907 for x in xs {
908 s.insert(MapKey::from_value(x)?);
909 }
910 Ok(Value::Set(s))
911 }
912 ("set", "union") => {
913 let a = expect_set(args.first())?;
914 let b = expect_set(args.get(1))?;
915 Ok(Value::Set(a.union(b).cloned().collect()))
916 }
917 ("set", "intersect") => {
918 let a = expect_set(args.first())?;
919 let b = expect_set(args.get(1))?;
920 Ok(Value::Set(a.intersection(b).cloned().collect()))
921 }
922 ("set", "diff") => {
923 let a = expect_set(args.first())?;
924 let b = expect_set(args.get(1))?;
925 Ok(Value::Set(a.difference(b).cloned().collect()))
926 }
927 ("set", "is_empty") => Ok(Value::Bool(expect_set(args.first())?.is_empty())),
928 ("set", "is_subset") => {
929 let a = expect_set(args.first())?;
930 let b = expect_set(args.get(1))?;
931 Ok(Value::Bool(a.is_subset(b)))
932 }
933
934 ("map", "merge") => {
936 let a = expect_map(args.first())?.clone();
939 let b = expect_map(args.get(1))?;
940 let mut out = a;
941 for (k, v) in b {
942 out.insert(k.clone(), v.clone());
943 }
944 Ok(Value::Map(out))
945 }
946 ("map", "is_empty") => Ok(Value::Bool(expect_map(args.first())?.is_empty())),
947
948 ("deque", "new") => Ok(Value::Deque(std::collections::VecDeque::new())),
950 ("deque", "size") => Ok(Value::Int(expect_deque(args.first())?.len() as i64)),
951 ("deque", "is_empty") => Ok(Value::Bool(expect_deque(args.first())?.is_empty())),
952 ("deque", "push_back") => {
953 let mut d = expect_deque(args.first())?.clone();
954 let x = args.get(1).ok_or("deque.push_back: missing value")?.clone();
955 d.push_back(x);
956 Ok(Value::Deque(d))
957 }
958 ("deque", "push_front") => {
959 let mut d = expect_deque(args.first())?.clone();
960 let x = args.get(1).ok_or("deque.push_front: missing value")?.clone();
961 d.push_front(x);
962 Ok(Value::Deque(d))
963 }
964 ("deque", "pop_back") => {
965 let mut d = expect_deque(args.first())?.clone();
966 match d.pop_back() {
967 Some(x) => Ok(Value::Variant {
968 name: "Some".into(),
969 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
970 }),
971 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
972 }
973 }
974 ("deque", "pop_front") => {
975 let mut d = expect_deque(args.first())?.clone();
976 match d.pop_front() {
977 Some(x) => Ok(Value::Variant {
978 name: "Some".into(),
979 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
980 }),
981 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
982 }
983 }
984 ("deque", "peek_back") => {
985 let d = expect_deque(args.first())?;
986 match d.back() {
987 Some(x) => Ok(Value::Variant {
988 name: "Some".into(),
989 args: vec![x.clone()],
990 }),
991 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
992 }
993 }
994 ("deque", "peek_front") => {
995 let d = expect_deque(args.first())?;
996 match d.front() {
997 Some(x) => Ok(Value::Variant {
998 name: "Some".into(),
999 args: vec![x.clone()],
1000 }),
1001 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1002 }
1003 }
1004 ("deque", "from_list") => {
1005 let xs = expect_list(args.first())?;
1006 Ok(Value::Deque(xs.iter().cloned().collect()))
1007 }
1008 ("deque", "to_list") => {
1009 let d = expect_deque(args.first())?;
1010 Ok(Value::List(d.iter().cloned().collect()))
1011 }
1012
1013 ("crypto", "sha256") => {
1016 use sha2::{Digest, Sha256};
1017 let data = expect_bytes(args.first())?;
1018 let mut h = Sha256::new();
1019 h.update(data);
1020 Ok(Value::Bytes(h.finalize().to_vec()))
1021 }
1022 ("crypto", "sha512") => {
1023 use sha2::{Digest, Sha512};
1024 let data = expect_bytes(args.first())?;
1025 let mut h = Sha512::new();
1026 h.update(data);
1027 Ok(Value::Bytes(h.finalize().to_vec()))
1028 }
1029 ("crypto", "md5") => {
1030 use md5::{Digest, Md5};
1031 let data = expect_bytes(args.first())?;
1032 let mut h = Md5::new();
1033 h.update(data);
1034 Ok(Value::Bytes(h.finalize().to_vec()))
1035 }
1036 ("crypto", "blake2b") => {
1040 use blake2::{Blake2b512, Digest};
1041 let data = expect_bytes(args.first())?;
1042 let mut h = Blake2b512::new();
1043 h.update(data);
1044 Ok(Value::Bytes(h.finalize().to_vec()))
1045 }
1046 ("crypto", "keccak256") => {
1051 use sha3::{Digest, Keccak256};
1052 let data = expect_bytes(args.first())?;
1053 let mut h = Keccak256::new();
1054 h.update(data);
1055 Ok(Value::Bytes(h.finalize().to_vec()))
1056 }
1057 ("crypto", "sha256_str") => {
1061 use sha2::{Digest, Sha256};
1062 let s = expect_str(args.first())?;
1063 let mut h = Sha256::new();
1064 h.update(s.as_bytes());
1065 Ok(Value::Str(hex::encode(h.finalize()).into()))
1066 }
1067 ("crypto", "sha512_str") => {
1068 use sha2::{Digest, Sha512};
1069 let s = expect_str(args.first())?;
1070 let mut h = Sha512::new();
1071 h.update(s.as_bytes());
1072 Ok(Value::Str(hex::encode(h.finalize()).into()))
1073 }
1074 ("crypto", "hmac_sha256") => {
1075 use hmac::{Hmac, KeyInit, Mac};
1076 type HmacSha256 = Hmac<sha2::Sha256>;
1077 let key = expect_bytes(args.first())?;
1078 let data = expect_bytes(args.get(1))?;
1079 let mut mac = HmacSha256::new_from_slice(key)
1080 .map_err(|e| format!("hmac_sha256 key: {e}"))?;
1081 mac.update(data);
1082 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1083 }
1084 ("crypto", "hmac_sha512") => {
1085 use hmac::{Hmac, KeyInit, Mac};
1086 type HmacSha512 = Hmac<sha2::Sha512>;
1087 let key = expect_bytes(args.first())?;
1088 let data = expect_bytes(args.get(1))?;
1089 let mut mac = HmacSha512::new_from_slice(key)
1090 .map_err(|e| format!("hmac_sha512 key: {e}"))?;
1091 mac.update(data);
1092 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1093 }
1094 ("crypto", "ed25519_public_key") => {
1098 use ed25519_dalek::SigningKey;
1099 let secret = expect_bytes(args.first())?;
1100 let seed: [u8; 32] = match secret.as_slice().try_into() {
1101 Ok(s) => s,
1102 Err(_) => return Ok(err_v(Value::Str("ed25519_public_key: secret must be 32 bytes".into()))),
1103 };
1104 let sk = SigningKey::from_bytes(&seed);
1105 Ok(ok_v(Value::Bytes(sk.verifying_key().to_bytes().to_vec())))
1106 }
1107 ("crypto", "ed25519_sign") => {
1108 use ed25519_dalek::{Signer, SigningKey};
1109 let secret = expect_bytes(args.first())?;
1110 let message = expect_bytes(args.get(1))?;
1111 let seed: [u8; 32] = match secret.as_slice().try_into() {
1112 Ok(s) => s,
1113 Err(_) => return Ok(err_v(Value::Str("ed25519_sign: secret must be 32 bytes".into()))),
1114 };
1115 let sk = SigningKey::from_bytes(&seed);
1116 Ok(ok_v(Value::Bytes(sk.sign(message).to_bytes().to_vec())))
1117 }
1118 ("crypto", "ed25519_verify") => {
1119 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
1120 let public = expect_bytes(args.first())?;
1121 let message = expect_bytes(args.get(1))?;
1122 let sig_bytes = expect_bytes(args.get(2))?;
1123 let pk_arr: [u8; 32] = match public.as_slice().try_into() {
1124 Ok(p) => p,
1125 Err(_) => return Ok(Value::Bool(false)),
1126 };
1127 let sig_arr: [u8; 64] = match sig_bytes.as_slice().try_into() {
1128 Ok(s) => s,
1129 Err(_) => return Ok(Value::Bool(false)),
1130 };
1131 let vk = match VerifyingKey::from_bytes(&pk_arr) {
1132 Ok(v) => v,
1133 Err(_) => return Ok(Value::Bool(false)),
1134 };
1135 let sig = Signature::from_bytes(&sig_arr);
1136 Ok(Value::Bool(vk.verify(message, &sig).is_ok()))
1137 }
1138 ("crypto", "ed25519_is_valid_point") => {
1139 use ed25519_dalek::VerifyingKey;
1140 let candidate = expect_bytes(args.first())?;
1141 let arr: [u8; 32] = match candidate.as_slice().try_into() {
1142 Ok(a) => a,
1143 Err(_) => return Ok(Value::Bool(false)),
1144 };
1145 Ok(Value::Bool(VerifyingKey::from_bytes(&arr).is_ok()))
1146 }
1147 ("crypto", "p256_public_key") => {
1159 use p256::ecdsa::SigningKey;
1160 let secret = expect_bytes(args.first())?;
1161 let sk = match SigningKey::from_slice(secret) {
1162 Ok(k) => k,
1163 Err(_) => return Ok(err_v(Value::Str(
1164 "p256_public_key: secret must be a 32-byte P-256 scalar".into()))),
1165 };
1166 let point = sk.verifying_key().to_encoded_point(true);
1167 Ok(ok_v(Value::Bytes(point.as_bytes().to_vec())))
1168 }
1169 ("crypto", "p256_sign") => {
1170 use p256::ecdsa::{signature::Signer, Signature, SigningKey};
1171 let secret = expect_bytes(args.first())?;
1172 let message = expect_bytes(args.get(1))?;
1173 let sk = match SigningKey::from_slice(secret) {
1174 Ok(k) => k,
1175 Err(_) => return Ok(err_v(Value::Str(
1176 "p256_sign: secret must be a 32-byte P-256 scalar".into()))),
1177 };
1178 let sig: Signature = sk.sign(message);
1179 Ok(ok_v(Value::Bytes(sig.to_der().as_bytes().to_vec())))
1180 }
1181 ("crypto", "p256_verify") => {
1182 use p256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
1183 let public = expect_bytes(args.first())?;
1184 let message = expect_bytes(args.get(1))?;
1185 let sig_bytes = expect_bytes(args.get(2))?;
1186 let vk = match VerifyingKey::from_sec1_bytes(public) {
1187 Ok(v) => v,
1188 Err(_) => return Ok(Value::Bool(false)),
1189 };
1190 let sig = match Signature::from_der(sig_bytes) {
1191 Ok(s) => s,
1192 Err(_) => return Ok(Value::Bool(false)),
1193 };
1194 Ok(Value::Bool(vk.verify(message, &sig).is_ok()))
1195 }
1196 ("crypto", "secp256k1_public_key") => {
1207 use k256::ecdsa::SigningKey;
1208 let secret = expect_bytes(args.first())?;
1209 let sk = match SigningKey::from_slice(secret) {
1210 Ok(k) => k,
1211 Err(_) => return Ok(err_v(Value::Str(
1212 "secp256k1_public_key: secret must be a 32-byte secp256k1 scalar".into()))),
1213 };
1214 let point = sk.verifying_key().to_encoded_point(false);
1217 Ok(ok_v(Value::Bytes(point.as_bytes().to_vec())))
1218 }
1219 ("crypto", "secp256k1_sign_digest") => {
1220 use k256::ecdsa::SigningKey;
1221 let secret = expect_bytes(args.first())?;
1222 let digest = expect_bytes(args.get(1))?;
1223 if digest.len() != 32 {
1224 return Ok(err_v(Value::Str(
1225 "secp256k1_sign_digest: digest must be exactly 32 bytes".into())));
1226 }
1227 let sk = match SigningKey::from_slice(secret) {
1228 Ok(k) => k,
1229 Err(_) => return Ok(err_v(Value::Str(
1230 "secp256k1_sign_digest: secret must be a 32-byte secp256k1 scalar".into()))),
1231 };
1232 match sk.sign_prehash_recoverable(digest) {
1235 Ok((sig, recid)) => {
1236 let mut out = sig.to_bytes().to_vec(); out.push(27u8 + recid.to_byte());
1238 Ok(ok_v(Value::Bytes(out)))
1239 }
1240 Err(e) => Ok(err_v(Value::Str(
1241 format!("secp256k1_sign_digest: {e}").into()))),
1242 }
1243 }
1244 ("crypto", "secp256k1_recover") => {
1245 use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
1246 let digest = expect_bytes(args.first())?;
1247 let sig_bytes = expect_bytes(args.get(1))?;
1248 if digest.len() != 32 {
1249 return Ok(err_v(Value::Str(
1250 "secp256k1_recover: digest must be exactly 32 bytes".into())));
1251 }
1252 if sig_bytes.len() != 65 {
1253 return Ok(err_v(Value::Str(
1254 "secp256k1_recover: signature must be 65 bytes (r‖s‖v)".into())));
1255 }
1256 let sig = match Signature::from_slice(&sig_bytes[..64]) {
1257 Ok(s) => s,
1258 Err(_) => return Ok(err_v(Value::Str(
1259 "secp256k1_recover: malformed r‖s".into()))),
1260 };
1261 let v = sig_bytes[64];
1263 let recid_byte = if v >= 27 { v - 27 } else { v };
1264 let recid = match RecoveryId::from_byte(recid_byte) {
1265 Some(r) => r,
1266 None => return Ok(err_v(Value::Str(
1267 "secp256k1_recover: invalid recovery id".into()))),
1268 };
1269 match VerifyingKey::recover_from_prehash(digest, &sig, recid) {
1270 Ok(vk) => Ok(ok_v(Value::Bytes(
1271 vk.to_encoded_point(false).as_bytes().to_vec()))),
1272 Err(e) => Ok(err_v(Value::Str(
1273 format!("secp256k1_recover: {e}").into()))),
1274 }
1275 }
1276 ("crypto", "secp256k1_verify") => {
1277 use k256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};
1278 let public = expect_bytes(args.first())?;
1279 let digest = expect_bytes(args.get(1))?;
1280 let sig_bytes = expect_bytes(args.get(2))?;
1281 if digest.len() != 32 {
1282 return Ok(Value::Bool(false));
1283 }
1284 let vk = match VerifyingKey::from_sec1_bytes(public) {
1285 Ok(v) => v,
1286 Err(_) => return Ok(Value::Bool(false)),
1287 };
1288 let rs = if sig_bytes.len() == 65 { &sig_bytes[..64] } else { sig_bytes.as_slice() };
1290 let sig = match Signature::from_slice(rs) {
1291 Ok(s) => s,
1292 Err(_) => return Ok(Value::Bool(false)),
1293 };
1294 Ok(Value::Bool(vk.verify_prehash(digest, &sig).is_ok()))
1295 }
1296 ("crypto", "base64_encode") => {
1297 use base64::{Engine, engine::general_purpose::STANDARD};
1298 let data = expect_bytes(args.first())?;
1299 Ok(Value::Str(STANDARD.encode(data).into()))
1300 }
1301 ("crypto", "base64_decode") => {
1302 use base64::{Engine, engine::general_purpose::STANDARD};
1303 let s = expect_str(args.first())?;
1304 match STANDARD.decode(s) {
1305 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1306 Err(e) => Ok(err_v(Value::Str(format!("base64: {e}").into()))),
1307 }
1308 }
1309 ("crypto", "base64url_encode") => {
1313 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
1314 let data = expect_bytes(args.first())?;
1315 Ok(Value::Str(URL_SAFE_NO_PAD.encode(data).into()))
1316 }
1317 ("crypto", "base64url_decode") => {
1318 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
1319 let s = expect_str(args.first())?;
1320 match URL_SAFE_NO_PAD.decode(s) {
1321 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1322 Err(e) => Ok(err_v(Value::Str(format!("base64url: {e}").into()))),
1323 }
1324 }
1325 ("crypto", "hex_encode") => {
1326 let data = expect_bytes(args.first())?;
1327 Ok(Value::Str(hex::encode(data).into()))
1328 }
1329 ("crypto", "hex_decode") => {
1330 let s = expect_str(args.first())?;
1331 match hex::decode(s) {
1332 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1333 Err(e) => Ok(err_v(Value::Str(format!("hex: {e}").into()))),
1334 }
1335 }
1336 ("crypto", "base58_encode") => {
1340 let data = expect_bytes(args.first())?;
1341 Ok(Value::Str(bs58::encode(data).into_string().into()))
1342 }
1343 ("crypto", "base58_decode") => {
1344 let s = expect_str(args.first())?;
1345 match bs58::decode(s).into_vec() {
1346 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1347 Err(e) => Ok(err_v(Value::Str(format!("base58: {e}").into()))),
1348 }
1349 }
1350 ("crypto", "constant_time_eq") | ("crypto", "eq") => {
1351 use subtle::ConstantTimeEq;
1352 let a = expect_bytes(args.first())?;
1353 let b = expect_bytes(args.get(1))?;
1354 let eq = if a.len() == b.len() {
1363 a.ct_eq(b).into()
1364 } else {
1365 false
1366 };
1367 Ok(Value::Bool(eq))
1368 }
1369 ("crypto", "eq_str") => {
1372 use subtle::ConstantTimeEq;
1373 let a = expect_str(args.first())?;
1374 let b = expect_str(args.get(1))?;
1375 let eq = if a.len() == b.len() {
1376 a.as_bytes().ct_eq(b.as_bytes()).into()
1377 } else {
1378 false
1379 };
1380 Ok(Value::Bool(eq))
1381 }
1382
1383 ("crypto", "aes_gcm_seal") => Ok(aes_gcm_seal_impl(args)),
1393 ("crypto", "aes_gcm_open") => Ok(aes_gcm_open_impl(args)),
1394 ("crypto", "chacha20_poly1305_seal") => Ok(chacha20_seal_impl(args)),
1395 ("crypto", "chacha20_poly1305_open") => Ok(chacha20_open_impl(args)),
1396 ("crypto", "aes_cbc_encrypt_raw") => Ok(aes_cbc_impl(args, true)),
1397 ("crypto", "aes_cbc_decrypt_raw") => Ok(aes_cbc_impl(args, false)),
1398 ("crypto", "pbkdf2_sha256") => Ok(pbkdf2_sha256_impl(args)),
1399 ("crypto", "hkdf_sha256") => Ok(hkdf_sha256_impl(args)),
1400 ("crypto", "argon2id") => Ok(argon2id_impl(args)),
1401
1402 ("random", "seed") => {
1409 let s = args.first().ok_or("random.seed: missing arg")?.as_int();
1410 let mixed = splitmix64(s as u64).0;
1415 Ok(rng_value(mixed))
1416 }
1417 ("random", "int") => {
1418 let state = rng_decode(args.first())?;
1419 let lo = args.get(1).ok_or("random.int: missing lo")?.as_int();
1420 let hi = args.get(2).ok_or("random.int: missing hi")?.as_int();
1421 if hi < lo {
1422 return Err(format!(
1423 "random.int: hi ({hi}) must be >= lo ({lo})"));
1424 }
1425 let span = (hi as i128) - (lo as i128) + 1;
1426 let (raw, next_state) = splitmix64(state);
1427 let drawn = lo as i128 + (raw as u128 % span as u128) as i128;
1432 Ok(Value::Tuple(vec![
1433 Value::Int(drawn as i64),
1434 rng_value(next_state),
1435 ]))
1436 }
1437 ("random", "float") => {
1438 let state = rng_decode(args.first())?;
1439 let (raw, next_state) = splitmix64(state);
1440 let f = ((raw >> 11) as f64) / ((1u64 << 53) as f64);
1443 Ok(Value::Tuple(vec![Value::Float(f), rng_value(next_state)]))
1444 }
1445 ("random", "choose") => {
1446 let state = rng_decode(args.first())?;
1447 let xs = match args.get(1) {
1448 Some(Value::List(xs)) => xs,
1449 _ => return Err("random.choose: expected List".into()),
1450 };
1451 if xs.is_empty() {
1452 return Ok(Value::Variant {
1453 name: "None".into(), args: vec![],
1454 });
1455 }
1456 let (raw, next_state) = splitmix64(state);
1457 let idx = (raw as usize) % xs.len();
1458 let pick = xs[idx].clone();
1459 Ok(Value::Variant {
1460 name: "Some".into(),
1461 args: vec![Value::Tuple(vec![pick, rng_value(next_state)])],
1462 })
1463 }
1464
1465 ("parser", "char") => {
1470 let s = expect_str(args.first())?;
1471 if s.chars().count() != 1 {
1472 return Err(format!(
1473 "parser.char: expected 1-character string, got {s:?}"));
1474 }
1475 Ok(parser_node("Char", &[("ch", Value::Str(s.into()))]))
1476 }
1477 ("parser", "string") => {
1478 let s = expect_str(args.first())?;
1479 Ok(parser_node("String", &[("s", Value::Str(s.into()))]))
1480 }
1481 ("parser", "digit") => Ok(parser_node("Digit", &[])),
1482 ("parser", "alpha") => Ok(parser_node("Alpha", &[])),
1483 ("parser", "whitespace") => Ok(parser_node("Whitespace", &[])),
1484 ("parser", "eof") => Ok(parser_node("Eof", &[])),
1485 ("parser", "seq") => {
1486 let a = args.first().cloned()
1487 .ok_or_else(|| "parser.seq: missing first parser".to_string())?;
1488 let b = args.get(1).cloned()
1489 .ok_or_else(|| "parser.seq: missing second parser".to_string())?;
1490 Ok(parser_node("Seq", &[("a", a), ("b", b)]))
1491 }
1492 ("parser", "alt") => {
1493 let a = args.first().cloned()
1494 .ok_or_else(|| "parser.alt: missing first parser".to_string())?;
1495 let b = args.get(1).cloned()
1496 .ok_or_else(|| "parser.alt: missing second parser".to_string())?;
1497 Ok(parser_node("Alt", &[("a", a), ("b", b)]))
1498 }
1499 ("parser", "many") => {
1500 let p = args.first().cloned()
1501 .ok_or_else(|| "parser.many: missing inner parser".to_string())?;
1502 Ok(parser_node("Many", &[("p", p)]))
1503 }
1504 ("parser", "optional") => {
1505 let p = args.first().cloned()
1506 .ok_or_else(|| "parser.optional: missing inner parser".to_string())?;
1507 Ok(parser_node("Optional", &[("p", p)]))
1508 }
1509 ("parser", "map") => {
1513 let p = args.first().cloned()
1514 .ok_or_else(|| "parser.map: missing parser".to_string())?;
1515 let f = args.get(1).cloned()
1516 .ok_or_else(|| "parser.map: missing closure".to_string())?;
1517 Ok(parser_node("Map", &[("p", p), ("f", f)]))
1518 }
1519 ("parser", "and_then") => {
1520 let p = args.first().cloned()
1521 .ok_or_else(|| "parser.and_then: missing parser".to_string())?;
1522 let f = args.get(1).cloned()
1523 .ok_or_else(|| "parser.and_then: missing closure".to_string())?;
1524 Ok(parser_node("AndThen", &[("p", p), ("f", f)]))
1525 }
1526 ("regex", "compile") => {
1536 let pat = expect_str(args.first())?;
1537 match get_or_compile_regex(pat) {
1538 Ok(_) => Ok(ok_v(Value::Str(pat.into()))),
1539 Err(e) => Ok(err_v(Value::Str(e.into()))),
1540 }
1541 }
1542 ("regex", "is_match") => {
1543 let pat = expect_str(args.first())?;
1544 let s = expect_str(args.get(1))?;
1545 let re = get_or_compile_regex(pat).map_err(|e| format!("regex.is_match: {e}"))?;
1546 Ok(Value::Bool(re.is_match(s)))
1547 }
1548 ("regex", "is_match_str") => {
1553 let pat = expect_str(args.first())?;
1554 let s = expect_str(args.get(1))?;
1555 match get_or_compile_regex(pat) {
1556 Ok(re) => Ok(Value::Bool(re.is_match(s))),
1557 Err(_) => Ok(Value::Bool(false)),
1558 }
1559 }
1560 ("regex", "find") => {
1561 let pat = expect_str(args.first())?;
1562 let s = expect_str(args.get(1))?;
1563 let re = get_or_compile_regex(pat).map_err(|e| format!("regex.find: {e}"))?;
1564 match re.captures(s) {
1565 Some(caps) => Ok(Value::Variant {
1566 name: "Some".into(),
1567 args: vec![match_value(&caps)],
1568 }),
1569 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1570 }
1571 }
1572 ("regex", "find_all") => {
1573 let pat = expect_str(args.first())?;
1574 let s = expect_str(args.get(1))?;
1575 let re = get_or_compile_regex(pat).map_err(|e| format!("regex.find_all: {e}"))?;
1576 let items: std::collections::VecDeque<Value> = re.captures_iter(s).map(|caps| match_value(&caps)).collect();
1577 Ok(Value::List(items.into()))
1578 }
1579 ("regex", "replace") => {
1580 let pat = expect_str(args.first())?;
1581 let s = expect_str(args.get(1))?;
1582 let rep = expect_str(args.get(2))?;
1583 let re = get_or_compile_regex(pat).map_err(|e| format!("regex.replace: {e}"))?;
1584 Ok(Value::Str(re.replace(s, rep).into_owned().into()))
1585 }
1586 ("regex", "replace_all") => {
1587 let pat = expect_str(args.first())?;
1588 let s = expect_str(args.get(1))?;
1589 let rep = expect_str(args.get(2))?;
1590 let re = get_or_compile_regex(pat).map_err(|e| format!("regex.replace_all: {e}"))?;
1591 Ok(Value::Str(re.replace_all(s, rep).into_owned().into()))
1592 }
1593 ("datetime", "parse_iso") => {
1596 let s = expect_str(args.first())?;
1597 match chrono::DateTime::parse_from_rfc3339(s) {
1598 Ok(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1599 Err(e) => Ok(err_v(Value::Str(format!("parse_iso: {e}").into()))),
1600 }
1601 }
1602 ("datetime", "format_iso") => {
1603 let n = expect_int(args.first())?;
1604 Ok(Value::Str(format_iso(n).into()))
1605 }
1606 ("datetime", "parse") => {
1607 let s = expect_str(args.first())?;
1608 let fmt = expect_str(args.get(1))?;
1609 match chrono::NaiveDateTime::parse_from_str(s, fmt) {
1610 Ok(naive) => {
1611 use chrono::TimeZone;
1612 match chrono::Utc.from_local_datetime(&naive).single() {
1613 Some(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1614 None => Ok(err_v(Value::Str("parse: ambiguous local time".into()))),
1615 }
1616 }
1617 Err(e) => Ok(err_v(Value::Str(format!("parse: {e}").into()))),
1618 }
1619 }
1620 ("datetime", "format") => {
1621 let n = expect_int(args.first())?;
1622 let fmt = expect_str(args.get(1))?;
1623 let dt = chrono_from_instant(n);
1624 Ok(Value::Str(dt.format(fmt).to_string().into()))
1625 }
1626 ("datetime", "to_components") => {
1627 let n = expect_int(args.first())?;
1628 let tz = match parse_tz_arg(args.get(1)) {
1629 Ok(t) => t,
1630 Err(e) => return Ok(err_v(Value::Str(e.into()))),
1631 };
1632 match resolve_tz_to_components(n, &tz) {
1633 Ok(rec) => Ok(ok_v(rec)),
1634 Err(e) => Ok(err_v(Value::Str(e.into()))),
1635 }
1636 }
1637 ("datetime", "from_components") => {
1638 let rec = match args.first() {
1639 Some(Value::Record { fields: r, .. }) => r.clone(),
1640 _ => return Err("from_components: expected DateTime record".into()),
1641 };
1642 match instant_from_components(&rec) {
1643 Ok(n) => Ok(ok_v(Value::Int(n))),
1644 Err(e) => Ok(err_v(Value::Str(e.into()))),
1645 }
1646 }
1647 ("datetime", "add") => {
1648 let a = expect_int(args.first())?;
1649 let d = expect_int(args.get(1))?;
1650 Ok(Value::Int(a.saturating_add(d)))
1651 }
1652 ("datetime", "diff") => {
1653 let a = expect_int(args.first())?;
1654 let b = expect_int(args.get(1))?;
1655 Ok(Value::Int(a.saturating_sub(b)))
1656 }
1657 ("datetime", "duration_seconds") => {
1658 let s = expect_float(args.first())?;
1659 let nanos = (s * 1_000_000_000.0) as i64;
1660 Ok(Value::Int(nanos))
1661 }
1662 ("datetime", "duration_minutes") => {
1663 let m = expect_int(args.first())?;
1664 Ok(Value::Int(m.saturating_mul(60_000_000_000)))
1665 }
1666 ("datetime", "duration_days") => {
1667 let d = expect_int(args.first())?;
1668 Ok(Value::Int(d.saturating_mul(86_400_000_000_000)))
1669 }
1670 ("datetime", "before") => {
1672 let a = expect_int(args.first())?;
1673 let b = expect_int(args.get(1))?;
1674 Ok(Value::Bool(a < b))
1675 }
1676 ("datetime", "after") => {
1677 let a = expect_int(args.first())?;
1678 let b = expect_int(args.get(1))?;
1679 Ok(Value::Bool(a > b))
1680 }
1681 ("datetime", "compare") => {
1682 let a = expect_int(args.first())?;
1683 let b = expect_int(args.get(1))?;
1684 Ok(Value::Int(a.cmp(&b) as i64))
1685 }
1686 ("duration", "millis") => Ok(Value::Int(expect_int(args.first())? / 1_000_000)),
1689 ("duration", "seconds") => Ok(Value::Int(expect_int(args.first())? / 1_000_000_000)),
1690 ("duration", "minutes") => Ok(Value::Int(expect_int(args.first())? / 60_000_000_000)),
1691 ("duration", "hours") => Ok(Value::Int(expect_int(args.first())? / 3_600_000_000_000)),
1692 ("duration", "days") => Ok(Value::Int(expect_int(args.first())? / 86_400_000_000_000)),
1693
1694 ("regex", "split") => {
1695 let pat = expect_str(args.first())?;
1696 let s = expect_str(args.get(1))?;
1697 let re = get_or_compile_regex(pat).map_err(|e| format!("regex.split: {e}"))?;
1698 let parts: std::collections::VecDeque<Value> = re.split(s).map(|p| Value::Str(p.into())).collect();
1699 Ok(Value::List(parts.into()))
1700 }
1701
1702 ("http", "with_header") => {
1705 let req = expect_record_pure(args.first())?.clone();
1706 let k = expect_str(args.get(1))?;
1707 let v = expect_str(args.get(2))?;
1708 Ok(Value::record_interned(http_set_header(req, k, v)))
1709 }
1710 ("http", "with_auth") => {
1711 let req = expect_record_pure(args.first())?.clone();
1712 let scheme = expect_str(args.get(1))?;
1713 let token = expect_str(args.get(2))?;
1714 let value = format!("{scheme} {token}");
1715 Ok(Value::record_interned(http_set_header(req, "Authorization", &value)))
1716 }
1717 ("http", "with_query") => {
1718 let req = expect_record_pure(args.first())?.clone();
1719 let params = match args.get(1) {
1720 Some(Value::Map(m)) => m.clone(),
1721 Some(other) => return Err(format!(
1722 "http.with_query: params must be Map[Str, Str], got {other:?}")),
1723 None => return Err("http.with_query: missing params argument".into()),
1724 };
1725 Ok(Value::record_interned(http_append_query(req, ¶ms)))
1726 }
1727 ("http", "with_timeout_ms") => {
1728 let req = expect_record_pure(args.first())?.clone();
1729 let ms = expect_int(args.get(1))?;
1730 let mut out = req;
1731 out.insert("timeout_ms".into(), Value::Variant {
1732 name: "Some".into(),
1733 args: vec![Value::Int(ms)],
1734 });
1735 Ok(Value::record_interned(out))
1736 }
1737 ("http", "json_body") => {
1738 let resp = expect_record_pure(args.first())?;
1739 let body = match resp.get("body") {
1740 Some(Value::Bytes(b)) => b.clone(),
1741 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1742 };
1743 let s = match std::str::from_utf8(&body) {
1744 Ok(s) => s,
1745 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1746 };
1747 match serde_json::from_str::<serde_json::Value>(s) {
1748 Ok(j) => Ok(ok_v(Value::from_json(&j))),
1749 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1750 }
1751 }
1752 ("http", "json_body_typed") => {
1760 let resp = expect_record_pure(args.first())?;
1761 let required = required_field_names(args.get(1))?;
1762 let schema = extract_type_schema(args.get(2));
1763 let body = match resp.get("body") {
1764 Some(Value::Bytes(b)) => b.clone(),
1765 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1766 };
1767 let s = match std::str::from_utf8(&body) {
1768 Ok(s) => s,
1769 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1770 };
1771 match serde_json::from_str::<serde_json::Value>(s) {
1772 Ok(j) => {
1773 if let Err(e) = check_required_fields(&j, &required) {
1774 return Ok(http_decode_err_pure(e));
1775 }
1776 if let Err(e) = validate_field_types(&j, &schema) {
1777 return Ok(http_decode_err_pure(e));
1778 }
1779 Ok(ok_v(apply_option_wrapping(json_to_value(&j), &j, &schema)))
1780 }
1781 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1782 }
1783 }
1784 ("http", "text_body") => {
1785 let resp = expect_record_pure(args.first())?;
1786 let body = match resp.get("body") {
1787 Some(Value::Bytes(b)) => b.clone(),
1788 _ => return Err("http.text_body: HttpResponse.body must be Bytes".into()),
1789 };
1790 match String::from_utf8(body) {
1791 Ok(s) => Ok(ok_v(Value::Str(s.into()))),
1792 Err(e) => Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1793 }
1794 }
1795
1796 ("cli", "flag") => {
1800 let name = expect_str(args.first())?;
1801 let short = opt_str(args.get(1));
1802 let help = expect_str(args.get(2))?;
1803 Ok(value_from_json(crate::cli::flag_spec(name, short.as_deref(), help)))
1804 }
1805 ("cli", "option") => {
1806 let name = expect_str(args.first())?;
1807 let short = opt_str(args.get(1));
1808 let help = expect_str(args.get(2))?;
1809 let default = opt_str(args.get(3));
1810 Ok(value_from_json(crate::cli::option_spec(name, short.as_deref(), help, default.as_deref())))
1811 }
1812 ("cli", "positional") => {
1813 let name = expect_str(args.first())?;
1814 let help = expect_str(args.get(1))?;
1815 let required = expect_bool(args.get(2))?;
1816 Ok(value_from_json(crate::cli::positional_spec(name, help, required)))
1817 }
1818 ("cli", "spec") => {
1819 let name = expect_str(args.first())?;
1820 let help = expect_str(args.get(1))?;
1821 let arg_specs: Vec<serde_json::Value> = expect_list(args.get(2))?
1822 .iter().map(value_to_json).collect();
1823 let subs: Vec<serde_json::Value> = expect_list(args.get(3))?
1824 .iter().map(value_to_json).collect();
1825 Ok(value_from_json(crate::cli::build_spec(name, help, arg_specs, subs)))
1826 }
1827 ("cli", "parse") => {
1828 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1829 let argv: Vec<String> = expect_list(args.get(1))?
1830 .iter().map(|v| match v {
1831 Value::Str(s) => Ok(s.to_string()),
1832 other => Err(format!("cli.parse: argv must be List[Str], got {other:?}")),
1833 }).collect::<Result<_, _>>()?;
1834 match crate::cli::parse(&spec, &argv) {
1835 Ok(parsed) => Ok(ok_v(value_from_json(parsed))),
1836 Err(msg) => Ok(err_v(Value::Str(msg.into()))),
1837 }
1838 }
1839 ("cli", "envelope") => {
1840 let ok = expect_bool(args.first())?;
1841 let cmd = expect_str(args.get(1))?;
1842 let data = value_to_json(args.get(2).unwrap_or(&Value::Unit));
1843 Ok(value_from_json(crate::cli::envelope(ok, cmd, data)))
1844 }
1845 ("cli", "describe") => {
1846 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1847 Ok(value_from_json(crate::cli::describe(&spec)))
1848 }
1849 ("cli", "help") => {
1850 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1851 Ok(Value::Str(crate::cli::help_text(&spec).into()))
1852 }
1853
1854 ("arrow", op) => match crate::arrow::dispatch(op, args) {
1856 Some(r) => r,
1857 None => Err(format!("unknown pure builtin: arrow.{op}")),
1858 },
1859 #[cfg(feature = "df")]
1863 ("df", op) => match crate::df::dispatch(op, args) {
1864 Some(r) => r,
1865 None => Err(format!("unknown pure builtin: df.{op}")),
1866 },
1867 #[cfg(not(feature = "df"))]
1868 ("df", op) => Err(format!(
1869 "df.{op}: this build was compiled without the `df` feature; \
1870 Polars-backed dataframe query ops are unavailable"
1871 )),
1872
1873 ("decimal", "decimal") => {
1880 let coef = expect_int(args.first())?;
1881 let exp = expect_int(args.get(1))?;
1882 Ok(make_decimal(coef, exp))
1883 }
1884 ("decimal", "zero") => Ok(make_decimal(0, 0)),
1885 ("decimal", "one") => Ok(make_decimal(1, 0)),
1886 ("decimal", "from_int") => {
1887 Ok(make_decimal(expect_int(args.first())?, 0))
1888 }
1889 ("decimal", "pow10") => {
1890 Ok(Value::Int(decimal_pow10(expect_int(args.first())?)?))
1891 }
1892 ("decimal", "add") => {
1893 let (ca, ea) = expect_decimal(args.first())?;
1894 let (cb, eb) = expect_decimal(args.get(1))?;
1895 let (a2, b2, e) = decimal_align(ca, ea, cb, eb)?;
1896 Ok(make_decimal(
1897 a2.checked_add(b2).ok_or("decimal.add: overflow")?, e))
1898 }
1899 ("decimal", "sub") => {
1900 let (ca, ea) = expect_decimal(args.first())?;
1901 let (cb, eb) = expect_decimal(args.get(1))?;
1902 let (a2, b2, e) = decimal_align(ca, ea, cb, eb)?;
1903 Ok(make_decimal(
1904 a2.checked_sub(b2).ok_or("decimal.sub: overflow")?, e))
1905 }
1906 ("decimal", "mul") => {
1907 let (ca, ea) = expect_decimal(args.first())?;
1908 let (cb, eb) = expect_decimal(args.get(1))?;
1909 Ok(make_decimal(
1910 ca.checked_mul(cb).ok_or("decimal.mul: overflow")?,
1911 ea.checked_add(eb).ok_or("decimal.mul: exponent overflow")?,
1912 ))
1913 }
1914 ("decimal", "compare") => {
1915 let (ca, ea) = expect_decimal(args.first())?;
1916 let (cb, eb) = expect_decimal(args.get(1))?;
1917 let (a2, b2, _) = decimal_align(ca, ea, cb, eb)?;
1918 Ok(Value::Int(if a2 < b2 { -1 } else if a2 > b2 { 1 } else { 0 }))
1919 }
1920 ("decimal", "is_zero") => {
1921 let (c, _) = expect_decimal(args.first())?;
1922 Ok(Value::Bool(c == 0))
1923 }
1924 ("decimal", "is_positive") => {
1925 let (c, _) = expect_decimal(args.first())?;
1926 Ok(Value::Bool(c > 0))
1927 }
1928 ("decimal", "is_negative") => {
1929 let (c, _) = expect_decimal(args.first())?;
1930 Ok(Value::Bool(c < 0))
1931 }
1932 ("decimal", "negate") => {
1933 let (c, e) = expect_decimal(args.first())?;
1934 Ok(make_decimal(-c, e))
1935 }
1936 ("decimal", "abs") => {
1937 let (c, e) = expect_decimal(args.first())?;
1938 Ok(make_decimal(c.abs(), e))
1939 }
1940 ("decimal", "normalize") => {
1941 let (mut c, mut e) = expect_decimal(args.first())?;
1942 if c == 0 { return Ok(make_decimal(0, 0)); }
1943 while c % 10 == 0 { c /= 10; e += 1; }
1944 Ok(make_decimal(c, e))
1945 }
1946 ("decimal", "round_to") => {
1947 let (c, e) = expect_decimal(args.first())?;
1948 let target_e = expect_int(args.get(1))?;
1949 let mode = expect_str(args.get(2))?;
1950 Ok(make_decimal(decimal_round(c, e, target_e, mode)?, target_e))
1951 }
1952 ("decimal", "to_str") => {
1953 let (c, e) = expect_decimal(args.first())?;
1954 Ok(Value::Str(decimal_to_str(c, e)?.into()))
1955 }
1956
1957 _ => Err(format!("unknown pure builtin: {kind}.{op}")),
1958 }
1959}
1960
1961fn expect_decimal(v: Option<&Value>) -> Result<(i64, i64), String> {
1965 match v {
1966 Some(Value::Record { fields, .. }) => {
1967 let coef = match fields.get("coefficient") {
1968 Some(Value::Int(n)) => *n,
1969 _ => return Err("decimal: missing or invalid 'coefficient' field".into()),
1970 };
1971 let exp = match fields.get("exponent") {
1972 Some(Value::Int(n)) => *n,
1973 _ => return Err("decimal: missing or invalid 'exponent' field".into()),
1974 };
1975 Ok((coef, exp))
1976 }
1977 Some(other) => Err(format!("decimal: expected {{ coefficient, exponent }} record, got {other:?}")),
1978 None => Err("decimal: missing argument".into()),
1979 }
1980}
1981
1982fn make_decimal(coefficient: i64, exponent: i64) -> Value {
1984 let mut fields = indexmap::IndexMap::new();
1985 fields.insert("coefficient".into(), Value::Int(coefficient));
1986 fields.insert("exponent".into(), Value::Int(exponent));
1987 Value::record_interned(fields)
1988}
1989
1990fn decimal_pow10(n: i64) -> Result<i64, String> {
1992 if n < 0 { return Err(format!("decimal.pow10: negative exponent {n}")); }
1993 if n > 18 { return Err(format!("decimal.pow10: exponent {n} exceeds max (18)")); }
1994 Ok(10i64.pow(n as u32))
1995}
1996
1997fn decimal_align(ca: i64, ea: i64, cb: i64, eb: i64) -> Result<(i64, i64, i64), String> {
2000 if ea == eb { return Ok((ca, cb, ea)); }
2001 if ea > eb {
2002 let scale = decimal_pow10(ea - eb)?;
2003 let ca2 = ca.checked_mul(scale)
2004 .ok_or_else(|| format!("decimal: overflow aligning (shift {})", ea - eb))?;
2005 Ok((ca2, cb, eb))
2006 } else {
2007 let scale = decimal_pow10(eb - ea)?;
2008 let cb2 = cb.checked_mul(scale)
2009 .ok_or_else(|| format!("decimal: overflow aligning (shift {})", eb - ea))?;
2010 Ok((ca, cb2, ea))
2011 }
2012}
2013
2014fn decimal_round(c: i64, e: i64, target_e: i64, mode: &str) -> Result<i64, String> {
2019 if e >= target_e {
2020 let shift = e - target_e;
2022 let scale = decimal_pow10(shift)?;
2023 return c.checked_mul(scale)
2024 .ok_or_else(|| format!("decimal.round_to: overflow scaling (shift {shift})"));
2025 }
2026 let shift = target_e - e;
2028 let divisor = decimal_pow10(shift)?;
2029 let q = c / divisor;
2030 let r = c % divisor; if r == 0 { return Ok(q); }
2033
2034 let abs_r = r.abs();
2035 let positive = r > 0; let rounded = match mode {
2038 "Down" => q,
2039 "Up" => if positive { q + 1 } else { q - 1 },
2040 "Floor" => if positive { q } else { q - 1 },
2041 "Ceiling" => if positive { q + 1 } else { q },
2042 "HalfUp" => {
2043 if abs_r * 2 >= divisor {
2044 if positive { q + 1 } else { q - 1 }
2045 } else { q }
2046 }
2047 "HalfDown" => {
2048 if abs_r * 2 > divisor {
2049 if positive { q + 1 } else { q - 1 }
2050 } else { q }
2051 }
2052 "HalfEven" => {
2053 if abs_r * 2 > divisor {
2054 if positive { q + 1 } else { q - 1 }
2055 } else if abs_r * 2 == divisor {
2056 if q % 2 == 0 { q } else { if positive { q + 1 } else { q - 1 } }
2058 } else { q }
2059 }
2060 other => return Err(format!(
2061 "decimal.round_to: unknown rounding mode {other:?}; \
2062 valid modes: HalfUp HalfDown HalfEven Down Up Ceiling Floor")),
2063 };
2064 Ok(rounded)
2065}
2066
2067fn decimal_to_str(c: i64, e: i64) -> Result<String, String> {
2070 if e == 0 { return Ok(c.to_string()); }
2071 if e > 0 {
2072 let scale = decimal_pow10(e)?;
2073 let val = c.checked_mul(scale)
2074 .ok_or("decimal.to_str: overflow")?;
2075 return Ok(val.to_string());
2076 }
2077 let scale = decimal_pow10(-e)?;
2079 let sign = if c < 0 { "-" } else { "" };
2080 let abs_c = c.abs();
2081 let int_part = abs_c / scale;
2082 let frac_part = abs_c % scale;
2083 let decimal_places = (-e) as usize;
2084 Ok(format!("{sign}{int_part}.{frac_part:0>decimal_places$}"))
2085}
2086
2087fn opt_str(arg: Option<&Value>) -> Option<String> {
2091 match arg {
2092 Some(Value::Variant { name, args }) if name == "Some" => {
2093 args.first().and_then(|v| match v {
2094 Value::Str(s) => Some(s.to_string()),
2095 _ => None,
2096 })
2097 }
2098 _ => None,
2099 }
2100}
2101
2102fn value_from_json(v: serde_json::Value) -> Value { Value::from_json(&v) }
2103
2104fn regex_cache() -> &'static Mutex<HashMap<String, regex::Regex>> {
2109 static CACHE: OnceLock<Mutex<HashMap<String, regex::Regex>>> = OnceLock::new();
2110 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
2111}
2112
2113fn get_or_compile_regex(pattern: &str) -> Result<regex::Regex, String> {
2114 let cache = regex_cache();
2115 {
2116 let guard = cache.lock().unwrap();
2117 if let Some(re) = guard.get(pattern) {
2118 return Ok(re.clone());
2119 }
2120 }
2121 let re = regex::Regex::new(pattern).map_err(|e| format!("invalid regex: {e}"))?;
2122 let mut guard = cache.lock().unwrap();
2123 guard.insert(pattern.to_string(), re.clone());
2124 Ok(re)
2125}
2126
2127fn match_value(caps: ®ex::Captures) -> Value {
2131 let m0 = caps.get(0).expect("regex match always has group 0");
2132 let mut rec = indexmap::IndexMap::new();
2133 rec.insert("text".into(), Value::Str(m0.as_str().into()));
2134 rec.insert("start".into(), Value::Int(m0.start() as i64));
2135 rec.insert("end".into(), Value::Int(m0.end() as i64));
2136 let groups: std::collections::VecDeque<Value> = (1..caps.len())
2137 .map(|i| {
2138 Value::Str(
2139 caps.get(i)
2140 .map(|m| m.as_str())
2141 .unwrap_or_default()
2142 .into(),
2143 )
2144 })
2145 .collect();
2146 rec.insert("groups".into(), Value::List(groups.into()));
2147 Value::record_dynamic(rec)
2148}
2149
2150fn expect_map(v: Option<&Value>) -> Result<&BTreeMap<MapKey, Value>, String> {
2151 match v {
2152 Some(Value::Map(m)) => Ok(m),
2153 other => Err(format!("expected Map, got {other:?}")),
2154 }
2155}
2156
2157fn expect_set(v: Option<&Value>) -> Result<&BTreeSet<MapKey>, String> {
2158 match v {
2159 Some(Value::Set(s)) => Ok(s),
2160 other => Err(format!("expected Set, got {other:?}")),
2161 }
2162}
2163
2164fn unpack_matrix(v: &Value) -> Result<(usize, usize, Vec<f64>), String> {
2168 if let Value::F64Array { rows, cols, data } = v {
2169 return Ok((*rows as usize, *cols as usize, data.clone()));
2170 }
2171 let rec = match v {
2172 Value::Record { fields: r, .. } => r,
2173 other => return Err(format!("expected matrix, got {other:?}")),
2174 };
2175 let rows = match rec.get("rows") {
2176 Some(Value::Int(n)) => *n as usize,
2177 _ => return Err("matrix: missing/invalid `rows`".into()),
2178 };
2179 let cols = match rec.get("cols") {
2180 Some(Value::Int(n)) => *n as usize,
2181 _ => return Err("matrix: missing/invalid `cols`".into()),
2182 };
2183 let data = match rec.get("data") {
2184 Some(Value::List(items)) => {
2185 let mut out = Vec::with_capacity(items.len());
2186 for it in items {
2187 out.push(match it {
2188 Value::Float(f) => *f,
2189 Value::Int(n) => *n as f64,
2190 other => return Err(format!("matrix data: not numeric, got {other:?}")),
2191 });
2192 }
2193 out
2194 }
2195 _ => return Err("matrix: missing/invalid `data`".into()),
2196 };
2197 if data.len() != rows * cols {
2198 return Err(format!("matrix: data len {} != {rows}*{cols}", data.len()));
2199 }
2200 Ok((rows, cols, data))
2201}
2202
2203fn expect_bytes(v: Option<&Value>) -> Result<&Vec<u8>, String> {
2204 match v {
2205 Some(Value::Bytes(b)) => Ok(b),
2206 Some(other) => Err(format!("expected Bytes, got {other:?}")),
2207 None => Err("missing argument".into()),
2208 }
2209}
2210
2211fn first_arg(args: &[Value]) -> Result<&Value, String> {
2212 args.first().ok_or_else(|| "missing argument".into())
2213}
2214
2215fn tuple_index(v: &Value, i: usize) -> Result<Value, String> {
2216 match v {
2217 Value::Tuple(items) => items.get(i).cloned()
2218 .ok_or_else(|| format!("tuple index {i} out of range (len={})", items.len())),
2219 other => Err(format!("expected Tuple, got {other:?}")),
2220 }
2221}
2222
2223pub(crate) fn str_arg(v: Option<&Value>) -> Result<&SmolStr, String> {
2226 match v {
2227 Some(Value::Str(s)) => Ok(s),
2228 Some(other) => Err(format!("expected Str, got {other:?}")),
2229 None => Err("missing argument".into()),
2230 }
2231}
2232
2233thread_local! {
2234 static CP_CURSOR: std::cell::RefCell<Option<(SmolStr, usize, usize)>> =
2245 const { std::cell::RefCell::new(None) };
2246}
2247
2248pub(crate) fn cp_to_byte(s: &SmolStr, cp: usize) -> usize {
2257 CP_CURSOR.with(|cur| {
2258 let mut cur = cur.borrow_mut();
2259 let cached = match &*cur {
2260 Some((cached, ccp, cbyte))
2261 if cached.as_ptr() == s.as_ptr() && cached.len() == s.len() =>
2262 {
2263 Some((*ccp, *cbyte))
2264 }
2265 _ => None,
2266 };
2267 let byte = match cached {
2268 Some((ccp, cbyte)) if ccp <= cp => s[cbyte..]
2269 .char_indices()
2270 .nth(cp - ccp)
2271 .map(|(b, _)| cbyte + b)
2272 .unwrap_or(s.len()),
2273 Some((ccp, cbyte)) if ccp - cp < cp => {
2276 let mut b = cbyte;
2277 for _ in 0..(ccp - cp) {
2278 b -= 1;
2279 while b > 0 && !s.is_char_boundary(b) {
2280 b -= 1;
2281 }
2282 }
2283 b
2284 }
2285 _ => s.char_indices().nth(cp).map(|(b, _)| b).unwrap_or(s.len()),
2286 };
2287 if byte < s.len() {
2288 *cur = Some((s.clone(), cp, byte));
2289 }
2290 byte
2291 })
2292}
2293
2294pub(crate) fn remember_cursor(s: &SmolStr, cp: usize, byte: usize) {
2296 if byte < s.len() {
2297 CP_CURSOR.with(|cur| *cur.borrow_mut() = Some((s.clone(), cp, byte)));
2298 }
2299}
2300
2301pub(crate) fn expect_str(v: Option<&Value>) -> Result<&str, String> {
2307 match v {
2308 Some(Value::Str(s)) => Ok(s.as_str()),
2309 Some(other) => Err(format!("expected Str, got {other:?}")),
2310 None => Err("missing argument".into()),
2311 }
2312}
2313
2314pub(crate) fn expect_int(v: Option<&Value>) -> Result<i64, String> {
2315 match v {
2316 Some(Value::Int(n)) => Ok(*n),
2317 Some(other) => Err(format!("expected Int, got {other:?}")),
2318 None => Err("missing argument".into()),
2319 }
2320}
2321
2322fn expect_float(v: Option<&Value>) -> Result<f64, String> {
2323 match v {
2324 Some(Value::Float(f)) => Ok(*f),
2325 Some(other) => Err(format!("expected Float, got {other:?}")),
2326 None => Err("missing argument".into()),
2327 }
2328}
2329
2330pub(crate) fn expect_list(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
2331 match v {
2332 Some(Value::List(xs)) => Ok(xs),
2333 Some(other) => Err(format!("expected List, got {other:?}")),
2334 None => Err("missing argument".into()),
2335 }
2336}
2337
2338fn expect_bool(v: Option<&Value>) -> Result<bool, String> {
2339 match v {
2340 Some(Value::Bool(b)) => Ok(*b),
2341 Some(other) => Err(format!("expected Bool, got {other:?}")),
2342 None => Err("missing argument".into()),
2343 }
2344}
2345
2346fn expect_deque(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
2347 match v {
2348 Some(Value::Deque(d)) => Ok(d),
2349 Some(other) => Err(format!("expected Deque, got {other:?}")),
2350 None => Err("missing argument".into()),
2351 }
2352}
2353
2354pub(crate) fn some(v: Value) -> Value { Value::Variant { name: "Some".into(), args: vec![v] } }
2355pub(crate) fn none() -> Value { Value::Variant { name: "None".into(), args: Vec::new() } }
2356fn ok_v(v: Value) -> Value { Value::Variant { name: "Ok".into(), args: vec![v] } }
2357fn err_v(v: Value) -> Value { Value::Variant { name: "Err".into(), args: vec![v] } }
2358
2359fn parser_node(kind: &str, fields: &[(&str, Value)]) -> Value {
2367 let mut r = indexmap::IndexMap::new();
2368 r.insert("kind".into(), Value::Str(kind.into()));
2369 for (k, v) in fields {
2370 r.insert((*k).into(), v.clone());
2371 }
2372 Value::record_dynamic(r)
2373}
2374
2375fn splitmix64(state: u64) -> (u64, u64) {
2386 let next = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
2387 let mut z = next;
2388 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2389 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
2390 let z = z ^ (z >> 31);
2391 (z, next)
2392}
2393
2394fn rng_value(state: u64) -> Value {
2398 let mut fields = indexmap::IndexMap::new();
2399 fields.insert("state".into(), Value::Int(state as i64));
2400 Value::record_dynamic(fields)
2401}
2402
2403fn rng_decode(v: Option<&Value>) -> Result<u64, String> {
2405 let rec = match v {
2406 Some(Value::Record { fields: r, .. }) => r,
2407 Some(other) => return Err(format!("expected Rng, got {other:?}")),
2408 None => return Err("missing Rng arg".into()),
2409 };
2410 match rec.get("state") {
2411 Some(Value::Int(n)) => Ok(*n as u64),
2412 _ => Err("malformed Rng: missing `state :: Int`".into()),
2413 }
2414}
2415
2416fn expect_record_pure(v: Option<&Value>) -> Result<&indexmap::IndexMap<smol_str::SmolStr, Value>, String> {
2419 match v {
2420 Some(Value::Record { fields: r, .. }) => Ok(r),
2421 Some(other) => Err(format!("expected Record, got {other:?}")),
2422 None => Err("missing Record argument".into()),
2423 }
2424}
2425
2426fn http_decode_err_pure(msg: String) -> Value {
2427 let inner = Value::Variant {
2428 name: "DecodeError".into(),
2429 args: vec![Value::Str(msg.into())],
2430 };
2431 err_v(inner)
2432}
2433
2434fn http_set_header(
2439 mut req: indexmap::IndexMap<smol_str::SmolStr, Value>,
2440 name: &str,
2441 value: &str,
2442) -> indexmap::IndexMap<smol_str::SmolStr, Value> {
2443 use lex_bytecode::MapKey;
2444 let mut headers = match req.shift_remove("headers") {
2445 Some(Value::Map(m)) => m,
2446 _ => std::collections::BTreeMap::new(),
2447 };
2448 let key = MapKey::Str(name.to_lowercase());
2449 let lowered = name.to_lowercase();
2452 headers.retain(|k, _| match k {
2453 MapKey::Str(s) => s.to_lowercase() != lowered,
2454 _ => true,
2455 });
2456 headers.insert(key, Value::Str(value.into()));
2457 req.insert("headers".into(), Value::Map(headers));
2458 req
2459}
2460
2461fn http_append_query(
2467 mut req: indexmap::IndexMap<smol_str::SmolStr, Value>,
2468 params: &std::collections::BTreeMap<lex_bytecode::MapKey, Value>,
2469) -> indexmap::IndexMap<smol_str::SmolStr, Value> {
2470 use lex_bytecode::MapKey;
2471 let url = match req.get("url") {
2472 Some(Value::Str(s)) => s.clone(),
2473 _ => return req,
2474 };
2475 let mut pieces = Vec::new();
2476 for (k, v) in params {
2477 let kk = match k { MapKey::Str(s) => s.to_string(), _ => continue };
2478 let vv = match v { Value::Str(s) => s.to_string(), _ => continue };
2479 pieces.push(format!("{}={}", url_encode(&kk), url_encode(&vv)));
2480 }
2481 if pieces.is_empty() { return req; }
2482 let sep = if url.contains('?') { '&' } else { '?' };
2483 let new_url = format!("{url}{sep}{}", pieces.join("&"));
2484 req.insert("url".into(), Value::Str(new_url.into()));
2485 req
2486}
2487
2488fn url_encode(s: &str) -> String {
2493 let mut out = String::with_capacity(s.len());
2494 for b in s.bytes() {
2495 match b {
2496 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
2497 out.push(b as char);
2498 }
2499 _ => out.push_str(&format!("%{:02X}", b)),
2500 }
2501 }
2502 out
2503}
2504
2505fn value_to_json(v: &Value) -> serde_json::Value { v.to_json() }
2506
2507fn unwrap_toml_datetime_markers(v: &mut serde_json::Value) {
2515 use serde_json::Value as J;
2516 match v {
2517 J::Object(map) => {
2518 if map.len() == 1 {
2522 if let Some(J::String(s)) = map.get("$__toml_private_datetime") {
2523 let s = s.clone();
2524 *v = J::String(s);
2525 return;
2526 }
2527 }
2528 for (_, child) in map.iter_mut() {
2529 unwrap_toml_datetime_markers(child);
2530 }
2531 }
2532 J::Array(items) => {
2533 for item in items.iter_mut() {
2534 unwrap_toml_datetime_markers(item);
2535 }
2536 }
2537 _ => {}
2538 }
2539}
2540
2541fn json_to_value(v: &serde_json::Value) -> Value { Value::from_json(v) }
2542
2543fn json_to_adt(v: &serde_json::Value) -> Value {
2549 use serde_json::Value as J;
2550 let variant = |name: &str, args: Vec<Value>| Value::Variant { name: name.into(), args };
2551 match v {
2552 J::Null => variant("JNull", vec![]),
2553 J::Bool(b) => variant("JBool", vec![Value::Bool(*b)]),
2554 J::Number(n) => {
2555 if let Some(i) = n.as_i64() {
2556 variant("JInt", vec![Value::Int(i)])
2557 } else {
2558 variant("JFloat", vec![Value::Float(n.as_f64().unwrap_or(f64::NAN))])
2560 }
2561 }
2562 J::String(s) => variant("JStr", vec![Value::Str(s.as_str().into())]),
2563 J::Array(xs) => variant(
2564 "JList",
2565 vec![Value::List(xs.iter().map(json_to_adt).collect())],
2566 ),
2567 J::Object(m) => variant(
2568 "JObj",
2569 vec![Value::List(
2570 m.iter()
2571 .map(|(k, val)| {
2572 Value::Tuple(vec![Value::Str(k.as_str().into()), json_to_adt(val)])
2573 })
2574 .collect(),
2575 )],
2576 ),
2577 }
2578}
2579
2580fn adt_to_json(v: &Value) -> Result<serde_json::Value, String> {
2586 let (name, args) = match v {
2587 Value::Variant { name, args } => (name.as_str(), args),
2588 other => return Err(format!("json.encode: expected Json, got {other:?}")),
2589 };
2590 fn arg0<'a>(name: &str, args: &'a [Value]) -> Result<&'a Value, String> {
2591 args.first().ok_or_else(|| format!("json.encode: {name} missing payload"))
2592 }
2593 match name {
2594 "JNull" => Ok(serde_json::Value::Null),
2595 "JBool" => match arg0(name, args)? {
2596 Value::Bool(b) => Ok(serde_json::Value::Bool(*b)),
2597 o => Err(format!("json.encode: JBool payload not a Bool: {o:?}")),
2598 },
2599 "JInt" => match arg0(name, args)? {
2600 Value::Int(i) => Ok(serde_json::Value::Number((*i).into())),
2601 o => Err(format!("json.encode: JInt payload not an Int: {o:?}")),
2602 },
2603 "JFloat" => match arg0(name, args)? {
2604 Value::Float(f) => Ok(serde_json::Number::from_f64(*f)
2605 .map(serde_json::Value::Number)
2606 .unwrap_or(serde_json::Value::Null)),
2609 o => Err(format!("json.encode: JFloat payload not a Float: {o:?}")),
2610 },
2611 "JStr" => match arg0(name, args)? {
2612 Value::Str(s) => Ok(serde_json::Value::String(s.to_string())),
2613 o => Err(format!("json.encode: JStr payload not a Str: {o:?}")),
2614 },
2615 "JList" => match arg0(name, args)? {
2616 Value::List(xs) => {
2617 let mut out = Vec::with_capacity(xs.len());
2618 for x in xs.iter() {
2619 out.push(adt_to_json(x)?);
2620 }
2621 Ok(serde_json::Value::Array(out))
2622 }
2623 o => Err(format!("json.encode: JList payload not a List: {o:?}")),
2624 },
2625 "JObj" => match arg0(name, args)? {
2626 Value::List(pairs) => {
2627 let mut map = serde_json::Map::new();
2628 for pair in pairs.iter() {
2629 match pair {
2630 Value::Tuple(kv) if kv.len() == 2 => {
2631 let k = match &kv[0] {
2632 Value::Str(s) => s.to_string(),
2633 o => return Err(format!("json.encode: JObj key not a Str: {o:?}")),
2634 };
2635 map.insert(k, adt_to_json(&kv[1])?);
2636 }
2637 o => return Err(format!("json.encode: JObj entry not a (Str, Json) pair: {o:?}")),
2638 }
2639 }
2640 Ok(serde_json::Value::Object(map))
2641 }
2642 o => Err(format!("json.encode: JObj payload not a List: {o:?}")),
2643 },
2644 other => Err(format!("json.encode: not a Json constructor: {other}")),
2645 }
2646}
2647
2648fn required_field_names(arg: Option<&Value>) -> Result<Vec<String>, String> {
2653 let list = expect_list(arg)?;
2654 let mut out = Vec::with_capacity(list.len());
2655 for v in list {
2656 match v {
2657 Value::Str(s) => out.push(s.to_string()),
2658 other => return Err(format!(
2659 "parse_strict: required-fields list must contain Str, got {other:?}"
2660 )),
2661 }
2662 }
2663 Ok(out)
2664}
2665
2666fn check_required_fields(
2687 value: &serde_json::Value,
2688 required: &[String],
2689) -> Result<(), String> {
2690 if required.is_empty() {
2691 return Ok(());
2692 }
2693 if !matches!(value, serde_json::Value::Object(_)) {
2694 return Err(format!(
2695 "parse_strict: expected top-level object with fields {:?}, got {value}",
2696 required
2697 ));
2698 }
2699 let mut missing: Vec<String> = Vec::new();
2700 for path in required {
2701 if !path_exists(value, path) {
2702 missing.push(path.clone());
2703 }
2704 }
2705 if missing.is_empty() {
2706 Ok(())
2707 } else {
2708 Err(format!("missing required field(s): {}", missing.join(", ")))
2709 }
2710}
2711
2712fn path_exists(value: &serde_json::Value, path: &str) -> bool {
2717 let mut cursor = value;
2718 let segments = split_dotted_path(path);
2719 for seg in &segments {
2720 match cursor {
2721 serde_json::Value::Object(o) => match o.get(seg.as_str()) {
2722 Some(next) => cursor = next,
2723 None => return false,
2724 },
2725 _ => return false,
2726 }
2727 }
2728 true
2729}
2730
2731fn split_dotted_path(path: &str) -> Vec<String> {
2735 let mut out: Vec<String> = Vec::new();
2736 let mut cur = String::new();
2737 let mut iter = path.chars().peekable();
2738 while let Some(c) = iter.next() {
2739 if c == '\\' {
2740 if let Some(&'.') = iter.peek() {
2742 cur.push('.');
2743 iter.next();
2744 continue;
2745 }
2746 cur.push(c);
2747 } else if c == '.' {
2748 out.push(std::mem::take(&mut cur));
2749 } else {
2750 cur.push(c);
2751 }
2752 }
2753 out.push(cur);
2754 out
2755}
2756
2757fn extract_type_schema(v: Option<&Value>) -> Vec<(String, String)> {
2761 match v {
2762 Some(Value::List(pairs)) => pairs.iter().filter_map(|p| {
2763 if let Value::Tuple(items) = p {
2764 if items.len() == 2 {
2765 if let (Value::Str(name), Value::Str(tag)) = (&items[0], &items[1]) {
2766 return Some((name.to_string(), tag.to_string()));
2767 }
2768 }
2769 }
2770 None
2771 }).collect(),
2772 _ => vec![],
2773 }
2774}
2775
2776fn validate_field_types(
2782 json: &serde_json::Value,
2783 schema: &[(String, String)],
2784) -> Result<(), String> {
2785 if schema.is_empty() {
2786 return Ok(());
2787 }
2788 let obj = match json.as_object() {
2789 Some(o) => o,
2790 None => return Ok(()), };
2792 for (field, tag) in schema {
2793 if let Some(val) = obj.get(field) {
2794 if let Err(e) = check_json_type(val, tag) {
2795 return Err(format!("field `{field}`: {e}"));
2796 }
2797 }
2798 }
2799 Ok(())
2800}
2801
2802fn apply_option_wrapping(v: Value, json: &serde_json::Value, schema: &[(String, String)]) -> Value {
2807 if schema.is_empty() {
2808 return v;
2809 }
2810 let fields = match v {
2811 Value::Record { fields, .. } => *fields,
2812 other => return other,
2813 };
2814 let json_obj = match json.as_object() {
2815 Some(o) => o,
2816 None => return Value::record_interned(fields),
2817 };
2818 let mut new_fields = fields;
2819 for (field_name, tag) in schema {
2820 if tag.starts_with("Option[") && tag.ends_with(']') {
2821 let json_val = json_obj.get(field_name.as_str());
2822 let wrapped = match json_val {
2823 None | Some(serde_json::Value::Null) => none(),
2824 Some(_) => {
2825 let inner = new_fields
2826 .get(field_name.as_str())
2827 .cloned()
2828 .unwrap_or(Value::Unit);
2829 some(inner)
2830 }
2831 };
2832 new_fields.insert(smol_str::SmolStr::from(field_name.as_str()), wrapped);
2833 }
2834 }
2835 Value::record_interned(new_fields)
2836}
2837
2838fn check_json_type(val: &serde_json::Value, tag: &str) -> Result<(), String> {
2840 use serde_json::Value as J;
2841 match (tag, val) {
2842 ("Int", J::Number(n)) if n.is_i64() || n.is_u64() => Ok(()),
2843 ("Int", other) => Err(format!("expected Int, got {}", json_type_name(other))),
2844 ("Float", J::Number(_)) => Ok(()),
2845 ("Float", other) => Err(format!("expected Float, got {}", json_type_name(other))),
2846 ("Bool", J::Bool(_)) => Ok(()),
2847 ("Bool", other) => Err(format!("expected Bool, got {}", json_type_name(other))),
2848 ("Str", J::String(_)) => Ok(()),
2849 ("Str", other) => Err(format!("expected Str, got {}", json_type_name(other))),
2850 (tag, J::Null) if tag.starts_with("Option[") => Ok(()),
2852 (tag, val) if tag.starts_with("Option[") && tag.ends_with(']') => {
2853 let inner = &tag[7..tag.len() - 1]; check_json_type(val, inner)
2855 }
2856 (tag, J::Array(items)) if tag.starts_with("List[") && tag.ends_with(']') => {
2858 let inner = &tag[5..tag.len() - 1]; for (i, item) in items.iter().enumerate() {
2860 if let Err(e) = check_json_type(item, inner) {
2861 return Err(format!("[{i}]: {e}"));
2862 }
2863 }
2864 Ok(())
2865 }
2866 ("Record", _) => Ok(()), ("Any", _) => Ok(()), _ => Ok(()), }
2870}
2871
2872fn json_type_name(v: &serde_json::Value) -> &'static str {
2873 match v {
2874 serde_json::Value::Null => "null",
2875 serde_json::Value::Bool(_) => "Bool",
2876 serde_json::Value::Number(_) => "Number",
2877 serde_json::Value::String(_) => "Str",
2878 serde_json::Value::Array(_) => "Array",
2879 serde_json::Value::Object(_) => "Object",
2880 }
2881}
2882
2883fn parse_dotenv(src: &str) -> Result<indexmap::IndexMap<String, String>, String> {
2894 let mut out = indexmap::IndexMap::new();
2895 for (idx, raw) in src.lines().enumerate() {
2896 let line = raw.trim();
2897 if line.is_empty() || line.starts_with('#') {
2898 continue;
2899 }
2900 let after_export = line.strip_prefix("export ").unwrap_or(line);
2903 let (k, v) = match after_export.split_once('=') {
2904 Some(kv) => kv,
2905 None => return Err(format!("dotenv.parse line {}: missing `=`", idx + 1)),
2906 };
2907 let key = k.trim();
2908 if key.is_empty() {
2909 return Err(format!("dotenv.parse line {}: empty key", idx + 1));
2910 }
2911 let v_trim = v.trim();
2912 let value = if let Some(q) = v_trim.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
2913 q.to_string()
2914 } else if let Some(q) = v_trim.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
2915 q.to_string()
2916 } else {
2917 v_trim.to_string()
2918 };
2919 out.insert(key.to_string(), value);
2920 }
2921 Ok(out)
2922}
2923
2924fn instant_from_chrono<Tz: chrono::TimeZone>(dt: chrono::DateTime<Tz>) -> i64 {
2930 dt.timestamp_nanos_opt().unwrap_or(i64::MAX)
2931}
2932
2933fn chrono_from_instant(n: i64) -> chrono::DateTime<chrono::Utc> {
2934 let secs = n.div_euclid(1_000_000_000);
2935 let nanos = n.rem_euclid(1_000_000_000) as u32;
2936 use chrono::TimeZone;
2937 chrono::Utc
2938 .timestamp_opt(secs, nanos)
2939 .single()
2940 .unwrap_or_else(chrono::Utc::now)
2941}
2942
2943fn format_iso(n: i64) -> String {
2944 chrono_from_instant(n).to_rfc3339()
2945}
2946
2947enum TzArg {
2950 Utc,
2951 Local,
2952 Offset(i32),
2954 Iana(String),
2956}
2957
2958fn parse_tz_arg(v: Option<&Value>) -> Result<TzArg, String> {
2959 match v {
2960 Some(Value::Variant { name, args }) => match (name.as_str(), args.as_slice()) {
2961 ("Utc", []) => Ok(TzArg::Utc),
2962 ("Local", []) => Ok(TzArg::Local),
2963 ("Offset", [Value::Int(m)]) => {
2964 let m = i32::try_from(*m).map_err(|_| {
2965 format!("Tz::Offset: minutes out of range: {m}")
2966 })?;
2967 Ok(TzArg::Offset(m))
2968 }
2969 ("Iana", [Value::Str(s)]) => Ok(TzArg::Iana(s.to_string())),
2970 (other, _) => Err(format!(
2971 "expected Tz variant (Utc | Local | Offset(Int) | Iana(Str)), got `{other}` with {} arg(s)",
2972 args.len()
2973 )),
2974 },
2975 Some(other) => Err(format!("expected Tz variant, got {other:?}")),
2976 None => Err("missing Tz argument".into()),
2977 }
2978}
2979
2980fn resolve_tz_to_components(n: i64, tz: &TzArg) -> Result<Value, String> {
2981 use chrono::{TimeZone, Datelike, Timelike, Offset};
2982 let utc_dt = chrono_from_instant(n);
2983 let (y, m, d, hh, mm, ss, ns, off_min) = match tz {
2984 TzArg::Utc => {
2985 let d = utc_dt;
2986 (d.year(), d.month() as i32, d.day() as i32,
2987 d.hour() as i32, d.minute() as i32, d.second() as i32,
2988 d.nanosecond() as i32, 0)
2989 }
2990 TzArg::Local => {
2991 let d = utc_dt.with_timezone(&chrono::Local);
2992 let off = d.offset().fix().local_minus_utc() / 60;
2993 (d.year(), d.month() as i32, d.day() as i32,
2994 d.hour() as i32, d.minute() as i32, d.second() as i32,
2995 d.nanosecond() as i32, off)
2996 }
2997 TzArg::Offset(off_min) => {
2998 let off_secs = off_min.saturating_mul(60);
2999 let fixed = chrono::FixedOffset::east_opt(off_secs)
3000 .ok_or("to_components: offset out of range")?;
3001 let d = utc_dt.with_timezone(&fixed);
3002 (d.year(), d.month() as i32, d.day() as i32,
3003 d.hour() as i32, d.minute() as i32, d.second() as i32,
3004 d.nanosecond() as i32, *off_min)
3005 }
3006 TzArg::Iana(name) => {
3007 let tz: chrono_tz::Tz = name.parse()
3008 .map_err(|e| format!("to_components: unknown timezone `{name}`: {e}"))?;
3009 let d = utc_dt.with_timezone(&tz);
3010 let off = d.offset().fix().local_minus_utc() / 60;
3011 (d.year(), d.month() as i32, d.day() as i32,
3012 d.hour() as i32, d.minute() as i32, d.second() as i32,
3013 d.nanosecond() as i32, off)
3014 }
3015 };
3016 let mut rec = indexmap::IndexMap::new();
3017 rec.insert("year".into(), Value::Int(y as i64));
3018 rec.insert("month".into(), Value::Int(m as i64));
3019 rec.insert("day".into(), Value::Int(d as i64));
3020 rec.insert("hour".into(), Value::Int(hh as i64));
3021 rec.insert("minute".into(), Value::Int(mm as i64));
3022 rec.insert("second".into(), Value::Int(ss as i64));
3023 rec.insert("nano".into(), Value::Int(ns as i64));
3024 rec.insert("tz_offset_minutes".into(), Value::Int(off_min as i64));
3025 let _ = chrono::Utc.timestamp_opt(0, 0); Ok(Value::record_dynamic(rec))
3027}
3028
3029
3030fn instant_from_components(rec: &indexmap::IndexMap<smol_str::SmolStr, Value>) -> Result<i64, String> {
3031 use chrono::TimeZone;
3032 fn get_int(rec: &indexmap::IndexMap<smol_str::SmolStr, Value>, k: &str) -> Result<i64, String> {
3033 match rec.get(k) {
3034 Some(Value::Int(n)) => Ok(*n),
3035 other => Err(format!("from_components: missing or non-int field `{k}`: {other:?}")),
3036 }
3037 }
3038 let y = get_int(rec, "year")? as i32;
3039 let m = get_int(rec, "month")? as u32;
3040 let d = get_int(rec, "day")? as u32;
3041 let hh = get_int(rec, "hour")? as u32;
3042 let mm = get_int(rec, "minute")? as u32;
3043 let ss = get_int(rec, "second")? as u32;
3044 let ns = get_int(rec, "nano")? as u32;
3045 let off_min = get_int(rec, "tz_offset_minutes")? as i32;
3046 let off = chrono::FixedOffset::east_opt(off_min * 60)
3047 .ok_or("from_components: offset out of range")?;
3048 let dt = off
3049 .with_ymd_and_hms(y, m, d, hh, mm, ss)
3050 .single()
3051 .ok_or("from_components: invalid or ambiguous date/time")?;
3052 let dt = dt + chrono::Duration::nanoseconds(ns as i64);
3053 Ok(instant_from_chrono(dt))
3054}
3055
3056type Aead4<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>);
3073
3074type Aead5<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>);
3077
3078fn unpack4_bytes<'a>(
3079 args: &'a [Value],
3080 op: &str,
3081) -> Result<Aead4<'a>, String> {
3082 let pick = |i: usize, name: &str| -> Result<&'a Vec<u8>, String> {
3083 match args.get(i) {
3084 Some(Value::Bytes(b)) => Ok(b),
3085 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3086 None => Err(format!("{op}: missing {name} argument")),
3087 }
3088 };
3089 Ok((pick(0, "key")?, pick(1, "nonce")?, pick(2, "aad")?, pick(3, "plaintext")?))
3090}
3091
3092fn unpack5_bytes<'a>(
3093 args: &'a [Value],
3094 op: &str,
3095) -> Result<Aead5<'a>, String> {
3096 let pick = |i: usize, name: &str| -> Result<&'a Vec<u8>, String> {
3097 match args.get(i) {
3098 Some(Value::Bytes(b)) => Ok(b),
3099 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3100 None => Err(format!("{op}: missing {name} argument")),
3101 }
3102 };
3103 Ok((
3104 pick(0, "key")?,
3105 pick(1, "nonce")?,
3106 pick(2, "aad")?,
3107 pick(3, "ciphertext")?,
3108 pick(4, "tag")?,
3109 ))
3110}
3111
3112fn aead_result(ciphertext: Vec<u8>, tag: Vec<u8>) -> Value {
3113 let mut rec = indexmap::IndexMap::new();
3114 rec.insert("ciphertext".into(), Value::Bytes(ciphertext));
3115 rec.insert("tag".into(), Value::Bytes(tag));
3116 Value::record_dynamic(rec)
3117}
3118
3119fn aead_err(msg: impl Into<String>) -> Value {
3120 let s: String = msg.into();
3121 err_v(Value::Str(s.into()))
3122}
3123
3124fn aes_cbc_impl(args: &[Value], encrypt: bool) -> Value {
3134 use aes::{Aes128, Aes192, Aes256};
3135 use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7};
3136
3137 let op = if encrypt { "aes_cbc_encrypt_raw" } else { "aes_cbc_decrypt_raw" };
3138 let pick = |i: usize, name: &str| -> Result<&Vec<u8>, String> {
3139 match args.get(i) {
3140 Some(Value::Bytes(b)) => Ok(b),
3141 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3142 None => Err(format!("{op}: missing {name} argument")),
3143 }
3144 };
3145 let (key, iv, data) = match (pick(0, "key"), pick(1, "iv"), pick(2, "data")) {
3146 (Ok(k), Ok(v), Ok(d)) => (k, v, d),
3147 (Err(e), _, _) | (_, Err(e), _) | (_, _, Err(e)) => return aead_err(e),
3148 };
3149 if iv.len() != 16 {
3150 return aead_err(format!("{op}: iv must be exactly 16 bytes, got {}", iv.len()));
3151 }
3152 if !encrypt && (data.is_empty() || data.len() % 16 != 0) {
3156 return aead_err(format!(
3157 "{op}: ciphertext must be a non-zero multiple of the 16-byte block size, got {}",
3158 data.len()
3159 ));
3160 }
3161
3162 macro_rules! run {
3166 ($cipher:ty) => {
3167 if encrypt {
3168 type Enc = cbc::Encryptor<$cipher>;
3169 Ok(Enc::new(key.as_slice().into(), iv.as_slice().into())
3170 .encrypt_padded_vec_mut::<Pkcs7>(data))
3171 } else {
3172 type Dec = cbc::Decryptor<$cipher>;
3173 Dec::new(key.as_slice().into(), iv.as_slice().into())
3174 .decrypt_padded_vec_mut::<Pkcs7>(data)
3175 .map_err(|_| {
3176 format!("{op}: invalid padding or wrong key/iv")
3181 })
3182 }
3183 };
3184 }
3185 let out: Result<Vec<u8>, String> = match key.len() {
3186 16 => run!(Aes128),
3187 24 => run!(Aes192),
3188 32 => run!(Aes256),
3189 n => Err(format!("{op}: key must be 16, 24, or 32 bytes, got {n}")),
3190 };
3191 match out {
3192 Ok(bytes) => ok_v(Value::Bytes(bytes)),
3193 Err(e) => aead_err(e),
3194 }
3195}
3196
3197fn aes_gcm_seal_impl(args: &[Value]) -> Value {
3198 use aes_gcm::aead::{Aead, KeyInit, Payload};
3199 use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
3200 let (key, nonce, aad, plaintext) = match unpack4_bytes(args, "aes_gcm_seal") {
3201 Ok(t) => t,
3202 Err(e) => return aead_err(e),
3203 };
3204 if nonce.len() != 12 {
3205 return aead_err(format!(
3206 "aes_gcm_seal: nonce must be exactly 12 bytes, got {}", nonce.len()
3207 ));
3208 }
3209 let n = Nonce::from_slice(nonce);
3210 let payload = Payload { msg: plaintext, aad };
3211 let combined = match key.len() {
3214 16 => {
3215 let cipher = Aes128Gcm::new_from_slice(key)
3216 .map_err(|e| e.to_string());
3217 match cipher {
3218 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("aes_gcm_seal: {e}")),
3219 Err(e) => Err(format!("aes_gcm_seal: {e}")),
3220 }
3221 }
3222 32 => {
3223 let cipher = Aes256Gcm::new_from_slice(key)
3224 .map_err(|e| e.to_string());
3225 match cipher {
3226 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("aes_gcm_seal: {e}")),
3227 Err(e) => Err(format!("aes_gcm_seal: {e}")),
3228 }
3229 }
3230 other => return aead_err(format!(
3233 "aes_gcm_seal: key must be 16 or 32 bytes, got {other}"
3234 )),
3235 };
3236 match combined {
3237 Ok(mut buf) => {
3238 let tag_start = buf.len() - 16;
3240 let tag = buf.split_off(tag_start);
3241 ok_v(aead_result(buf, tag))
3242 }
3243 Err(e) => aead_err(e),
3244 }
3245}
3246
3247fn aes_gcm_open_impl(args: &[Value]) -> Value {
3248 use aes_gcm::aead::{Aead, KeyInit, Payload};
3249 use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
3250 let (key, nonce, aad, ciphertext, tag) = match unpack5_bytes(args, "aes_gcm_open") {
3251 Ok(t) => t,
3252 Err(e) => return err_v(Value::Str(e.into())),
3253 };
3254 if nonce.len() != 12 {
3255 return err_v(Value::Str(format!(
3256 "aes_gcm_open: nonce must be exactly 12 bytes, got {}", nonce.len()
3257 ).into()));
3258 }
3259 if tag.len() != 16 {
3260 return err_v(Value::Str(format!(
3261 "aes_gcm_open: tag must be exactly 16 bytes, got {}", tag.len()
3262 ).into()));
3263 }
3264 let mut combined = Vec::with_capacity(ciphertext.len() + tag.len());
3266 combined.extend_from_slice(ciphertext);
3267 combined.extend_from_slice(tag);
3268 let n = Nonce::from_slice(nonce);
3269 let payload = Payload { msg: &combined, aad };
3270 let plaintext = match key.len() {
3271 16 => Aes128Gcm::new_from_slice(key)
3272 .map_err(|e| format!("aes_gcm_open: {e}"))
3273 .and_then(|c| c.decrypt(n, payload).map_err(|e| format!("aes_gcm_open: {e}"))),
3274 32 => Aes256Gcm::new_from_slice(key)
3275 .map_err(|e| format!("aes_gcm_open: {e}"))
3276 .and_then(|c| c.decrypt(n, payload).map_err(|e| format!("aes_gcm_open: {e}"))),
3277 other => return err_v(Value::Str(format!(
3278 "aes_gcm_open: key must be 16 or 32 bytes, got {other}"
3279 ).into())),
3280 };
3281 match plaintext {
3282 Ok(p) => ok_v(Value::Bytes(p)),
3283 Err(e) => err_v(Value::Str(e.into())),
3284 }
3285}
3286
3287fn chacha20_seal_impl(args: &[Value]) -> Value {
3288 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
3289 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
3290 let (key, nonce, aad, plaintext) = match unpack4_bytes(args, "chacha20_poly1305_seal") {
3291 Ok(t) => t,
3292 Err(e) => return aead_err(e),
3293 };
3294 if key.len() != 32 {
3295 return aead_err(format!(
3296 "chacha20_poly1305_seal: key must be exactly 32 bytes, got {}", key.len()
3297 ));
3298 }
3299 if nonce.len() != 12 {
3300 return aead_err(format!(
3301 "chacha20_poly1305_seal: nonce must be exactly 12 bytes, got {}", nonce.len()
3302 ));
3303 }
3304 let cipher = ChaCha20Poly1305::new_from_slice(key)
3305 .map_err(|e| format!("chacha20_poly1305_seal: {e}"));
3306 let n = Nonce::from_slice(nonce);
3307 let payload = Payload { msg: plaintext, aad };
3308 let combined = match cipher {
3309 Ok(c) => c.encrypt(n, payload).map_err(|e| format!("chacha20_poly1305_seal: {e}")),
3310 Err(e) => Err(e),
3311 };
3312 match combined {
3313 Ok(mut buf) => {
3314 let tag_start = buf.len() - 16;
3315 let tag = buf.split_off(tag_start);
3316 ok_v(aead_result(buf, tag))
3317 }
3318 Err(e) => aead_err(e),
3319 }
3320}
3321
3322fn chacha20_open_impl(args: &[Value]) -> Value {
3323 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
3324 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
3325 let (key, nonce, aad, ciphertext, tag) = match unpack5_bytes(args, "chacha20_poly1305_open") {
3326 Ok(t) => t,
3327 Err(e) => return err_v(Value::Str(e.into())),
3328 };
3329 if key.len() != 32 {
3330 return err_v(Value::Str(format!(
3331 "chacha20_poly1305_open: key must be exactly 32 bytes, got {}", key.len()
3332 ).into()));
3333 }
3334 if nonce.len() != 12 {
3335 return err_v(Value::Str(format!(
3336 "chacha20_poly1305_open: nonce must be exactly 12 bytes, got {}", nonce.len()
3337 ).into()));
3338 }
3339 if tag.len() != 16 {
3340 return err_v(Value::Str(format!(
3341 "chacha20_poly1305_open: tag must be exactly 16 bytes, got {}", tag.len()
3342 ).into()));
3343 }
3344 let mut combined = Vec::with_capacity(ciphertext.len() + tag.len());
3345 combined.extend_from_slice(ciphertext);
3346 combined.extend_from_slice(tag);
3347 let cipher = ChaCha20Poly1305::new_from_slice(key)
3348 .map_err(|e| format!("chacha20_poly1305_open: {e}"));
3349 let n = Nonce::from_slice(nonce);
3350 let payload = Payload { msg: &combined, aad };
3351 match cipher.and_then(|c| c.decrypt(n, payload).map_err(|e| format!("chacha20_poly1305_open: {e}"))) {
3352 Ok(p) => ok_v(Value::Bytes(p)),
3353 Err(e) => err_v(Value::Str(e.into())),
3354 }
3355}
3356
3357type Kdf4<'a> = (&'a Vec<u8>, &'a Vec<u8>, i64, i64);
3367
3368type Hkdf4<'a> = (&'a Vec<u8>, &'a Vec<u8>, &'a Vec<u8>, i64);
3371
3372type Argon5<'a> = (&'a Vec<u8>, &'a Vec<u8>, i64, i64, i64);
3375
3376fn pick_bytes<'a>(args: &'a [Value], i: usize, op: &str, name: &str)
3377 -> Result<&'a Vec<u8>, String>
3378{
3379 match args.get(i) {
3380 Some(Value::Bytes(b)) => Ok(b),
3381 Some(other) => Err(format!("{op}: {name} must be Bytes, got {other:?}")),
3382 None => Err(format!("{op}: missing {name} argument")),
3383 }
3384}
3385
3386fn pick_int(args: &[Value], i: usize, op: &str, name: &str) -> Result<i64, String> {
3387 match args.get(i) {
3388 Some(Value::Int(n)) => Ok(*n),
3389 Some(other) => Err(format!("{op}: {name} must be Int, got {other:?}")),
3390 None => Err(format!("{op}: missing {name} argument")),
3391 }
3392}
3393
3394fn unpack_kdf4<'a>(args: &'a [Value], op: &str) -> Result<Kdf4<'a>, String> {
3395 Ok((
3396 pick_bytes(args, 0, op, "password")?,
3397 pick_bytes(args, 1, op, "salt")?,
3398 pick_int(args, 2, op, "iterations")?,
3399 pick_int(args, 3, op, "len")?,
3400 ))
3401}
3402
3403fn unpack_hkdf4<'a>(args: &'a [Value], op: &str) -> Result<Hkdf4<'a>, String> {
3404 Ok((
3405 pick_bytes(args, 0, op, "ikm")?,
3406 pick_bytes(args, 1, op, "salt")?,
3407 pick_bytes(args, 2, op, "info")?,
3408 pick_int(args, 3, op, "len")?,
3409 ))
3410}
3411
3412fn unpack_argon5<'a>(args: &'a [Value], op: &str) -> Result<Argon5<'a>, String> {
3413 Ok((
3414 pick_bytes(args, 0, op, "password")?,
3415 pick_bytes(args, 1, op, "salt")?,
3416 pick_int(args, 2, op, "t_cost")?,
3417 pick_int(args, 3, op, "m_cost")?,
3418 pick_int(args, 4, op, "len")?,
3419 ))
3420}
3421
3422const KDF_MAX_LEN: usize = 1024 * 1024;
3427
3428fn check_len(op: &str, len: i64) -> Result<usize, String> {
3429 if len <= 0 {
3430 return Err(format!("{op}: len must be > 0, got {len}"));
3431 }
3432 if (len as u64) > KDF_MAX_LEN as u64 {
3433 return Err(format!(
3434 "{op}: len must be <= {KDF_MAX_LEN}, got {len}"
3435 ));
3436 }
3437 Ok(len as usize)
3438}
3439
3440fn pbkdf2_sha256_impl(args: &[Value]) -> Value {
3441 use hmac::Hmac;
3442 use sha2::Sha256;
3443 let op = "pbkdf2_sha256";
3444 let (password, salt, iterations, len) = match unpack_kdf4(args, op) {
3445 Ok(t) => t,
3446 Err(e) => return err_v(Value::Str(e.into())),
3447 };
3448 if iterations <= 0 {
3449 return err_v(Value::Str(format!(
3450 "{op}: iterations must be > 0, got {iterations}"
3451 ).into()));
3452 }
3453 let out_len = match check_len(op, len) {
3454 Ok(n) => n,
3455 Err(e) => return err_v(Value::Str(e.into())),
3456 };
3457 let rounds = match u32::try_from(iterations) {
3458 Ok(r) => r,
3459 Err(_) => {
3460 return err_v(Value::Str(format!(
3461 "{op}: iterations must fit in u32, got {iterations}"
3462 ).into()))
3463 }
3464 };
3465 let mut out = vec![0u8; out_len];
3466 if let Err(e) = pbkdf2::pbkdf2::<Hmac<Sha256>>(password, salt, rounds, &mut out) {
3467 return err_v(Value::Str(format!("{op}: {e}").into()));
3468 }
3469 ok_v(Value::Bytes(out))
3470}
3471
3472fn hkdf_sha256_impl(args: &[Value]) -> Value {
3473 use hkdf::Hkdf;
3474 use sha2::Sha256;
3475 let op = "hkdf_sha256";
3476 let (ikm, salt, info, len) = match unpack_hkdf4(args, op) {
3477 Ok(t) => t,
3478 Err(e) => return err_v(Value::Str(e.into())),
3479 };
3480 let out_len = match check_len(op, len) {
3481 Ok(n) => n,
3482 Err(e) => return err_v(Value::Str(e.into())),
3483 };
3484 let salt_opt: Option<&[u8]> = if salt.is_empty() { None } else { Some(salt) };
3487 let hk = Hkdf::<Sha256>::new(salt_opt, ikm);
3488 let mut out = vec![0u8; out_len];
3489 match hk.expand(info, &mut out) {
3490 Ok(()) => ok_v(Value::Bytes(out)),
3491 Err(e) => err_v(Value::Str(format!("{op}: {e}").into())),
3492 }
3493}
3494
3495fn argon2id_impl(args: &[Value]) -> Value {
3496 use argon2::{Algorithm, Argon2, Params, Version};
3497 let op = "argon2id";
3498 let (password, salt, t_cost, m_cost, len) = match unpack_argon5(args, op) {
3499 Ok(t) => t,
3500 Err(e) => return err_v(Value::Str(e.into())),
3501 };
3502 let out_len = match check_len(op, len) {
3503 Ok(n) => n,
3504 Err(e) => return err_v(Value::Str(e.into())),
3505 };
3506 let t = match u32::try_from(t_cost) {
3507 Ok(n) if n >= 1 => n,
3508 _ => return err_v(Value::Str(format!(
3509 "{op}: t_cost must be a u32 >= 1, got {t_cost}"
3510 ).into())),
3511 };
3512 let m = match u32::try_from(m_cost) {
3513 Ok(n) if n >= Params::MIN_M_COST => n,
3514 _ => return err_v(Value::Str(format!(
3515 "{op}: m_cost must be a u32 >= {}, got {m_cost}",
3516 Params::MIN_M_COST
3517 ).into())),
3518 };
3519 let params = match Params::new(m, t, 1, Some(out_len)) {
3524 Ok(p) => p,
3525 Err(e) => return err_v(Value::Str(format!("{op}: {e}").into())),
3526 };
3527 let hasher = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
3528 let mut out = vec![0u8; out_len];
3529 if let Err(e) = hasher.hash_password_into(password, salt, &mut out) {
3530 return err_v(Value::Str(format!("{op}: {e}").into()));
3531 }
3532 ok_v(Value::Bytes(out))
3533}
3534
3535#[cfg(test)]
3536mod bytes_builtin_tests {
3537 use super::*;
3538
3539 fn call(op: &str, args: Vec<Value>) -> Result<Value, String> {
3540 dispatch("bytes", op, &args)
3541 }
3542
3543 #[test]
3544 fn concat_appends_in_order() {
3545 let a = Value::Bytes(b"AB".to_vec());
3546 let b = Value::Bytes(b"CD".to_vec());
3547 let got = call("concat", vec![a, b]).unwrap();
3548 assert_eq!(got, Value::Bytes(b"ABCD".to_vec()));
3549 }
3550
3551 #[test]
3552 fn concat_all_joins_a_list_in_order() {
3553 let items = Value::List(vec![
3554 Value::Bytes(b"Hi".to_vec()),
3555 Value::Bytes(b", ".to_vec()),
3556 Value::Bytes(b"there".to_vec()),
3557 ].into());
3558 let got = call("concat_all", vec![items]).unwrap();
3559 assert_eq!(got, Value::Bytes(b"Hi, there".to_vec()));
3560 }
3561
3562 #[test]
3563 fn concat_all_rejects_non_bytes_elements() {
3564 let items = Value::List(vec![Value::Int(1)].into());
3565 assert!(call("concat_all", vec![items]).is_err());
3566 }
3567
3568 #[test]
3569 fn u8_encodes_and_truncates_to_one_byte() {
3570 let got = call("u8", vec![Value::Int(65)]).unwrap();
3571 assert_eq!(got, Value::Bytes(vec![65]));
3572 let wrapped = call("u8", vec![Value::Int(256 + 65)]).unwrap();
3576 assert_eq!(wrapped, Value::Bytes(vec![65]));
3577 }
3578
3579 #[test]
3580 fn u16_le_round_trips() {
3581 let encoded = call("u16_le", vec![Value::Int(258)]).unwrap();
3582 assert_eq!(encoded, Value::Bytes(vec![2, 1]));
3583 let decoded = call("u16_le_at", vec![encoded, Value::Int(0)]).unwrap();
3584 assert_eq!(decoded, ok_v(Value::Int(258)));
3585 }
3586
3587 #[test]
3588 fn u32_le_round_trips() {
3589 let encoded = call("u32_le", vec![Value::Int(70000)]).unwrap();
3590 let decoded = call("u32_le_at", vec![encoded, Value::Int(0)]).unwrap();
3591 assert_eq!(decoded, ok_v(Value::Int(70000)));
3592 }
3593
3594 #[test]
3595 fn u64_le_round_trips_beyond_u32_range() {
3596 let n = 9_007_199_254_740_993_i64; let encoded = call("u64_le", vec![Value::Int(n)]).unwrap();
3598 let decoded = call("u64_le_at", vec![encoded, Value::Int(0)]).unwrap();
3599 assert_eq!(decoded, ok_v(Value::Int(n)));
3600 }
3601
3602 #[test]
3603 fn decoders_report_out_of_range_offsets_as_err_not_panic() {
3604 let one_byte = Value::Bytes(vec![5]);
3605 let got = call("u16_le_at", vec![one_byte, Value::Int(0)]).unwrap();
3606 match got {
3607 Value::Variant { name, .. } => assert_eq!(name, "Err"),
3608 other => panic!("expected an Err variant, got {other:?}"),
3609 }
3610 }
3611
3612 #[test]
3613 fn u16_le_at_reads_at_a_nonzero_offset() {
3614 let buf = Value::Bytes(vec![0xFF, 2, 1]);
3617 let decoded = call("u16_le_at", vec![buf, Value::Int(1)]).unwrap();
3618 assert_eq!(decoded, ok_v(Value::Int(258)));
3619 }
3620}
3621
3622#[cfg(test)]
3623mod ed25519_curve_tests {
3624 use super::*;
3625
3626 fn call(op: &str, args: Vec<Value>) -> Result<Value, String> {
3627 dispatch("crypto", op, &args)
3628 }
3629
3630 #[test]
3631 fn a_real_public_key_is_a_valid_point() {
3632 use ed25519_dalek::SigningKey;
3633 let seed = [7u8; 32];
3634 let sk = SigningKey::from_bytes(&seed);
3635 let pk = sk.verifying_key().to_bytes().to_vec();
3636 let got = call("ed25519_is_valid_point", vec![Value::Bytes(pk)]).unwrap();
3637 assert_eq!(got, Value::Bool(true));
3638 }
3639
3640 #[test]
3641 fn wrong_length_is_not_a_valid_point() {
3642 let got = call("ed25519_is_valid_point", vec![Value::Bytes(vec![1, 2, 3])]).unwrap();
3643 assert_eq!(got, Value::Bool(false));
3644 }
3645
3646 #[test]
3647 fn a_non_curve_point_is_rejected() {
3648 let mut candidate = [1u8; 32];
3653 candidate[31] = 0;
3654 let got = call("ed25519_is_valid_point", vec![Value::Bytes(candidate.to_vec())]).unwrap();
3655 assert_eq!(got, Value::Bool(false));
3656 }
3657}