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