1use lex_bytecode::{MapKey, Value};
6use std::collections::{BTreeMap, BTreeSet, HashMap};
7use std::sync::{Mutex, OnceLock};
8
9pub fn try_pure_builtin(kind: &str, op: &str, args: &[Value]) -> Option<Result<Value, String>> {
12 if !is_pure_module(kind) { return None; }
13 if (kind, op) == ("crypto", "random") { return None; }
17 if (kind, op) == ("datetime", "now") { return None; }
20 if (kind, "send") == (kind, op) && kind == "http" { return None; }
23 if (kind, "get") == (kind, op) && kind == "http" { return None; }
24 if (kind, "post") == (kind, op) && kind == "http" { return None; }
25 Some(dispatch(kind, op, args))
26}
27
28pub fn is_pure_module(kind: &str) -> bool {
31 matches!(kind, "str" | "int" | "float" | "bool" | "list"
32 | "option" | "result" | "tuple" | "json" | "bytes" | "flow" | "math"
33 | "map" | "set" | "crypto" | "regex" | "deque" | "datetime" | "http"
34 | "toml" | "yaml" | "dotenv" | "csv" | "test" | "random" | "parser"
35 | "cli")
36}
37
38fn dispatch(kind: &str, op: &str, args: &[Value]) -> Result<Value, String> {
39 match (kind, op) {
40 ("str", "is_empty") => Ok(Value::Bool(expect_str(args.first())?.is_empty())),
42 ("str", "len") => Ok(Value::Int(expect_str(args.first())?.len() as i64)),
43 ("str", "concat") => {
44 let a = expect_str(args.first())?;
45 let b = expect_str(args.get(1))?;
46 Ok(Value::Str(format!("{a}{b}")))
47 }
48 ("str", "to_int") => {
49 let s = expect_str(args.first())?;
50 match s.parse::<i64>() {
51 Ok(n) => Ok(some(Value::Int(n))),
52 Err(_) => Ok(none()),
53 }
54 }
55 ("str", "split") => {
56 let s = expect_str(args.first())?;
57 let sep = expect_str(args.get(1))?;
58 let items: Vec<Value> = if sep.is_empty() {
59 s.chars().map(|c| Value::Str(c.to_string())).collect()
60 } else {
61 s.split(sep.as_str()).map(|p| Value::Str(p.to_string())).collect()
62 };
63 Ok(Value::List(items))
64 }
65 ("str", "join") => {
66 let parts = expect_list(args.first())?;
67 let sep = expect_str(args.get(1))?;
68 let mut out = String::new();
69 for (i, p) in parts.iter().enumerate() {
70 if i > 0 { out.push_str(&sep); }
71 match p {
72 Value::Str(s) => out.push_str(s),
73 other => return Err(format!("str.join element must be Str, got {other:?}")),
74 }
75 }
76 Ok(Value::Str(out))
77 }
78 ("str", "starts_with") => {
79 let s = expect_str(args.first())?;
80 let prefix = expect_str(args.get(1))?;
81 Ok(Value::Bool(s.starts_with(prefix.as_str())))
82 }
83 ("str", "ends_with") => {
84 let s = expect_str(args.first())?;
85 let suffix = expect_str(args.get(1))?;
86 Ok(Value::Bool(s.ends_with(suffix.as_str())))
87 }
88 ("str", "contains") => {
89 let s = expect_str(args.first())?;
90 let needle = expect_str(args.get(1))?;
91 Ok(Value::Bool(s.contains(needle.as_str())))
92 }
93 ("str", "replace") => {
94 let s = expect_str(args.first())?;
95 let from = expect_str(args.get(1))?;
96 let to = expect_str(args.get(2))?;
97 Ok(Value::Str(s.replace(from.as_str(), to.as_str())))
98 }
99 ("str", "trim") => Ok(Value::Str(expect_str(args.first())?.trim().to_string())),
100 ("str", "to_upper") => Ok(Value::Str(expect_str(args.first())?.to_uppercase())),
101 ("str", "to_lower") => Ok(Value::Str(expect_str(args.first())?.to_lowercase())),
102 ("str", "strip_prefix") => {
103 let s = expect_str(args.first())?;
104 let prefix = expect_str(args.get(1))?;
105 Ok(match s.strip_prefix(prefix.as_str()) {
106 Some(rest) => some(Value::Str(rest.to_string())),
107 None => none(),
108 })
109 }
110 ("str", "strip_suffix") => {
111 let s = expect_str(args.first())?;
112 let suffix = expect_str(args.get(1))?;
113 Ok(match s.strip_suffix(suffix.as_str()) {
114 Some(rest) => some(Value::Str(rest.to_string())),
115 None => none(),
116 })
117 }
118 ("str", "slice") => {
119 let s = expect_str(args.first())?;
129 let lo_i = expect_int(args.get(1))?;
130 let hi_i = expect_int(args.get(2))?;
131 let lo = (lo_i.max(0) as usize).min(s.len());
132 let hi = (hi_i.max(0) as usize).min(s.len());
133 if lo > hi {
134 return Err(format!(
135 "str.slice: reversed range [{lo}..{hi}] (after clamping to len {})",
136 s.len()));
137 }
138 if !s.is_char_boundary(lo) || !s.is_char_boundary(hi) {
139 return Err(format!("str.slice: [{lo}..{hi}] not on char boundaries"));
140 }
141 Ok(Value::Str(s[lo..hi].to_string()))
142 }
143
144 ("int", "to_str") => Ok(Value::Str(expect_int(args.first())?.to_string())),
146 ("int", "to_float") => Ok(Value::Float(expect_int(args.first())? as f64)),
147 ("float", "to_int") => Ok(Value::Int(expect_float(args.first())? as i64)),
148 ("float", "to_str") => Ok(Value::Str(expect_float(args.first())?.to_string())),
149 ("str", "to_float") => {
150 let s = expect_str(args.first())?;
151 match s.parse::<f64>() {
152 Ok(f) => Ok(some(Value::Float(f))),
153 Err(_) => Ok(none()),
154 }
155 }
156
157 ("list", "len") => Ok(Value::Int(expect_list(args.first())?.len() as i64)),
159 ("list", "is_empty") => Ok(Value::Bool(expect_list(args.first())?.is_empty())),
160 ("list", "head") => {
161 let xs = expect_list(args.first())?;
162 match xs.first() {
163 Some(v) => Ok(some(v.clone())),
164 None => Ok(none()),
165 }
166 }
167 ("list", "tail") => {
168 let xs = expect_list(args.first())?;
169 if xs.is_empty() { Ok(Value::List(Vec::new())) }
170 else { Ok(Value::List(xs[1..].to_vec())) }
171 }
172 ("list", "range") => {
173 let lo = expect_int(args.first())?;
174 let hi = expect_int(args.get(1))?;
175 Ok(Value::List((lo..hi).map(Value::Int).collect()))
176 }
177 ("list", "concat") => {
178 let mut out = expect_list(args.first())?.clone();
179 out.extend(expect_list(args.get(1))?.iter().cloned());
180 Ok(Value::List(out))
181 }
182 ("list", "reverse") => {
183 let mut out = expect_list(args.first())?.clone();
184 out.reverse();
185 Ok(Value::List(out))
186 }
187 ("list", "enumerate") => {
188 let xs = expect_list(args.first())?;
189 let pairs = xs.iter().cloned().enumerate()
190 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v]))
191 .collect::<Vec<_>>();
192 Ok(Value::List(pairs))
193 }
194
195 ("tuple", "fst") => tuple_index(first_arg(args)?, 0),
200 ("tuple", "snd") => tuple_index(first_arg(args)?, 1),
201 ("tuple", "third") => tuple_index(first_arg(args)?, 2),
202 ("tuple", "len") => match first_arg(args)? {
203 Value::Tuple(items) => Ok(Value::Int(items.len() as i64)),
204 other => Err(format!("tuple.len: expected Tuple, got {other:?}")),
205 },
206
207 ("option", "unwrap_or") => {
209 let opt = first_arg(args)?;
210 let default = args.get(1).cloned().unwrap_or(Value::Unit);
211 match opt {
212 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
213 Value::Variant { name, .. } if name == "None" => Ok(default),
214 other => Err(format!("option.unwrap_or expected Option, got {other:?}")),
215 }
216 }
217 ("option", "unwrap_or_else") => {
222 let opt = first_arg(args)?;
223 match opt {
224 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
225 Value::Variant { name, .. } if name == "None" => {
226 Ok(args.get(1).cloned().unwrap_or(Value::Unit))
230 }
231 other => Err(format!("option.unwrap_or_else expected Option, got {other:?}")),
232 }
233 }
234 ("option", "is_some") => match first_arg(args)? {
235 Value::Variant { name, .. } => Ok(Value::Bool(name == "Some")),
236 other => Err(format!("option.is_some expected Option, got {other:?}")),
237 },
238 ("option", "is_none") => match first_arg(args)? {
239 Value::Variant { name, .. } => Ok(Value::Bool(name == "None")),
240 other => Err(format!("option.is_none expected Option, got {other:?}")),
241 },
242
243 ("result", "is_ok") => match first_arg(args)? {
245 Value::Variant { name, .. } => Ok(Value::Bool(name == "Ok")),
246 other => Err(format!("result.is_ok expected Result, got {other:?}")),
247 },
248 ("result", "is_err") => match first_arg(args)? {
249 Value::Variant { name, .. } => Ok(Value::Bool(name == "Err")),
250 other => Err(format!("result.is_err expected Result, got {other:?}")),
251 },
252 ("result", "unwrap_or") => {
253 let res = first_arg(args)?;
254 let default = args.get(1).cloned().unwrap_or(Value::Unit);
255 match res {
256 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
257 Value::Variant { name, .. } if name == "Err" => Ok(default),
258 other => Err(format!("result.unwrap_or expected Result, got {other:?}")),
259 }
260 }
261
262 ("json", "stringify") => {
264 let v = first_arg(args)?;
265 Ok(Value::Str(serde_json::to_string(&value_to_json(v)).unwrap_or_default()))
266 }
267 ("json", "parse") => {
268 let s = expect_str(args.first())?;
269 match serde_json::from_str::<serde_json::Value>(&s) {
270 Ok(v) => Ok(ok_v(json_to_value(&v))),
271 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
272 }
273 }
274 ("json", "parse_strict") => {
277 let s = expect_str(args.first())?;
278 let required = required_field_names(args.get(1))?;
279 let schema = extract_type_schema(args.get(2));
280 match serde_json::from_str::<serde_json::Value>(&s) {
281 Ok(v) => {
282 if let Err(e) = check_required_fields(&v, &required) {
283 return Ok(err_v(Value::Str(e)));
284 }
285 if let Err(e) = validate_field_types(&v, &schema) {
286 return Ok(err_v(Value::Str(e)));
287 }
288 Ok(ok_v(json_to_value(&v)))
289 }
290 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
291 }
292 }
293
294 ("toml", "parse") => {
299 let s = expect_str(args.first())?;
300 match toml::from_str::<serde_json::Value>(&s) {
301 Ok(mut v) => {
302 unwrap_toml_datetime_markers(&mut v);
303 Ok(ok_v(json_to_value(&v)))
304 }
305 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
306 }
307 }
308 ("json", "parse_strict_typed") => {
312 let s = expect_str(args.first())?;
313 let required = required_field_names(args.get(1))?;
314 let schema = extract_type_schema(args.get(2));
315 match serde_json::from_str::<serde_json::Value>(&s) {
316 Ok(v) => {
317 if let Err(e) = check_required_fields(&v, &required) {
318 return Ok(err_v(Value::Str(e)));
319 }
320 if let Err(e) = validate_field_types(&v, &schema) {
321 return Ok(err_v(Value::Str(e)));
322 }
323 Ok(ok_v(json_to_value(&v)))
324 }
325 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
326 }
327 }
328
329 ("toml", "parse_strict") => {
332 let s = expect_str(args.first())?;
333 let required = required_field_names(args.get(1))?;
334 let schema = extract_type_schema(args.get(2));
335 match toml::from_str::<serde_json::Value>(&s) {
336 Ok(mut v) => {
337 unwrap_toml_datetime_markers(&mut v);
338 if let Err(e) = check_required_fields(&v, &required) {
339 return Ok(err_v(Value::Str(e)));
340 }
341 if let Err(e) = validate_field_types(&v, &schema) {
342 return Ok(err_v(Value::Str(e)));
343 }
344 Ok(ok_v(json_to_value(&v)))
345 }
346 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
347 }
348 }
349 ("toml", "parse_strict_typed") => {
350 let s = expect_str(args.first())?;
351 let required = required_field_names(args.get(1))?;
352 let schema = extract_type_schema(args.get(2));
353 match toml::from_str::<serde_json::Value>(&s) {
354 Ok(mut v) => {
355 unwrap_toml_datetime_markers(&mut v);
356 if let Err(e) = check_required_fields(&v, &required) {
357 return Ok(err_v(Value::Str(e)));
358 }
359 if let Err(e) = validate_field_types(&v, &schema) {
360 return Ok(err_v(Value::Str(e)));
361 }
362 Ok(ok_v(json_to_value(&v)))
363 }
364 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
365 }
366 }
367 ("toml", "stringify") => {
368 let v = first_arg(args)?;
369 let json = value_to_json(v);
375 match toml::to_string(&json) {
376 Ok(s) => Ok(ok_v(Value::Str(s))),
377 Err(e) => Ok(err_v(Value::Str(format!("toml.stringify: {e}")))),
378 }
379 }
380
381 ("yaml", "parse") => {
387 let s = expect_str(args.first())?;
388 match serde_yaml::from_str::<serde_json::Value>(&s) {
389 Ok(v) => Ok(ok_v(json_to_value(&v))),
390 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
391 }
392 }
393 ("yaml", "parse_strict") => {
396 let s = expect_str(args.first())?;
397 let required = required_field_names(args.get(1))?;
398 let schema = extract_type_schema(args.get(2));
399 match serde_yaml::from_str::<serde_json::Value>(&s) {
400 Ok(v) => {
401 if let Err(e) = check_required_fields(&v, &required) {
402 return Ok(err_v(Value::Str(e)));
403 }
404 if let Err(e) = validate_field_types(&v, &schema) {
405 return Ok(err_v(Value::Str(e)));
406 }
407 Ok(ok_v(json_to_value(&v)))
408 }
409 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
410 }
411 }
412 ("yaml", "parse_strict_typed") => {
413 let s = expect_str(args.first())?;
414 let required = required_field_names(args.get(1))?;
415 let schema = extract_type_schema(args.get(2));
416 match serde_yaml::from_str::<serde_json::Value>(&s) {
417 Ok(v) => {
418 if let Err(e) = check_required_fields(&v, &required) {
419 return Ok(err_v(Value::Str(e)));
420 }
421 if let Err(e) = validate_field_types(&v, &schema) {
422 return Ok(err_v(Value::Str(e)));
423 }
424 Ok(ok_v(json_to_value(&v)))
425 }
426 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
427 }
428 }
429 ("yaml", "stringify") => {
430 let v = first_arg(args)?;
431 let json = value_to_json(v);
432 match serde_yaml::to_string(&json) {
433 Ok(s) => Ok(ok_v(Value::Str(s))),
434 Err(e) => Ok(err_v(Value::Str(format!("yaml.stringify: {e}")))),
435 }
436 }
437
438 ("dotenv", "parse") => {
446 use std::collections::BTreeMap;
447 use lex_bytecode::MapKey;
448 let s = expect_str(args.first())?;
449 match parse_dotenv(&s) {
450 Ok(map) => {
451 let mut bt: BTreeMap<MapKey, Value> = BTreeMap::new();
452 for (k, v) in map {
453 bt.insert(MapKey::Str(k), Value::Str(v));
454 }
455 Ok(ok_v(Value::Map(bt)))
456 }
457 Err(e) => Ok(err_v(Value::Str(e))),
458 }
459 }
460
461 ("csv", "parse") => {
466 let s = expect_str(args.first())?;
467 let mut rdr = csv::ReaderBuilder::new()
468 .has_headers(false)
469 .flexible(true)
470 .from_reader(s.as_bytes());
471 let mut rows: Vec<Value> = Vec::new();
472 for r in rdr.records() {
473 match r {
474 Ok(rec) => {
475 let row: Vec<Value> = rec.iter()
476 .map(|f| Value::Str(f.to_string()))
477 .collect();
478 rows.push(Value::List(row));
479 }
480 Err(e) => return Ok(err_v(Value::Str(format!("csv.parse: {e}")))),
481 }
482 }
483 Ok(ok_v(Value::List(rows)))
484 }
485 ("csv", "stringify") => {
486 let v = first_arg(args)?;
491 let rows = match v {
492 Value::List(rs) => rs,
493 _ => return Ok(err_v(Value::Str("csv.stringify expects List[List[Str]]".into()))),
494 };
495 let mut out = Vec::new();
496 {
497 let mut wtr = csv::WriterBuilder::new()
498 .has_headers(false)
499 .from_writer(&mut out);
500 for row in rows {
501 let cells = match row {
502 Value::List(cs) => cs,
503 _ => return Ok(err_v(Value::Str("csv.stringify row must be List[Str]".into()))),
504 };
505 let strs: Vec<String> = cells.iter().map(|c| match c {
506 Value::Str(s) => s.clone(),
507 other => serde_json::to_string(&other.to_json())
508 .unwrap_or_else(|_| String::new()),
509 }).collect();
510 if let Err(e) = wtr.write_record(&strs) {
511 return Ok(err_v(Value::Str(format!("csv.stringify: {e}"))));
512 }
513 }
514 if let Err(e) = wtr.flush() {
515 return Ok(err_v(Value::Str(format!("csv.stringify flush: {e}"))));
516 }
517 }
518 match String::from_utf8(out) {
519 Ok(s) => Ok(ok_v(Value::Str(s))),
520 Err(e) => Ok(err_v(Value::Str(format!("csv.stringify utf8: {e}")))),
521 }
522 }
523
524 ("test", "assert_eq") => {
531 let a = first_arg(args)?;
532 let b = args.get(1).ok_or("test.assert_eq: missing second arg")?;
533 if a == b {
534 Ok(ok_v(Value::Unit))
535 } else {
536 Ok(err_v(Value::Str(format!("assert_eq: lhs {} != rhs {}",
537 value_to_json(a), value_to_json(b)))))
538 }
539 }
540 ("test", "assert_ne") => {
541 let a = first_arg(args)?;
542 let b = args.get(1).ok_or("test.assert_ne: missing second arg")?;
543 if a != b {
544 Ok(ok_v(Value::Unit))
545 } else {
546 Ok(err_v(Value::Str(format!("assert_ne: both sides are {}",
547 value_to_json(a)))))
548 }
549 }
550 ("test", "assert_true") => {
551 match first_arg(args)? {
552 Value::Bool(true) => Ok(ok_v(Value::Unit)),
553 Value::Bool(false) => Ok(err_v(Value::Str("assert_true: was false".into()))),
554 other => Err(format!("test.assert_true expects Bool, got {other:?}")),
555 }
556 }
557 ("test", "assert_false") => {
558 match first_arg(args)? {
559 Value::Bool(false) => Ok(ok_v(Value::Unit)),
560 Value::Bool(true) => Ok(err_v(Value::Str("assert_false: was true".into()))),
561 other => Err(format!("test.assert_false expects Bool, got {other:?}")),
562 }
563 }
564
565 ("bytes", "len") => {
567 let b = expect_bytes(args.first())?;
568 Ok(Value::Int(b.len() as i64))
569 }
570 ("bytes", "eq") => {
571 let a = expect_bytes(args.first())?;
572 let b = expect_bytes(args.get(1))?;
573 Ok(Value::Bool(a == b))
574 }
575 ("bytes", "from_str") => {
576 let s = expect_str(args.first())?;
577 Ok(Value::Bytes(s.into_bytes()))
578 }
579 ("bytes", "to_str") => {
580 let b = expect_bytes(args.first())?;
581 match String::from_utf8(b.to_vec()) {
582 Ok(s) => Ok(ok_v(Value::Str(s))),
583 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
584 }
585 }
586 ("bytes", "slice") => {
587 let b = expect_bytes(args.first())?;
588 let lo = expect_int(args.get(1))? as usize;
589 let hi = expect_int(args.get(2))? as usize;
590 if lo > hi || hi > b.len() {
591 return Err(format!("bytes.slice: out of range [{lo}..{hi}] of {}", b.len()));
592 }
593 Ok(Value::Bytes(b[lo..hi].to_vec()))
594 }
595 ("bytes", "is_empty") => {
596 let b = expect_bytes(args.first())?;
597 Ok(Value::Bool(b.is_empty()))
598 }
599
600 ("math", "exp") => Ok(Value::Float(expect_float(args.first())?.exp())),
607 ("math", "log") => Ok(Value::Float(expect_float(args.first())?.ln())),
608 ("math", "log2") => Ok(Value::Float(expect_float(args.first())?.log2())),
609 ("math", "log10") => Ok(Value::Float(expect_float(args.first())?.log10())),
610 ("math", "sqrt") => Ok(Value::Float(expect_float(args.first())?.sqrt())),
611 ("math", "abs") => Ok(Value::Float(expect_float(args.first())?.abs())),
612 ("math", "sin") => Ok(Value::Float(expect_float(args.first())?.sin())),
613 ("math", "cos") => Ok(Value::Float(expect_float(args.first())?.cos())),
614 ("math", "tan") => Ok(Value::Float(expect_float(args.first())?.tan())),
615 ("math", "asin") => Ok(Value::Float(expect_float(args.first())?.asin())),
616 ("math", "acos") => Ok(Value::Float(expect_float(args.first())?.acos())),
617 ("math", "atan") => Ok(Value::Float(expect_float(args.first())?.atan())),
618 ("math", "floor") => Ok(Value::Float(expect_float(args.first())?.floor())),
619 ("math", "ceil") => Ok(Value::Float(expect_float(args.first())?.ceil())),
620 ("math", "round") => Ok(Value::Float(expect_float(args.first())?.round())),
621 ("math", "trunc") => Ok(Value::Float(expect_float(args.first())?.trunc())),
622 ("math", "pow") => {
623 let a = expect_float(args.first())?;
624 let b = expect_float(args.get(1))?;
625 Ok(Value::Float(a.powf(b)))
626 }
627 ("math", "atan2") => {
628 let y = expect_float(args.first())?;
629 let x = expect_float(args.get(1))?;
630 Ok(Value::Float(y.atan2(x)))
631 }
632 ("math", "min") => {
633 let a = expect_float(args.first())?;
634 let b = expect_float(args.get(1))?;
635 Ok(Value::Float(a.min(b)))
636 }
637 ("math", "max") => {
638 let a = expect_float(args.first())?;
639 let b = expect_float(args.get(1))?;
640 Ok(Value::Float(a.max(b)))
641 }
642 ("math", "zeros") => {
643 let r = expect_int(args.first())?;
644 let c = expect_int(args.get(1))?;
645 if r < 0 || c < 0 {
646 return Err(format!("math.zeros: negative dim {r}x{c}"));
647 }
648 let r = r as usize; let c = c as usize;
649 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![0.0; r * c] })
650 }
651 ("math", "ones") => {
652 let r = expect_int(args.first())?;
653 let c = expect_int(args.get(1))?;
654 if r < 0 || c < 0 {
655 return Err(format!("math.ones: negative dim {r}x{c}"));
656 }
657 let r = r as usize; let c = c as usize;
658 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![1.0; r * c] })
659 }
660 ("math", "from_lists") => {
661 let rows = expect_list(args.first())?;
662 let r = rows.len();
663 if r == 0 {
664 return Ok(Value::F64Array { rows: 0, cols: 0, data: Vec::new() });
665 }
666 let first_row = match &rows[0] {
667 Value::List(xs) => xs,
668 other => return Err(format!("math.from_lists: row 0 not List, got {other:?}")),
669 };
670 let c = first_row.len();
671 let mut data = Vec::with_capacity(r * c);
672 for (i, row) in rows.iter().enumerate() {
673 let row = match row {
674 Value::List(xs) => xs,
675 other => return Err(format!("math.from_lists: row {i} not List, got {other:?}")),
676 };
677 if row.len() != c {
678 return Err(format!("math.from_lists: row {i} has {} cols, expected {c}", row.len()));
679 }
680 for (j, v) in row.iter().enumerate() {
681 let f = match v {
682 Value::Float(f) => *f,
683 Value::Int(n) => *n as f64,
684 other => return Err(format!("math.from_lists: ({i},{j}) not numeric, got {other:?}")),
685 };
686 data.push(f);
687 }
688 }
689 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
690 }
691 ("math", "from_flat") => {
692 let r = expect_int(args.first())?;
693 let c = expect_int(args.get(1))?;
694 let xs = expect_list(args.get(2))?;
695 if r < 0 || c < 0 {
696 return Err(format!("math.from_flat: negative dim {r}x{c}"));
697 }
698 let r = r as usize; let c = c as usize;
699 if xs.len() != r * c {
700 return Err(format!("math.from_flat: list len {} != {}*{}", xs.len(), r, c));
701 }
702 let mut data = Vec::with_capacity(r * c);
703 for v in xs {
704 data.push(match v {
705 Value::Float(f) => *f,
706 Value::Int(n) => *n as f64,
707 other => return Err(format!("math.from_flat: non-numeric element {other:?}")),
708 });
709 }
710 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
711 }
712 ("math", "rows") => {
713 let (r, _, _) = unpack_matrix(first_arg(args)?)?;
714 Ok(Value::Int(r as i64))
715 }
716 ("math", "cols") => {
717 let (_, c, _) = unpack_matrix(first_arg(args)?)?;
718 Ok(Value::Int(c as i64))
719 }
720 ("math", "get") => {
721 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
722 let i = expect_int(args.get(1))? as usize;
723 let j = expect_int(args.get(2))? as usize;
724 if i >= r || j >= c {
725 return Err(format!("math.get: ({i},{j}) out of {r}x{c}"));
726 }
727 Ok(Value::Float(data[i * c + j]))
728 }
729 ("math", "to_flat") => {
730 let (_, _, data) = unpack_matrix(first_arg(args)?)?;
731 Ok(Value::List(data.into_iter().map(Value::Float).collect()))
732 }
733 ("math", "transpose") => {
734 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
735 let mut out = vec![0.0; r * c];
736 for i in 0..r {
737 for j in 0..c {
738 out[j * r + i] = data[i * c + j];
739 }
740 }
741 Ok(Value::F64Array { rows: c as u32, cols: r as u32, data: out })
742 }
743 ("math", "matmul") => {
744 let (m, k1, a) = unpack_matrix(first_arg(args)?)?;
745 let (k2, n, b) = unpack_matrix(args.get(1).ok_or("math.matmul: missing arg 1")?)?;
746 if k1 != k2 {
747 return Err(format!("math.matmul: dim mismatch {m}x{k1} · {k2}x{n}"));
748 }
749 let mut c = vec![0.0; m * n];
753 for i in 0..m {
754 for kk in 0..k1 {
755 let aik = a[i * k1 + kk];
756 for j in 0..n {
757 c[i * n + j] += aik * b[kk * n + j];
758 }
759 }
760 }
761 Ok(Value::F64Array { rows: m as u32, cols: n as u32, data: c })
762 }
763 ("math", "scale") => {
764 let s = expect_float(args.first())?;
765 let (r, c, mut data) = unpack_matrix(args.get(1).ok_or("math.scale: missing arg 1")?)?;
766 for x in &mut data { *x *= s; }
767 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
768 }
769 ("math", "add") | ("math", "sub") => {
770 let (ar, ac, a) = unpack_matrix(first_arg(args)?)?;
771 let (br, bc, b) = unpack_matrix(args.get(1).ok_or("math.add/sub: missing arg 1")?)?;
772 if ar != br || ac != bc {
773 return Err(format!("math.{op}: shape mismatch {ar}x{ac} vs {br}x{bc}"));
774 }
775 let neg = op == "sub";
776 let mut out = a;
777 for (i, x) in out.iter_mut().enumerate() {
778 if neg { *x -= b[i] } else { *x += b[i] }
779 }
780 Ok(Value::F64Array { rows: ar as u32, cols: ac as u32, data: out })
781 }
782 ("math", "sigmoid") => {
783 let (r, c, mut data) = unpack_matrix(first_arg(args)?)?;
784 for x in &mut data { *x = 1.0 / (1.0 + (-*x).exp()); }
785 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
786 }
787
788 ("map", "new") => Ok(Value::Map(BTreeMap::new())),
790 ("map", "size") => Ok(Value::Int(expect_map(args.first())?.len() as i64)),
791 ("map", "has") => {
792 let m = expect_map(args.first())?;
793 let k = MapKey::from_value(args.get(1).ok_or("map.has: missing key")?)?;
794 Ok(Value::Bool(m.contains_key(&k)))
795 }
796 ("map", "get") => {
797 let m = expect_map(args.first())?;
798 let k = MapKey::from_value(args.get(1).ok_or("map.get: missing key")?)?;
799 Ok(match m.get(&k) {
800 Some(v) => some(v.clone()),
801 None => none(),
802 })
803 }
804 ("map", "set") => {
805 let mut m = expect_map(args.first())?.clone();
806 let k = MapKey::from_value(args.get(1).ok_or("map.set: missing key")?)?;
807 let v = args.get(2).ok_or("map.set: missing value")?.clone();
808 m.insert(k, v);
809 Ok(Value::Map(m))
810 }
811 ("map", "delete") => {
812 let mut m = expect_map(args.first())?.clone();
813 let k = MapKey::from_value(args.get(1).ok_or("map.delete: missing key")?)?;
814 m.remove(&k);
815 Ok(Value::Map(m))
816 }
817 ("map", "keys") => {
818 let m = expect_map(args.first())?;
819 Ok(Value::List(m.keys().cloned().map(MapKey::into_value).collect()))
820 }
821 ("map", "values") => {
822 let m = expect_map(args.first())?;
823 Ok(Value::List(m.values().cloned().collect()))
824 }
825 ("map", "entries") => {
826 let m = expect_map(args.first())?;
827 Ok(Value::List(m.iter()
828 .map(|(k, v)| Value::Tuple(vec![k.as_value(), v.clone()]))
829 .collect()))
830 }
831 ("map", "from_list") => {
832 let pairs = expect_list(args.first())?;
833 let mut m = BTreeMap::new();
834 for p in pairs {
835 let items = match p {
836 Value::Tuple(items) if items.len() == 2 => items,
837 other => return Err(format!(
838 "map.from_list element must be a 2-tuple, got {other:?}")),
839 };
840 let k = MapKey::from_value(&items[0])?;
841 m.insert(k, items[1].clone());
842 }
843 Ok(Value::Map(m))
844 }
845
846 ("set", "new") => Ok(Value::Set(BTreeSet::new())),
848 ("set", "size") => Ok(Value::Int(expect_set(args.first())?.len() as i64)),
849 ("set", "has") => {
850 let s = expect_set(args.first())?;
851 let k = MapKey::from_value(args.get(1).ok_or("set.has: missing element")?)?;
852 Ok(Value::Bool(s.contains(&k)))
853 }
854 ("set", "add") => {
855 let mut s = expect_set(args.first())?.clone();
856 let k = MapKey::from_value(args.get(1).ok_or("set.add: missing element")?)?;
857 s.insert(k);
858 Ok(Value::Set(s))
859 }
860 ("set", "delete") => {
861 let mut s = expect_set(args.first())?.clone();
862 let k = MapKey::from_value(args.get(1).ok_or("set.delete: missing element")?)?;
863 s.remove(&k);
864 Ok(Value::Set(s))
865 }
866 ("set", "to_list") => {
867 let s = expect_set(args.first())?;
868 Ok(Value::List(s.iter().cloned().map(MapKey::into_value).collect()))
869 }
870 ("set", "from_list") => {
871 let xs = expect_list(args.first())?;
872 let mut s = BTreeSet::new();
873 for x in xs {
874 s.insert(MapKey::from_value(x)?);
875 }
876 Ok(Value::Set(s))
877 }
878 ("set", "union") => {
879 let a = expect_set(args.first())?;
880 let b = expect_set(args.get(1))?;
881 Ok(Value::Set(a.union(b).cloned().collect()))
882 }
883 ("set", "intersect") => {
884 let a = expect_set(args.first())?;
885 let b = expect_set(args.get(1))?;
886 Ok(Value::Set(a.intersection(b).cloned().collect()))
887 }
888 ("set", "diff") => {
889 let a = expect_set(args.first())?;
890 let b = expect_set(args.get(1))?;
891 Ok(Value::Set(a.difference(b).cloned().collect()))
892 }
893 ("set", "is_empty") => Ok(Value::Bool(expect_set(args.first())?.is_empty())),
894 ("set", "is_subset") => {
895 let a = expect_set(args.first())?;
896 let b = expect_set(args.get(1))?;
897 Ok(Value::Bool(a.is_subset(b)))
898 }
899
900 ("map", "merge") => {
902 let a = expect_map(args.first())?.clone();
905 let b = expect_map(args.get(1))?;
906 let mut out = a;
907 for (k, v) in b {
908 out.insert(k.clone(), v.clone());
909 }
910 Ok(Value::Map(out))
911 }
912 ("map", "is_empty") => Ok(Value::Bool(expect_map(args.first())?.is_empty())),
913
914 ("deque", "new") => Ok(Value::Deque(std::collections::VecDeque::new())),
916 ("deque", "size") => Ok(Value::Int(expect_deque(args.first())?.len() as i64)),
917 ("deque", "is_empty") => Ok(Value::Bool(expect_deque(args.first())?.is_empty())),
918 ("deque", "push_back") => {
919 let mut d = expect_deque(args.first())?.clone();
920 let x = args.get(1).ok_or("deque.push_back: missing value")?.clone();
921 d.push_back(x);
922 Ok(Value::Deque(d))
923 }
924 ("deque", "push_front") => {
925 let mut d = expect_deque(args.first())?.clone();
926 let x = args.get(1).ok_or("deque.push_front: missing value")?.clone();
927 d.push_front(x);
928 Ok(Value::Deque(d))
929 }
930 ("deque", "pop_back") => {
931 let mut d = expect_deque(args.first())?.clone();
932 match d.pop_back() {
933 Some(x) => Ok(Value::Variant {
934 name: "Some".into(),
935 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
936 }),
937 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
938 }
939 }
940 ("deque", "pop_front") => {
941 let mut d = expect_deque(args.first())?.clone();
942 match d.pop_front() {
943 Some(x) => Ok(Value::Variant {
944 name: "Some".into(),
945 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
946 }),
947 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
948 }
949 }
950 ("deque", "peek_back") => {
951 let d = expect_deque(args.first())?;
952 match d.back() {
953 Some(x) => Ok(Value::Variant {
954 name: "Some".into(),
955 args: vec![x.clone()],
956 }),
957 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
958 }
959 }
960 ("deque", "peek_front") => {
961 let d = expect_deque(args.first())?;
962 match d.front() {
963 Some(x) => Ok(Value::Variant {
964 name: "Some".into(),
965 args: vec![x.clone()],
966 }),
967 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
968 }
969 }
970 ("deque", "from_list") => {
971 let xs = expect_list(args.first())?;
972 Ok(Value::Deque(xs.iter().cloned().collect()))
973 }
974 ("deque", "to_list") => {
975 let d = expect_deque(args.first())?;
976 Ok(Value::List(d.iter().cloned().collect()))
977 }
978
979 ("crypto", "sha256") => {
982 use sha2::{Digest, Sha256};
983 let data = expect_bytes(args.first())?;
984 let mut h = Sha256::new();
985 h.update(data);
986 Ok(Value::Bytes(h.finalize().to_vec()))
987 }
988 ("crypto", "sha512") => {
989 use sha2::{Digest, Sha512};
990 let data = expect_bytes(args.first())?;
991 let mut h = Sha512::new();
992 h.update(data);
993 Ok(Value::Bytes(h.finalize().to_vec()))
994 }
995 ("crypto", "md5") => {
996 use md5::{Digest, Md5};
997 let data = expect_bytes(args.first())?;
998 let mut h = Md5::new();
999 h.update(data);
1000 Ok(Value::Bytes(h.finalize().to_vec()))
1001 }
1002 ("crypto", "hmac_sha256") => {
1003 use hmac::{Hmac, KeyInit, Mac};
1004 type HmacSha256 = Hmac<sha2::Sha256>;
1005 let key = expect_bytes(args.first())?;
1006 let data = expect_bytes(args.get(1))?;
1007 let mut mac = HmacSha256::new_from_slice(key)
1008 .map_err(|e| format!("hmac_sha256 key: {e}"))?;
1009 mac.update(data);
1010 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1011 }
1012 ("crypto", "hmac_sha512") => {
1013 use hmac::{Hmac, KeyInit, Mac};
1014 type HmacSha512 = Hmac<sha2::Sha512>;
1015 let key = expect_bytes(args.first())?;
1016 let data = expect_bytes(args.get(1))?;
1017 let mut mac = HmacSha512::new_from_slice(key)
1018 .map_err(|e| format!("hmac_sha512 key: {e}"))?;
1019 mac.update(data);
1020 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1021 }
1022 ("crypto", "base64_encode") => {
1023 use base64::{Engine, engine::general_purpose::STANDARD};
1024 let data = expect_bytes(args.first())?;
1025 Ok(Value::Str(STANDARD.encode(data)))
1026 }
1027 ("crypto", "base64_decode") => {
1028 use base64::{Engine, engine::general_purpose::STANDARD};
1029 let s = expect_str(args.first())?;
1030 match STANDARD.decode(s) {
1031 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1032 Err(e) => Ok(err_v(Value::Str(format!("base64: {e}")))),
1033 }
1034 }
1035 ("crypto", "hex_encode") => {
1036 let data = expect_bytes(args.first())?;
1037 Ok(Value::Str(hex::encode(data)))
1038 }
1039 ("crypto", "hex_decode") => {
1040 let s = expect_str(args.first())?;
1041 match hex::decode(s) {
1042 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1043 Err(e) => Ok(err_v(Value::Str(format!("hex: {e}")))),
1044 }
1045 }
1046 ("crypto", "constant_time_eq") => {
1047 use subtle::ConstantTimeEq;
1048 let a = expect_bytes(args.first())?;
1049 let b = expect_bytes(args.get(1))?;
1050 let eq = if a.len() == b.len() {
1055 a.ct_eq(b).into()
1056 } else {
1057 false
1058 };
1059 Ok(Value::Bool(eq))
1060 }
1061
1062 ("random", "seed") => {
1069 let s = args.first().ok_or("random.seed: missing arg")?.as_int();
1070 let mixed = splitmix64(s as u64).0;
1075 Ok(rng_value(mixed))
1076 }
1077 ("random", "int") => {
1078 let state = rng_decode(args.first())?;
1079 let lo = args.get(1).ok_or("random.int: missing lo")?.as_int();
1080 let hi = args.get(2).ok_or("random.int: missing hi")?.as_int();
1081 if hi < lo {
1082 return Err(format!(
1083 "random.int: hi ({hi}) must be >= lo ({lo})"));
1084 }
1085 let span = (hi as i128) - (lo as i128) + 1;
1086 let (raw, next_state) = splitmix64(state);
1087 let drawn = lo as i128 + (raw as u128 % span as u128) as i128;
1092 Ok(Value::Tuple(vec![
1093 Value::Int(drawn as i64),
1094 rng_value(next_state),
1095 ]))
1096 }
1097 ("random", "float") => {
1098 let state = rng_decode(args.first())?;
1099 let (raw, next_state) = splitmix64(state);
1100 let f = ((raw >> 11) as f64) / ((1u64 << 53) as f64);
1103 Ok(Value::Tuple(vec![Value::Float(f), rng_value(next_state)]))
1104 }
1105 ("random", "choose") => {
1106 let state = rng_decode(args.first())?;
1107 let xs = match args.get(1) {
1108 Some(Value::List(xs)) => xs,
1109 _ => return Err("random.choose: expected List".into()),
1110 };
1111 if xs.is_empty() {
1112 return Ok(Value::Variant {
1113 name: "None".into(), args: vec![],
1114 });
1115 }
1116 let (raw, next_state) = splitmix64(state);
1117 let idx = (raw as usize) % xs.len();
1118 let pick = xs[idx].clone();
1119 Ok(Value::Variant {
1120 name: "Some".into(),
1121 args: vec![Value::Tuple(vec![pick, rng_value(next_state)])],
1122 })
1123 }
1124
1125 ("parser", "char") => {
1130 let s = expect_str(args.first())?;
1131 if s.chars().count() != 1 {
1132 return Err(format!(
1133 "parser.char: expected 1-character string, got {s:?}"));
1134 }
1135 Ok(parser_node("Char", &[("ch", Value::Str(s))]))
1136 }
1137 ("parser", "string") => {
1138 let s = expect_str(args.first())?;
1139 Ok(parser_node("String", &[("s", Value::Str(s))]))
1140 }
1141 ("parser", "digit") => Ok(parser_node("Digit", &[])),
1142 ("parser", "alpha") => Ok(parser_node("Alpha", &[])),
1143 ("parser", "whitespace") => Ok(parser_node("Whitespace", &[])),
1144 ("parser", "eof") => Ok(parser_node("Eof", &[])),
1145 ("parser", "seq") => {
1146 let a = args.first().cloned()
1147 .ok_or_else(|| "parser.seq: missing first parser".to_string())?;
1148 let b = args.get(1).cloned()
1149 .ok_or_else(|| "parser.seq: missing second parser".to_string())?;
1150 Ok(parser_node("Seq", &[("a", a), ("b", b)]))
1151 }
1152 ("parser", "alt") => {
1153 let a = args.first().cloned()
1154 .ok_or_else(|| "parser.alt: missing first parser".to_string())?;
1155 let b = args.get(1).cloned()
1156 .ok_or_else(|| "parser.alt: missing second parser".to_string())?;
1157 Ok(parser_node("Alt", &[("a", a), ("b", b)]))
1158 }
1159 ("parser", "many") => {
1160 let p = args.first().cloned()
1161 .ok_or_else(|| "parser.many: missing inner parser".to_string())?;
1162 Ok(parser_node("Many", &[("p", p)]))
1163 }
1164 ("parser", "optional") => {
1165 let p = args.first().cloned()
1166 .ok_or_else(|| "parser.optional: missing inner parser".to_string())?;
1167 Ok(parser_node("Optional", &[("p", p)]))
1168 }
1169 ("parser", "map") => {
1173 let p = args.first().cloned()
1174 .ok_or_else(|| "parser.map: missing parser".to_string())?;
1175 let f = args.get(1).cloned()
1176 .ok_or_else(|| "parser.map: missing closure".to_string())?;
1177 Ok(parser_node("Map", &[("p", p), ("f", f)]))
1178 }
1179 ("parser", "and_then") => {
1180 let p = args.first().cloned()
1181 .ok_or_else(|| "parser.and_then: missing parser".to_string())?;
1182 let f = args.get(1).cloned()
1183 .ok_or_else(|| "parser.and_then: missing closure".to_string())?;
1184 Ok(parser_node("AndThen", &[("p", p), ("f", f)]))
1185 }
1186 ("regex", "compile") => {
1196 let pat = expect_str(args.first())?;
1197 match get_or_compile_regex(&pat) {
1198 Ok(_) => Ok(ok_v(Value::Str(pat))),
1199 Err(e) => Ok(err_v(Value::Str(e))),
1200 }
1201 }
1202 ("regex", "is_match") => {
1203 let pat = expect_str(args.first())?;
1204 let s = expect_str(args.get(1))?;
1205 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.is_match: {e}"))?;
1206 Ok(Value::Bool(re.is_match(&s)))
1207 }
1208 ("regex", "is_match_str") => {
1213 let pat = expect_str(args.first())?;
1214 let s = expect_str(args.get(1))?;
1215 match get_or_compile_regex(&pat) {
1216 Ok(re) => Ok(Value::Bool(re.is_match(&s))),
1217 Err(_) => Ok(Value::Bool(false)),
1218 }
1219 }
1220 ("regex", "find") => {
1221 let pat = expect_str(args.first())?;
1222 let s = expect_str(args.get(1))?;
1223 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find: {e}"))?;
1224 match re.captures(&s) {
1225 Some(caps) => Ok(Value::Variant {
1226 name: "Some".into(),
1227 args: vec![match_value(&caps)],
1228 }),
1229 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1230 }
1231 }
1232 ("regex", "find_all") => {
1233 let pat = expect_str(args.first())?;
1234 let s = expect_str(args.get(1))?;
1235 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find_all: {e}"))?;
1236 let items: Vec<Value> = re.captures_iter(&s).map(|caps| match_value(&caps)).collect();
1237 Ok(Value::List(items))
1238 }
1239 ("regex", "replace") => {
1240 let pat = expect_str(args.first())?;
1241 let s = expect_str(args.get(1))?;
1242 let rep = expect_str(args.get(2))?;
1243 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace: {e}"))?;
1244 Ok(Value::Str(re.replace(&s, rep.as_str()).into_owned()))
1245 }
1246 ("regex", "replace_all") => {
1247 let pat = expect_str(args.first())?;
1248 let s = expect_str(args.get(1))?;
1249 let rep = expect_str(args.get(2))?;
1250 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace_all: {e}"))?;
1251 Ok(Value::Str(re.replace_all(&s, rep.as_str()).into_owned()))
1252 }
1253 ("datetime", "parse_iso") => {
1256 let s = expect_str(args.first())?;
1257 match chrono::DateTime::parse_from_rfc3339(&s) {
1258 Ok(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1259 Err(e) => Ok(err_v(Value::Str(format!("parse_iso: {e}")))),
1260 }
1261 }
1262 ("datetime", "format_iso") => {
1263 let n = expect_int(args.first())?;
1264 Ok(Value::Str(format_iso(n)))
1265 }
1266 ("datetime", "parse") => {
1267 let s = expect_str(args.first())?;
1268 let fmt = expect_str(args.get(1))?;
1269 match chrono::NaiveDateTime::parse_from_str(&s, &fmt) {
1270 Ok(naive) => {
1271 use chrono::TimeZone;
1272 match chrono::Utc.from_local_datetime(&naive).single() {
1273 Some(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1274 None => Ok(err_v(Value::Str("parse: ambiguous local time".into()))),
1275 }
1276 }
1277 Err(e) => Ok(err_v(Value::Str(format!("parse: {e}")))),
1278 }
1279 }
1280 ("datetime", "format") => {
1281 let n = expect_int(args.first())?;
1282 let fmt = expect_str(args.get(1))?;
1283 let dt = chrono_from_instant(n);
1284 Ok(Value::Str(dt.format(&fmt).to_string()))
1285 }
1286 ("datetime", "to_components") => {
1287 let n = expect_int(args.first())?;
1288 let tz = match parse_tz_arg(args.get(1)) {
1289 Ok(t) => t,
1290 Err(e) => return Ok(err_v(Value::Str(e))),
1291 };
1292 match resolve_tz_to_components(n, &tz) {
1293 Ok(rec) => Ok(ok_v(rec)),
1294 Err(e) => Ok(err_v(Value::Str(e))),
1295 }
1296 }
1297 ("datetime", "from_components") => {
1298 let rec = match args.first() {
1299 Some(Value::Record(r)) => r.clone(),
1300 _ => return Err("from_components: expected DateTime record".into()),
1301 };
1302 match instant_from_components(&rec) {
1303 Ok(n) => Ok(ok_v(Value::Int(n))),
1304 Err(e) => Ok(err_v(Value::Str(e))),
1305 }
1306 }
1307 ("datetime", "add") => {
1308 let a = expect_int(args.first())?;
1309 let d = expect_int(args.get(1))?;
1310 Ok(Value::Int(a.saturating_add(d)))
1311 }
1312 ("datetime", "diff") => {
1313 let a = expect_int(args.first())?;
1314 let b = expect_int(args.get(1))?;
1315 Ok(Value::Int(a.saturating_sub(b)))
1316 }
1317 ("datetime", "duration_seconds") => {
1318 let s = expect_float(args.first())?;
1319 let nanos = (s * 1_000_000_000.0) as i64;
1320 Ok(Value::Int(nanos))
1321 }
1322 ("datetime", "duration_minutes") => {
1323 let m = expect_int(args.first())?;
1324 Ok(Value::Int(m.saturating_mul(60_000_000_000)))
1325 }
1326 ("datetime", "duration_days") => {
1327 let d = expect_int(args.first())?;
1328 Ok(Value::Int(d.saturating_mul(86_400_000_000_000)))
1329 }
1330
1331 ("regex", "split") => {
1332 let pat = expect_str(args.first())?;
1333 let s = expect_str(args.get(1))?;
1334 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.split: {e}"))?;
1335 let parts: Vec<Value> = re.split(&s).map(|p| Value::Str(p.to_string())).collect();
1336 Ok(Value::List(parts))
1337 }
1338
1339 ("http", "with_header") => {
1342 let req = expect_record_pure(args.first())?.clone();
1343 let k = expect_str(args.get(1))?;
1344 let v = expect_str(args.get(2))?;
1345 Ok(Value::Record(http_set_header(req, &k, &v)))
1346 }
1347 ("http", "with_auth") => {
1348 let req = expect_record_pure(args.first())?.clone();
1349 let scheme = expect_str(args.get(1))?;
1350 let token = expect_str(args.get(2))?;
1351 let value = format!("{scheme} {token}");
1352 Ok(Value::Record(http_set_header(req, "Authorization", &value)))
1353 }
1354 ("http", "with_query") => {
1355 let req = expect_record_pure(args.first())?.clone();
1356 let params = match args.get(1) {
1357 Some(Value::Map(m)) => m.clone(),
1358 Some(other) => return Err(format!(
1359 "http.with_query: params must be Map[Str, Str], got {other:?}")),
1360 None => return Err("http.with_query: missing params argument".into()),
1361 };
1362 Ok(Value::Record(http_append_query(req, ¶ms)))
1363 }
1364 ("http", "with_timeout_ms") => {
1365 let req = expect_record_pure(args.first())?.clone();
1366 let ms = expect_int(args.get(1))?;
1367 let mut out = req;
1368 out.insert("timeout_ms".into(), Value::Variant {
1369 name: "Some".into(),
1370 args: vec![Value::Int(ms)],
1371 });
1372 Ok(Value::Record(out))
1373 }
1374 ("http", "json_body") => {
1375 let resp = expect_record_pure(args.first())?;
1376 let body = match resp.get("body") {
1377 Some(Value::Bytes(b)) => b.clone(),
1378 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1379 };
1380 let s = match std::str::from_utf8(&body) {
1381 Ok(s) => s,
1382 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1383 };
1384 match serde_json::from_str::<serde_json::Value>(s) {
1385 Ok(j) => Ok(ok_v(Value::from_json(&j))),
1386 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1387 }
1388 }
1389 ("http", "text_body") => {
1390 let resp = expect_record_pure(args.first())?;
1391 let body = match resp.get("body") {
1392 Some(Value::Bytes(b)) => b.clone(),
1393 _ => return Err("http.text_body: HttpResponse.body must be Bytes".into()),
1394 };
1395 match String::from_utf8(body) {
1396 Ok(s) => Ok(ok_v(Value::Str(s))),
1397 Err(e) => Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1398 }
1399 }
1400
1401 ("cli", "flag") => {
1405 let name = expect_str(args.first())?;
1406 let short = opt_str(args.get(1));
1407 let help = expect_str(args.get(2))?;
1408 Ok(value_from_json(crate::cli::flag_spec(&name, short.as_deref(), &help)))
1409 }
1410 ("cli", "option") => {
1411 let name = expect_str(args.first())?;
1412 let short = opt_str(args.get(1));
1413 let help = expect_str(args.get(2))?;
1414 let default = opt_str(args.get(3));
1415 Ok(value_from_json(crate::cli::option_spec(&name, short.as_deref(), &help, default.as_deref())))
1416 }
1417 ("cli", "positional") => {
1418 let name = expect_str(args.first())?;
1419 let help = expect_str(args.get(1))?;
1420 let required = expect_bool(args.get(2))?;
1421 Ok(value_from_json(crate::cli::positional_spec(&name, &help, required)))
1422 }
1423 ("cli", "spec") => {
1424 let name = expect_str(args.first())?;
1425 let help = expect_str(args.get(1))?;
1426 let arg_specs: Vec<serde_json::Value> = expect_list(args.get(2))?
1427 .iter().map(value_to_json).collect();
1428 let subs: Vec<serde_json::Value> = expect_list(args.get(3))?
1429 .iter().map(value_to_json).collect();
1430 Ok(value_from_json(crate::cli::build_spec(&name, &help, arg_specs, subs)))
1431 }
1432 ("cli", "parse") => {
1433 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1434 let argv: Vec<String> = expect_list(args.get(1))?
1435 .iter().map(|v| match v {
1436 Value::Str(s) => Ok(s.clone()),
1437 other => Err(format!("cli.parse: argv must be List[Str], got {other:?}")),
1438 }).collect::<Result<_, _>>()?;
1439 match crate::cli::parse(&spec, &argv) {
1440 Ok(parsed) => Ok(ok_v(value_from_json(parsed))),
1441 Err(msg) => Ok(err_v(Value::Str(msg))),
1442 }
1443 }
1444 ("cli", "envelope") => {
1445 let ok = expect_bool(args.first())?;
1446 let cmd = expect_str(args.get(1))?;
1447 let data = value_to_json(args.get(2).unwrap_or(&Value::Unit));
1448 Ok(value_from_json(crate::cli::envelope(ok, &cmd, data)))
1449 }
1450 ("cli", "describe") => {
1451 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1452 Ok(value_from_json(crate::cli::describe(&spec)))
1453 }
1454 ("cli", "help") => {
1455 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1456 Ok(Value::Str(crate::cli::help_text(&spec)))
1457 }
1458
1459 _ => Err(format!("unknown pure builtin: {kind}.{op}")),
1460 }
1461}
1462
1463fn opt_str(arg: Option<&Value>) -> Option<String> {
1467 match arg {
1468 Some(Value::Variant { name, args }) if name == "Some" => {
1469 args.first().and_then(|v| match v {
1470 Value::Str(s) => Some(s.clone()),
1471 _ => None,
1472 })
1473 }
1474 _ => None,
1475 }
1476}
1477
1478fn value_from_json(v: serde_json::Value) -> Value { Value::from_json(&v) }
1479
1480fn regex_cache() -> &'static Mutex<HashMap<String, regex::Regex>> {
1485 static CACHE: OnceLock<Mutex<HashMap<String, regex::Regex>>> = OnceLock::new();
1486 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
1487}
1488
1489fn get_or_compile_regex(pattern: &str) -> Result<regex::Regex, String> {
1490 let cache = regex_cache();
1491 {
1492 let guard = cache.lock().unwrap();
1493 if let Some(re) = guard.get(pattern) {
1494 return Ok(re.clone());
1495 }
1496 }
1497 let re = regex::Regex::new(pattern).map_err(|e| format!("invalid regex: {e}"))?;
1498 let mut guard = cache.lock().unwrap();
1499 guard.insert(pattern.to_string(), re.clone());
1500 Ok(re)
1501}
1502
1503fn match_value(caps: ®ex::Captures) -> Value {
1507 let m0 = caps.get(0).expect("regex match always has group 0");
1508 let mut rec = indexmap::IndexMap::new();
1509 rec.insert("text".into(), Value::Str(m0.as_str().to_string()));
1510 rec.insert("start".into(), Value::Int(m0.start() as i64));
1511 rec.insert("end".into(), Value::Int(m0.end() as i64));
1512 let groups: Vec<Value> = (1..caps.len())
1513 .map(|i| {
1514 Value::Str(
1515 caps.get(i)
1516 .map(|m| m.as_str().to_string())
1517 .unwrap_or_default(),
1518 )
1519 })
1520 .collect();
1521 rec.insert("groups".into(), Value::List(groups));
1522 Value::Record(rec)
1523}
1524
1525fn expect_map(v: Option<&Value>) -> Result<&BTreeMap<MapKey, Value>, String> {
1526 match v {
1527 Some(Value::Map(m)) => Ok(m),
1528 other => Err(format!("expected Map, got {other:?}")),
1529 }
1530}
1531
1532fn expect_set(v: Option<&Value>) -> Result<&BTreeSet<MapKey>, String> {
1533 match v {
1534 Some(Value::Set(s)) => Ok(s),
1535 other => Err(format!("expected Set, got {other:?}")),
1536 }
1537}
1538
1539fn unpack_matrix(v: &Value) -> Result<(usize, usize, Vec<f64>), String> {
1543 if let Value::F64Array { rows, cols, data } = v {
1544 return Ok((*rows as usize, *cols as usize, data.clone()));
1545 }
1546 let rec = match v {
1547 Value::Record(r) => r,
1548 other => return Err(format!("expected matrix, got {other:?}")),
1549 };
1550 let rows = match rec.get("rows") {
1551 Some(Value::Int(n)) => *n as usize,
1552 _ => return Err("matrix: missing/invalid `rows`".into()),
1553 };
1554 let cols = match rec.get("cols") {
1555 Some(Value::Int(n)) => *n as usize,
1556 _ => return Err("matrix: missing/invalid `cols`".into()),
1557 };
1558 let data = match rec.get("data") {
1559 Some(Value::List(items)) => {
1560 let mut out = Vec::with_capacity(items.len());
1561 for it in items {
1562 out.push(match it {
1563 Value::Float(f) => *f,
1564 Value::Int(n) => *n as f64,
1565 other => return Err(format!("matrix data: not numeric, got {other:?}")),
1566 });
1567 }
1568 out
1569 }
1570 _ => return Err("matrix: missing/invalid `data`".into()),
1571 };
1572 if data.len() != rows * cols {
1573 return Err(format!("matrix: data len {} != {rows}*{cols}", data.len()));
1574 }
1575 Ok((rows, cols, data))
1576}
1577
1578fn expect_bytes(v: Option<&Value>) -> Result<&Vec<u8>, String> {
1579 match v {
1580 Some(Value::Bytes(b)) => Ok(b),
1581 Some(other) => Err(format!("expected Bytes, got {other:?}")),
1582 None => Err("missing argument".into()),
1583 }
1584}
1585
1586fn first_arg(args: &[Value]) -> Result<&Value, String> {
1587 args.first().ok_or_else(|| "missing argument".into())
1588}
1589
1590fn tuple_index(v: &Value, i: usize) -> Result<Value, String> {
1591 match v {
1592 Value::Tuple(items) => items.get(i).cloned()
1593 .ok_or_else(|| format!("tuple index {i} out of range (len={})", items.len())),
1594 other => Err(format!("expected Tuple, got {other:?}")),
1595 }
1596}
1597
1598fn expect_str(v: Option<&Value>) -> Result<String, String> {
1599 match v {
1600 Some(Value::Str(s)) => Ok(s.clone()),
1601 Some(other) => Err(format!("expected Str, got {other:?}")),
1602 None => Err("missing argument".into()),
1603 }
1604}
1605
1606fn expect_int(v: Option<&Value>) -> Result<i64, String> {
1607 match v {
1608 Some(Value::Int(n)) => Ok(*n),
1609 Some(other) => Err(format!("expected Int, got {other:?}")),
1610 None => Err("missing argument".into()),
1611 }
1612}
1613
1614fn expect_float(v: Option<&Value>) -> Result<f64, String> {
1615 match v {
1616 Some(Value::Float(f)) => Ok(*f),
1617 Some(other) => Err(format!("expected Float, got {other:?}")),
1618 None => Err("missing argument".into()),
1619 }
1620}
1621
1622fn expect_list(v: Option<&Value>) -> Result<&Vec<Value>, String> {
1623 match v {
1624 Some(Value::List(xs)) => Ok(xs),
1625 Some(other) => Err(format!("expected List, got {other:?}")),
1626 None => Err("missing argument".into()),
1627 }
1628}
1629
1630fn expect_bool(v: Option<&Value>) -> Result<bool, String> {
1631 match v {
1632 Some(Value::Bool(b)) => Ok(*b),
1633 Some(other) => Err(format!("expected Bool, got {other:?}")),
1634 None => Err("missing argument".into()),
1635 }
1636}
1637
1638fn expect_deque(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
1639 match v {
1640 Some(Value::Deque(d)) => Ok(d),
1641 Some(other) => Err(format!("expected Deque, got {other:?}")),
1642 None => Err("missing argument".into()),
1643 }
1644}
1645
1646fn some(v: Value) -> Value { Value::Variant { name: "Some".into(), args: vec![v] } }
1647fn none() -> Value { Value::Variant { name: "None".into(), args: Vec::new() } }
1648fn ok_v(v: Value) -> Value { Value::Variant { name: "Ok".into(), args: vec![v] } }
1649fn err_v(v: Value) -> Value { Value::Variant { name: "Err".into(), args: vec![v] } }
1650
1651fn parser_node(kind: &str, fields: &[(&str, Value)]) -> Value {
1659 let mut r = indexmap::IndexMap::new();
1660 r.insert("kind".into(), Value::Str(kind.into()));
1661 for (k, v) in fields {
1662 r.insert((*k).into(), v.clone());
1663 }
1664 Value::Record(r)
1665}
1666
1667fn splitmix64(state: u64) -> (u64, u64) {
1678 let next = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
1679 let mut z = next;
1680 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
1681 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
1682 let z = z ^ (z >> 31);
1683 (z, next)
1684}
1685
1686fn rng_value(state: u64) -> Value {
1690 let mut fields = indexmap::IndexMap::new();
1691 fields.insert("state".into(), Value::Int(state as i64));
1692 Value::Record(fields)
1693}
1694
1695fn rng_decode(v: Option<&Value>) -> Result<u64, String> {
1697 let rec = match v {
1698 Some(Value::Record(r)) => r,
1699 Some(other) => return Err(format!("expected Rng, got {other:?}")),
1700 None => return Err("missing Rng arg".into()),
1701 };
1702 match rec.get("state") {
1703 Some(Value::Int(n)) => Ok(*n as u64),
1704 _ => Err("malformed Rng: missing `state :: Int`".into()),
1705 }
1706}
1707
1708fn expect_record_pure(v: Option<&Value>) -> Result<&indexmap::IndexMap<String, Value>, String> {
1711 match v {
1712 Some(Value::Record(r)) => Ok(r),
1713 Some(other) => Err(format!("expected Record, got {other:?}")),
1714 None => Err("missing Record argument".into()),
1715 }
1716}
1717
1718fn http_decode_err_pure(msg: String) -> Value {
1719 let inner = Value::Variant {
1720 name: "DecodeError".into(),
1721 args: vec![Value::Str(msg)],
1722 };
1723 err_v(inner)
1724}
1725
1726fn http_set_header(
1731 mut req: indexmap::IndexMap<String, Value>,
1732 name: &str,
1733 value: &str,
1734) -> indexmap::IndexMap<String, Value> {
1735 use lex_bytecode::MapKey;
1736 let mut headers = match req.shift_remove("headers") {
1737 Some(Value::Map(m)) => m,
1738 _ => std::collections::BTreeMap::new(),
1739 };
1740 let key = MapKey::Str(name.to_lowercase());
1741 let lowered = name.to_lowercase();
1744 headers.retain(|k, _| match k {
1745 MapKey::Str(s) => s.to_lowercase() != lowered,
1746 _ => true,
1747 });
1748 headers.insert(key, Value::Str(value.to_string()));
1749 req.insert("headers".into(), Value::Map(headers));
1750 req
1751}
1752
1753fn http_append_query(
1759 mut req: indexmap::IndexMap<String, Value>,
1760 params: &std::collections::BTreeMap<lex_bytecode::MapKey, Value>,
1761) -> indexmap::IndexMap<String, Value> {
1762 use lex_bytecode::MapKey;
1763 let url = match req.get("url") {
1764 Some(Value::Str(s)) => s.clone(),
1765 _ => return req,
1766 };
1767 let mut pieces = Vec::new();
1768 for (k, v) in params {
1769 let kk = match k { MapKey::Str(s) => s.clone(), _ => continue };
1770 let vv = match v { Value::Str(s) => s.clone(), _ => continue };
1771 pieces.push(format!("{}={}", url_encode(&kk), url_encode(&vv)));
1772 }
1773 if pieces.is_empty() { return req; }
1774 let sep = if url.contains('?') { '&' } else { '?' };
1775 let new_url = format!("{url}{sep}{}", pieces.join("&"));
1776 req.insert("url".into(), Value::Str(new_url));
1777 req
1778}
1779
1780fn url_encode(s: &str) -> String {
1785 let mut out = String::with_capacity(s.len());
1786 for b in s.bytes() {
1787 match b {
1788 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
1789 out.push(b as char);
1790 }
1791 _ => out.push_str(&format!("%{:02X}", b)),
1792 }
1793 }
1794 out
1795}
1796
1797fn value_to_json(v: &Value) -> serde_json::Value { v.to_json() }
1798
1799fn unwrap_toml_datetime_markers(v: &mut serde_json::Value) {
1807 use serde_json::Value as J;
1808 match v {
1809 J::Object(map) => {
1810 if map.len() == 1 {
1814 if let Some(J::String(s)) = map.get("$__toml_private_datetime") {
1815 let s = s.clone();
1816 *v = J::String(s);
1817 return;
1818 }
1819 }
1820 for (_, child) in map.iter_mut() {
1821 unwrap_toml_datetime_markers(child);
1822 }
1823 }
1824 J::Array(items) => {
1825 for item in items.iter_mut() {
1826 unwrap_toml_datetime_markers(item);
1827 }
1828 }
1829 _ => {}
1830 }
1831}
1832
1833fn json_to_value(v: &serde_json::Value) -> Value { Value::from_json(v) }
1834
1835fn required_field_names(arg: Option<&Value>) -> Result<Vec<String>, String> {
1840 let list = expect_list(arg)?;
1841 let mut out = Vec::with_capacity(list.len());
1842 for v in list {
1843 match v {
1844 Value::Str(s) => out.push(s.clone()),
1845 other => return Err(format!(
1846 "parse_strict: required-fields list must contain Str, got {other:?}"
1847 )),
1848 }
1849 }
1850 Ok(out)
1851}
1852
1853fn check_required_fields(
1874 value: &serde_json::Value,
1875 required: &[String],
1876) -> Result<(), String> {
1877 if required.is_empty() {
1878 return Ok(());
1879 }
1880 if !matches!(value, serde_json::Value::Object(_)) {
1881 return Err(format!(
1882 "parse_strict: expected top-level object with fields {:?}, got {value}",
1883 required
1884 ));
1885 }
1886 let mut missing: Vec<String> = Vec::new();
1887 for path in required {
1888 if !path_exists(value, path) {
1889 missing.push(path.clone());
1890 }
1891 }
1892 if missing.is_empty() {
1893 Ok(())
1894 } else {
1895 Err(format!("missing required field(s): {}", missing.join(", ")))
1896 }
1897}
1898
1899fn path_exists(value: &serde_json::Value, path: &str) -> bool {
1904 let mut cursor = value;
1905 let segments = split_dotted_path(path);
1906 for seg in &segments {
1907 match cursor {
1908 serde_json::Value::Object(o) => match o.get(seg.as_str()) {
1909 Some(next) => cursor = next,
1910 None => return false,
1911 },
1912 _ => return false,
1913 }
1914 }
1915 true
1916}
1917
1918fn split_dotted_path(path: &str) -> Vec<String> {
1922 let mut out: Vec<String> = Vec::new();
1923 let mut cur = String::new();
1924 let mut iter = path.chars().peekable();
1925 while let Some(c) = iter.next() {
1926 if c == '\\' {
1927 if let Some(&'.') = iter.peek() {
1929 cur.push('.');
1930 iter.next();
1931 continue;
1932 }
1933 cur.push(c);
1934 } else if c == '.' {
1935 out.push(std::mem::take(&mut cur));
1936 } else {
1937 cur.push(c);
1938 }
1939 }
1940 out.push(cur);
1941 out
1942}
1943
1944fn extract_type_schema(v: Option<&Value>) -> Vec<(String, String)> {
1948 match v {
1949 Some(Value::List(pairs)) => pairs.iter().filter_map(|p| {
1950 if let Value::Tuple(items) = p {
1951 if items.len() == 2 {
1952 if let (Value::Str(name), Value::Str(tag)) = (&items[0], &items[1]) {
1953 return Some((name.clone(), tag.clone()));
1954 }
1955 }
1956 }
1957 None
1958 }).collect(),
1959 _ => vec![],
1960 }
1961}
1962
1963fn validate_field_types(
1969 json: &serde_json::Value,
1970 schema: &[(String, String)],
1971) -> Result<(), String> {
1972 if schema.is_empty() {
1973 return Ok(());
1974 }
1975 let obj = match json.as_object() {
1976 Some(o) => o,
1977 None => return Ok(()), };
1979 for (field, tag) in schema {
1980 if let Some(val) = obj.get(field) {
1981 if let Err(e) = check_json_type(val, tag) {
1982 return Err(format!("field `{field}`: {e}"));
1983 }
1984 }
1985 }
1986 Ok(())
1987}
1988
1989fn check_json_type(val: &serde_json::Value, tag: &str) -> Result<(), String> {
1991 use serde_json::Value as J;
1992 match (tag, val) {
1993 ("Int", J::Number(n)) if n.is_i64() || n.is_u64() => Ok(()),
1994 ("Int", other) => Err(format!("expected Int, got {}", json_type_name(other))),
1995 ("Float", J::Number(_)) => Ok(()),
1996 ("Float", other) => Err(format!("expected Float, got {}", json_type_name(other))),
1997 ("Bool", J::Bool(_)) => Ok(()),
1998 ("Bool", other) => Err(format!("expected Bool, got {}", json_type_name(other))),
1999 ("Str", J::String(_)) => Ok(()),
2000 ("Str", other) => Err(format!("expected Str, got {}", json_type_name(other))),
2001 (tag, J::Null) if tag.starts_with("Option[") => Ok(()),
2003 (tag, val) if tag.starts_with("Option[") && tag.ends_with(']') => {
2004 let inner = &tag[7..tag.len() - 1]; check_json_type(val, inner)
2006 }
2007 (tag, J::Array(items)) if tag.starts_with("List[") && tag.ends_with(']') => {
2009 let inner = &tag[5..tag.len() - 1]; for (i, item) in items.iter().enumerate() {
2011 if let Err(e) = check_json_type(item, inner) {
2012 return Err(format!("[{i}]: {e}"));
2013 }
2014 }
2015 Ok(())
2016 }
2017 ("Record", _) => Ok(()), ("Any", _) => Ok(()), _ => Ok(()), }
2021}
2022
2023fn json_type_name(v: &serde_json::Value) -> &'static str {
2024 match v {
2025 serde_json::Value::Null => "null",
2026 serde_json::Value::Bool(_) => "Bool",
2027 serde_json::Value::Number(_) => "Number",
2028 serde_json::Value::String(_) => "Str",
2029 serde_json::Value::Array(_) => "Array",
2030 serde_json::Value::Object(_) => "Object",
2031 }
2032}
2033
2034fn parse_dotenv(src: &str) -> Result<indexmap::IndexMap<String, String>, String> {
2045 let mut out = indexmap::IndexMap::new();
2046 for (idx, raw) in src.lines().enumerate() {
2047 let line = raw.trim();
2048 if line.is_empty() || line.starts_with('#') {
2049 continue;
2050 }
2051 let after_export = line.strip_prefix("export ").unwrap_or(line);
2054 let (k, v) = match after_export.split_once('=') {
2055 Some(kv) => kv,
2056 None => return Err(format!("dotenv.parse line {}: missing `=`", idx + 1)),
2057 };
2058 let key = k.trim();
2059 if key.is_empty() {
2060 return Err(format!("dotenv.parse line {}: empty key", idx + 1));
2061 }
2062 let v_trim = v.trim();
2063 let value = if let Some(q) = v_trim.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
2064 q.to_string()
2065 } else if let Some(q) = v_trim.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
2066 q.to_string()
2067 } else {
2068 v_trim.to_string()
2069 };
2070 out.insert(key.to_string(), value);
2071 }
2072 Ok(out)
2073}
2074
2075fn instant_from_chrono<Tz: chrono::TimeZone>(dt: chrono::DateTime<Tz>) -> i64 {
2081 dt.timestamp_nanos_opt().unwrap_or(i64::MAX)
2082}
2083
2084fn chrono_from_instant(n: i64) -> chrono::DateTime<chrono::Utc> {
2085 let secs = n.div_euclid(1_000_000_000);
2086 let nanos = n.rem_euclid(1_000_000_000) as u32;
2087 use chrono::TimeZone;
2088 chrono::Utc
2089 .timestamp_opt(secs, nanos)
2090 .single()
2091 .unwrap_or_else(chrono::Utc::now)
2092}
2093
2094fn format_iso(n: i64) -> String {
2095 chrono_from_instant(n).to_rfc3339()
2096}
2097
2098enum TzArg {
2101 Utc,
2102 Local,
2103 Offset(i32),
2105 Iana(String),
2107}
2108
2109fn parse_tz_arg(v: Option<&Value>) -> Result<TzArg, String> {
2110 match v {
2111 Some(Value::Variant { name, args }) => match (name.as_str(), args.as_slice()) {
2112 ("Utc", []) => Ok(TzArg::Utc),
2113 ("Local", []) => Ok(TzArg::Local),
2114 ("Offset", [Value::Int(m)]) => {
2115 let m = i32::try_from(*m).map_err(|_| {
2116 format!("Tz::Offset: minutes out of range: {m}")
2117 })?;
2118 Ok(TzArg::Offset(m))
2119 }
2120 ("Iana", [Value::Str(s)]) => Ok(TzArg::Iana(s.clone())),
2121 (other, _) => Err(format!(
2122 "expected Tz variant (Utc | Local | Offset(Int) | Iana(Str)), got `{other}` with {} arg(s)",
2123 args.len()
2124 )),
2125 },
2126 Some(other) => Err(format!("expected Tz variant, got {other:?}")),
2127 None => Err("missing Tz argument".into()),
2128 }
2129}
2130
2131fn resolve_tz_to_components(n: i64, tz: &TzArg) -> Result<Value, String> {
2132 use chrono::{TimeZone, Datelike, Timelike, Offset};
2133 let utc_dt = chrono_from_instant(n);
2134 let (y, m, d, hh, mm, ss, ns, off_min) = match tz {
2135 TzArg::Utc => {
2136 let d = utc_dt;
2137 (d.year(), d.month() as i32, d.day() as i32,
2138 d.hour() as i32, d.minute() as i32, d.second() as i32,
2139 d.nanosecond() as i32, 0)
2140 }
2141 TzArg::Local => {
2142 let d = utc_dt.with_timezone(&chrono::Local);
2143 let off = d.offset().fix().local_minus_utc() / 60;
2144 (d.year(), d.month() as i32, d.day() as i32,
2145 d.hour() as i32, d.minute() as i32, d.second() as i32,
2146 d.nanosecond() as i32, off)
2147 }
2148 TzArg::Offset(off_min) => {
2149 let off_secs = off_min.saturating_mul(60);
2150 let fixed = chrono::FixedOffset::east_opt(off_secs)
2151 .ok_or("to_components: offset out of range")?;
2152 let d = utc_dt.with_timezone(&fixed);
2153 (d.year(), d.month() as i32, d.day() as i32,
2154 d.hour() as i32, d.minute() as i32, d.second() as i32,
2155 d.nanosecond() as i32, *off_min)
2156 }
2157 TzArg::Iana(name) => {
2158 let tz: chrono_tz::Tz = name.parse()
2159 .map_err(|e| format!("to_components: unknown timezone `{name}`: {e}"))?;
2160 let d = utc_dt.with_timezone(&tz);
2161 let off = d.offset().fix().local_minus_utc() / 60;
2162 (d.year(), d.month() as i32, d.day() as i32,
2163 d.hour() as i32, d.minute() as i32, d.second() as i32,
2164 d.nanosecond() as i32, off)
2165 }
2166 };
2167 let mut rec = indexmap::IndexMap::new();
2168 rec.insert("year".into(), Value::Int(y as i64));
2169 rec.insert("month".into(), Value::Int(m as i64));
2170 rec.insert("day".into(), Value::Int(d as i64));
2171 rec.insert("hour".into(), Value::Int(hh as i64));
2172 rec.insert("minute".into(), Value::Int(mm as i64));
2173 rec.insert("second".into(), Value::Int(ss as i64));
2174 rec.insert("nano".into(), Value::Int(ns as i64));
2175 rec.insert("tz_offset_minutes".into(), Value::Int(off_min as i64));
2176 let _ = chrono::Utc.timestamp_opt(0, 0); Ok(Value::Record(rec))
2178}
2179
2180
2181fn instant_from_components(rec: &indexmap::IndexMap<String, Value>) -> Result<i64, String> {
2182 use chrono::TimeZone;
2183 fn get_int(rec: &indexmap::IndexMap<String, Value>, k: &str) -> Result<i64, String> {
2184 match rec.get(k) {
2185 Some(Value::Int(n)) => Ok(*n),
2186 other => Err(format!("from_components: missing or non-int field `{k}`: {other:?}")),
2187 }
2188 }
2189 let y = get_int(rec, "year")? as i32;
2190 let m = get_int(rec, "month")? as u32;
2191 let d = get_int(rec, "day")? as u32;
2192 let hh = get_int(rec, "hour")? as u32;
2193 let mm = get_int(rec, "minute")? as u32;
2194 let ss = get_int(rec, "second")? as u32;
2195 let ns = get_int(rec, "nano")? as u32;
2196 let off_min = get_int(rec, "tz_offset_minutes")? as i32;
2197 let off = chrono::FixedOffset::east_opt(off_min * 60)
2198 .ok_or("from_components: offset out of range")?;
2199 let dt = off
2200 .with_ymd_and_hms(y, m, d, hh, mm, ss)
2201 .single()
2202 .ok_or("from_components: invalid or ambiguous date/time")?;
2203 let dt = dt + chrono::Duration::nanoseconds(ns as i64);
2204 Ok(instant_from_chrono(dt))
2205}