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" | "duration" | "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", "cons") => {
189 let head = args.first().cloned().unwrap_or(Value::Unit);
190 let mut out = vec![head];
191 out.extend(expect_list(args.get(1))?.iter().cloned());
192 Ok(Value::List(out))
193 }
194 ("list", "enumerate") => {
195 let xs = expect_list(args.first())?;
196 let pairs = xs.iter().cloned().enumerate()
197 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v]))
198 .collect::<Vec<_>>();
199 Ok(Value::List(pairs))
200 }
201
202 ("tuple", "fst") => tuple_index(first_arg(args)?, 0),
207 ("tuple", "snd") => tuple_index(first_arg(args)?, 1),
208 ("tuple", "third") => tuple_index(first_arg(args)?, 2),
209 ("tuple", "len") => match first_arg(args)? {
210 Value::Tuple(items) => Ok(Value::Int(items.len() as i64)),
211 other => Err(format!("tuple.len: expected Tuple, got {other:?}")),
212 },
213
214 ("option", "unwrap_or") => {
216 let opt = first_arg(args)?;
217 let default = args.get(1).cloned().unwrap_or(Value::Unit);
218 match opt {
219 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
220 Value::Variant { name, .. } if name == "None" => Ok(default),
221 other => Err(format!("option.unwrap_or expected Option, got {other:?}")),
222 }
223 }
224 ("option", "unwrap_or_else") => {
229 let opt = first_arg(args)?;
230 match opt {
231 Value::Variant { name, args } if name == "Some" && !args.is_empty() => Ok(args[0].clone()),
232 Value::Variant { name, .. } if name == "None" => {
233 Ok(args.get(1).cloned().unwrap_or(Value::Unit))
237 }
238 other => Err(format!("option.unwrap_or_else expected Option, got {other:?}")),
239 }
240 }
241 ("option", "is_some") => match first_arg(args)? {
242 Value::Variant { name, .. } => Ok(Value::Bool(name == "Some")),
243 other => Err(format!("option.is_some expected Option, got {other:?}")),
244 },
245 ("option", "is_none") => match first_arg(args)? {
246 Value::Variant { name, .. } => Ok(Value::Bool(name == "None")),
247 other => Err(format!("option.is_none expected Option, got {other:?}")),
248 },
249
250 ("result", "is_ok") => match first_arg(args)? {
252 Value::Variant { name, .. } => Ok(Value::Bool(name == "Ok")),
253 other => Err(format!("result.is_ok expected Result, got {other:?}")),
254 },
255 ("result", "is_err") => match first_arg(args)? {
256 Value::Variant { name, .. } => Ok(Value::Bool(name == "Err")),
257 other => Err(format!("result.is_err expected Result, got {other:?}")),
258 },
259 ("result", "unwrap_or") => {
260 let res = first_arg(args)?;
261 let default = args.get(1).cloned().unwrap_or(Value::Unit);
262 match res {
263 Value::Variant { name, args } if name == "Ok" && !args.is_empty() => Ok(args[0].clone()),
264 Value::Variant { name, .. } if name == "Err" => Ok(default),
265 other => Err(format!("result.unwrap_or expected Result, got {other:?}")),
266 }
267 }
268
269 ("json", "stringify") => {
271 let v = first_arg(args)?;
272 Ok(Value::Str(serde_json::to_string(&value_to_json(v)).unwrap_or_default()))
273 }
274 ("json", "parse") => {
275 let s = expect_str(args.first())?;
276 match serde_json::from_str::<serde_json::Value>(&s) {
277 Ok(v) => Ok(ok_v(json_to_value(&v))),
278 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
279 }
280 }
281 ("json", "parse_strict") => {
284 let s = expect_str(args.first())?;
285 let required = required_field_names(args.get(1))?;
286 let schema = extract_type_schema(args.get(2));
287 match serde_json::from_str::<serde_json::Value>(&s) {
288 Ok(v) => {
289 if let Err(e) = check_required_fields(&v, &required) {
290 return Ok(err_v(Value::Str(e)));
291 }
292 if let Err(e) = validate_field_types(&v, &schema) {
293 return Ok(err_v(Value::Str(e)));
294 }
295 Ok(ok_v(json_to_value(&v)))
296 }
297 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
298 }
299 }
300
301 ("toml", "parse") => {
306 let s = expect_str(args.first())?;
307 match toml::from_str::<serde_json::Value>(&s) {
308 Ok(mut v) => {
309 unwrap_toml_datetime_markers(&mut v);
310 Ok(ok_v(json_to_value(&v)))
311 }
312 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
313 }
314 }
315 ("json", "parse_strict_typed") => {
319 let s = expect_str(args.first())?;
320 let required = required_field_names(args.get(1))?;
321 let schema = extract_type_schema(args.get(2));
322 match serde_json::from_str::<serde_json::Value>(&s) {
323 Ok(v) => {
324 if let Err(e) = check_required_fields(&v, &required) {
325 return Ok(err_v(Value::Str(e)));
326 }
327 if let Err(e) = validate_field_types(&v, &schema) {
328 return Ok(err_v(Value::Str(e)));
329 }
330 Ok(ok_v(json_to_value(&v)))
331 }
332 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
333 }
334 }
335
336 ("toml", "parse_strict") => {
339 let s = expect_str(args.first())?;
340 let required = required_field_names(args.get(1))?;
341 let schema = extract_type_schema(args.get(2));
342 match toml::from_str::<serde_json::Value>(&s) {
343 Ok(mut v) => {
344 unwrap_toml_datetime_markers(&mut v);
345 if let Err(e) = check_required_fields(&v, &required) {
346 return Ok(err_v(Value::Str(e)));
347 }
348 if let Err(e) = validate_field_types(&v, &schema) {
349 return Ok(err_v(Value::Str(e)));
350 }
351 Ok(ok_v(json_to_value(&v)))
352 }
353 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
354 }
355 }
356 ("toml", "parse_strict_typed") => {
357 let s = expect_str(args.first())?;
358 let required = required_field_names(args.get(1))?;
359 let schema = extract_type_schema(args.get(2));
360 match toml::from_str::<serde_json::Value>(&s) {
361 Ok(mut v) => {
362 unwrap_toml_datetime_markers(&mut v);
363 if let Err(e) = check_required_fields(&v, &required) {
364 return Ok(err_v(Value::Str(e)));
365 }
366 if let Err(e) = validate_field_types(&v, &schema) {
367 return Ok(err_v(Value::Str(e)));
368 }
369 Ok(ok_v(json_to_value(&v)))
370 }
371 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
372 }
373 }
374 ("toml", "stringify") => {
375 let v = first_arg(args)?;
376 let json = value_to_json(v);
382 match toml::to_string(&json) {
383 Ok(s) => Ok(ok_v(Value::Str(s))),
384 Err(e) => Ok(err_v(Value::Str(format!("toml.stringify: {e}")))),
385 }
386 }
387
388 ("yaml", "parse") => {
394 let s = expect_str(args.first())?;
395 match serde_yaml::from_str::<serde_json::Value>(&s) {
396 Ok(v) => Ok(ok_v(json_to_value(&v))),
397 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
398 }
399 }
400 ("yaml", "parse_strict") => {
403 let s = expect_str(args.first())?;
404 let required = required_field_names(args.get(1))?;
405 let schema = extract_type_schema(args.get(2));
406 match serde_yaml::from_str::<serde_json::Value>(&s) {
407 Ok(v) => {
408 if let Err(e) = check_required_fields(&v, &required) {
409 return Ok(err_v(Value::Str(e)));
410 }
411 if let Err(e) = validate_field_types(&v, &schema) {
412 return Ok(err_v(Value::Str(e)));
413 }
414 Ok(ok_v(json_to_value(&v)))
415 }
416 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
417 }
418 }
419 ("yaml", "parse_strict_typed") => {
420 let s = expect_str(args.first())?;
421 let required = required_field_names(args.get(1))?;
422 let schema = extract_type_schema(args.get(2));
423 match serde_yaml::from_str::<serde_json::Value>(&s) {
424 Ok(v) => {
425 if let Err(e) = check_required_fields(&v, &required) {
426 return Ok(err_v(Value::Str(e)));
427 }
428 if let Err(e) = validate_field_types(&v, &schema) {
429 return Ok(err_v(Value::Str(e)));
430 }
431 Ok(ok_v(json_to_value(&v)))
432 }
433 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
434 }
435 }
436 ("yaml", "stringify") => {
437 let v = first_arg(args)?;
438 let json = value_to_json(v);
439 match serde_yaml::to_string(&json) {
440 Ok(s) => Ok(ok_v(Value::Str(s))),
441 Err(e) => Ok(err_v(Value::Str(format!("yaml.stringify: {e}")))),
442 }
443 }
444
445 ("dotenv", "parse") => {
453 use std::collections::BTreeMap;
454 use lex_bytecode::MapKey;
455 let s = expect_str(args.first())?;
456 match parse_dotenv(&s) {
457 Ok(map) => {
458 let mut bt: BTreeMap<MapKey, Value> = BTreeMap::new();
459 for (k, v) in map {
460 bt.insert(MapKey::Str(k), Value::Str(v));
461 }
462 Ok(ok_v(Value::Map(bt)))
463 }
464 Err(e) => Ok(err_v(Value::Str(e))),
465 }
466 }
467
468 ("csv", "parse") => {
473 let s = expect_str(args.first())?;
474 let mut rdr = csv::ReaderBuilder::new()
475 .has_headers(false)
476 .flexible(true)
477 .from_reader(s.as_bytes());
478 let mut rows: Vec<Value> = Vec::new();
479 for r in rdr.records() {
480 match r {
481 Ok(rec) => {
482 let row: Vec<Value> = rec.iter()
483 .map(|f| Value::Str(f.to_string()))
484 .collect();
485 rows.push(Value::List(row));
486 }
487 Err(e) => return Ok(err_v(Value::Str(format!("csv.parse: {e}")))),
488 }
489 }
490 Ok(ok_v(Value::List(rows)))
491 }
492 ("csv", "stringify") => {
493 let v = first_arg(args)?;
498 let rows = match v {
499 Value::List(rs) => rs,
500 _ => return Ok(err_v(Value::Str("csv.stringify expects List[List[Str]]".into()))),
501 };
502 let mut out = Vec::new();
503 {
504 let mut wtr = csv::WriterBuilder::new()
505 .has_headers(false)
506 .from_writer(&mut out);
507 for row in rows {
508 let cells = match row {
509 Value::List(cs) => cs,
510 _ => return Ok(err_v(Value::Str("csv.stringify row must be List[Str]".into()))),
511 };
512 let strs: Vec<String> = cells.iter().map(|c| match c {
513 Value::Str(s) => s.clone(),
514 other => serde_json::to_string(&other.to_json())
515 .unwrap_or_else(|_| String::new()),
516 }).collect();
517 if let Err(e) = wtr.write_record(&strs) {
518 return Ok(err_v(Value::Str(format!("csv.stringify: {e}"))));
519 }
520 }
521 if let Err(e) = wtr.flush() {
522 return Ok(err_v(Value::Str(format!("csv.stringify flush: {e}"))));
523 }
524 }
525 match String::from_utf8(out) {
526 Ok(s) => Ok(ok_v(Value::Str(s))),
527 Err(e) => Ok(err_v(Value::Str(format!("csv.stringify utf8: {e}")))),
528 }
529 }
530
531 ("test", "assert_eq") => {
538 let a = first_arg(args)?;
539 let b = args.get(1).ok_or("test.assert_eq: missing second arg")?;
540 if a == b {
541 Ok(ok_v(Value::Unit))
542 } else {
543 Ok(err_v(Value::Str(format!("assert_eq: lhs {} != rhs {}",
544 value_to_json(a), value_to_json(b)))))
545 }
546 }
547 ("test", "assert_ne") => {
548 let a = first_arg(args)?;
549 let b = args.get(1).ok_or("test.assert_ne: missing second arg")?;
550 if a != b {
551 Ok(ok_v(Value::Unit))
552 } else {
553 Ok(err_v(Value::Str(format!("assert_ne: both sides are {}",
554 value_to_json(a)))))
555 }
556 }
557 ("test", "assert_true") => {
558 match first_arg(args)? {
559 Value::Bool(true) => Ok(ok_v(Value::Unit)),
560 Value::Bool(false) => Ok(err_v(Value::Str("assert_true: was false".into()))),
561 other => Err(format!("test.assert_true expects Bool, got {other:?}")),
562 }
563 }
564 ("test", "assert_false") => {
565 match first_arg(args)? {
566 Value::Bool(false) => Ok(ok_v(Value::Unit)),
567 Value::Bool(true) => Ok(err_v(Value::Str("assert_false: was true".into()))),
568 other => Err(format!("test.assert_false expects Bool, got {other:?}")),
569 }
570 }
571
572 ("bytes", "len") => {
574 let b = expect_bytes(args.first())?;
575 Ok(Value::Int(b.len() as i64))
576 }
577 ("bytes", "eq") => {
578 let a = expect_bytes(args.first())?;
579 let b = expect_bytes(args.get(1))?;
580 Ok(Value::Bool(a == b))
581 }
582 ("bytes", "from_str") => {
583 let s = expect_str(args.first())?;
584 Ok(Value::Bytes(s.into_bytes()))
585 }
586 ("bytes", "to_str") => {
587 let b = expect_bytes(args.first())?;
588 match String::from_utf8(b.to_vec()) {
589 Ok(s) => Ok(ok_v(Value::Str(s))),
590 Err(e) => Ok(err_v(Value::Str(format!("{e}")))),
591 }
592 }
593 ("bytes", "slice") => {
594 let b = expect_bytes(args.first())?;
595 let lo = expect_int(args.get(1))? as usize;
596 let hi = expect_int(args.get(2))? as usize;
597 if lo > hi || hi > b.len() {
598 return Err(format!("bytes.slice: out of range [{lo}..{hi}] of {}", b.len()));
599 }
600 Ok(Value::Bytes(b[lo..hi].to_vec()))
601 }
602 ("bytes", "is_empty") => {
603 let b = expect_bytes(args.first())?;
604 Ok(Value::Bool(b.is_empty()))
605 }
606
607 ("math", "exp") => Ok(Value::Float(expect_float(args.first())?.exp())),
614 ("math", "log") => Ok(Value::Float(expect_float(args.first())?.ln())),
615 ("math", "log2") => Ok(Value::Float(expect_float(args.first())?.log2())),
616 ("math", "log10") => Ok(Value::Float(expect_float(args.first())?.log10())),
617 ("math", "sqrt") => Ok(Value::Float(expect_float(args.first())?.sqrt())),
618 ("math", "abs") => Ok(Value::Float(expect_float(args.first())?.abs())),
619 ("math", "sin") => Ok(Value::Float(expect_float(args.first())?.sin())),
620 ("math", "cos") => Ok(Value::Float(expect_float(args.first())?.cos())),
621 ("math", "tan") => Ok(Value::Float(expect_float(args.first())?.tan())),
622 ("math", "asin") => Ok(Value::Float(expect_float(args.first())?.asin())),
623 ("math", "acos") => Ok(Value::Float(expect_float(args.first())?.acos())),
624 ("math", "atan") => Ok(Value::Float(expect_float(args.first())?.atan())),
625 ("math", "floor") => Ok(Value::Float(expect_float(args.first())?.floor())),
626 ("math", "ceil") => Ok(Value::Float(expect_float(args.first())?.ceil())),
627 ("math", "round") => Ok(Value::Float(expect_float(args.first())?.round())),
628 ("math", "trunc") => Ok(Value::Float(expect_float(args.first())?.trunc())),
629 ("math", "pow") => {
630 let a = expect_float(args.first())?;
631 let b = expect_float(args.get(1))?;
632 Ok(Value::Float(a.powf(b)))
633 }
634 ("math", "atan2") => {
635 let y = expect_float(args.first())?;
636 let x = expect_float(args.get(1))?;
637 Ok(Value::Float(y.atan2(x)))
638 }
639 ("math", "min") => {
640 let a = expect_float(args.first())?;
641 let b = expect_float(args.get(1))?;
642 Ok(Value::Float(a.min(b)))
643 }
644 ("math", "max") => {
645 let a = expect_float(args.first())?;
646 let b = expect_float(args.get(1))?;
647 Ok(Value::Float(a.max(b)))
648 }
649 ("math", "zeros") => {
650 let r = expect_int(args.first())?;
651 let c = expect_int(args.get(1))?;
652 if r < 0 || c < 0 {
653 return Err(format!("math.zeros: negative dim {r}x{c}"));
654 }
655 let r = r as usize; let c = c as usize;
656 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![0.0; r * c] })
657 }
658 ("math", "ones") => {
659 let r = expect_int(args.first())?;
660 let c = expect_int(args.get(1))?;
661 if r < 0 || c < 0 {
662 return Err(format!("math.ones: negative dim {r}x{c}"));
663 }
664 let r = r as usize; let c = c as usize;
665 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data: vec![1.0; r * c] })
666 }
667 ("math", "from_lists") => {
668 let rows = expect_list(args.first())?;
669 let r = rows.len();
670 if r == 0 {
671 return Ok(Value::F64Array { rows: 0, cols: 0, data: Vec::new() });
672 }
673 let first_row = match &rows[0] {
674 Value::List(xs) => xs,
675 other => return Err(format!("math.from_lists: row 0 not List, got {other:?}")),
676 };
677 let c = first_row.len();
678 let mut data = Vec::with_capacity(r * c);
679 for (i, row) in rows.iter().enumerate() {
680 let row = match row {
681 Value::List(xs) => xs,
682 other => return Err(format!("math.from_lists: row {i} not List, got {other:?}")),
683 };
684 if row.len() != c {
685 return Err(format!("math.from_lists: row {i} has {} cols, expected {c}", row.len()));
686 }
687 for (j, v) in row.iter().enumerate() {
688 let f = match v {
689 Value::Float(f) => *f,
690 Value::Int(n) => *n as f64,
691 other => return Err(format!("math.from_lists: ({i},{j}) not numeric, got {other:?}")),
692 };
693 data.push(f);
694 }
695 }
696 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
697 }
698 ("math", "from_flat") => {
699 let r = expect_int(args.first())?;
700 let c = expect_int(args.get(1))?;
701 let xs = expect_list(args.get(2))?;
702 if r < 0 || c < 0 {
703 return Err(format!("math.from_flat: negative dim {r}x{c}"));
704 }
705 let r = r as usize; let c = c as usize;
706 if xs.len() != r * c {
707 return Err(format!("math.from_flat: list len {} != {}*{}", xs.len(), r, c));
708 }
709 let mut data = Vec::with_capacity(r * c);
710 for v in xs {
711 data.push(match v {
712 Value::Float(f) => *f,
713 Value::Int(n) => *n as f64,
714 other => return Err(format!("math.from_flat: non-numeric element {other:?}")),
715 });
716 }
717 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
718 }
719 ("math", "rows") => {
720 let (r, _, _) = unpack_matrix(first_arg(args)?)?;
721 Ok(Value::Int(r as i64))
722 }
723 ("math", "cols") => {
724 let (_, c, _) = unpack_matrix(first_arg(args)?)?;
725 Ok(Value::Int(c as i64))
726 }
727 ("math", "get") => {
728 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
729 let i = expect_int(args.get(1))? as usize;
730 let j = expect_int(args.get(2))? as usize;
731 if i >= r || j >= c {
732 return Err(format!("math.get: ({i},{j}) out of {r}x{c}"));
733 }
734 Ok(Value::Float(data[i * c + j]))
735 }
736 ("math", "to_flat") => {
737 let (_, _, data) = unpack_matrix(first_arg(args)?)?;
738 Ok(Value::List(data.into_iter().map(Value::Float).collect()))
739 }
740 ("math", "transpose") => {
741 let (r, c, data) = unpack_matrix(first_arg(args)?)?;
742 let mut out = vec![0.0; r * c];
743 for i in 0..r {
744 for j in 0..c {
745 out[j * r + i] = data[i * c + j];
746 }
747 }
748 Ok(Value::F64Array { rows: c as u32, cols: r as u32, data: out })
749 }
750 ("math", "matmul") => {
751 let (m, k1, a) = unpack_matrix(first_arg(args)?)?;
752 let (k2, n, b) = unpack_matrix(args.get(1).ok_or("math.matmul: missing arg 1")?)?;
753 if k1 != k2 {
754 return Err(format!("math.matmul: dim mismatch {m}x{k1} · {k2}x{n}"));
755 }
756 let mut c = vec![0.0; m * n];
760 for i in 0..m {
761 for kk in 0..k1 {
762 let aik = a[i * k1 + kk];
763 for j in 0..n {
764 c[i * n + j] += aik * b[kk * n + j];
765 }
766 }
767 }
768 Ok(Value::F64Array { rows: m as u32, cols: n as u32, data: c })
769 }
770 ("math", "scale") => {
771 let s = expect_float(args.first())?;
772 let (r, c, mut data) = unpack_matrix(args.get(1).ok_or("math.scale: missing arg 1")?)?;
773 for x in &mut data { *x *= s; }
774 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
775 }
776 ("math", "add") | ("math", "sub") => {
777 let (ar, ac, a) = unpack_matrix(first_arg(args)?)?;
778 let (br, bc, b) = unpack_matrix(args.get(1).ok_or("math.add/sub: missing arg 1")?)?;
779 if ar != br || ac != bc {
780 return Err(format!("math.{op}: shape mismatch {ar}x{ac} vs {br}x{bc}"));
781 }
782 let neg = op == "sub";
783 let mut out = a;
784 for (i, x) in out.iter_mut().enumerate() {
785 if neg { *x -= b[i] } else { *x += b[i] }
786 }
787 Ok(Value::F64Array { rows: ar as u32, cols: ac as u32, data: out })
788 }
789 ("math", "sigmoid") => {
790 let (r, c, mut data) = unpack_matrix(first_arg(args)?)?;
791 for x in &mut data { *x = 1.0 / (1.0 + (-*x).exp()); }
792 Ok(Value::F64Array { rows: r as u32, cols: c as u32, data })
793 }
794
795 ("map", "new") => Ok(Value::Map(BTreeMap::new())),
797 ("map", "size") => Ok(Value::Int(expect_map(args.first())?.len() as i64)),
798 ("map", "has") => {
799 let m = expect_map(args.first())?;
800 let k = MapKey::from_value(args.get(1).ok_or("map.has: missing key")?)?;
801 Ok(Value::Bool(m.contains_key(&k)))
802 }
803 ("map", "get") => {
804 let m = expect_map(args.first())?;
805 let k = MapKey::from_value(args.get(1).ok_or("map.get: missing key")?)?;
806 Ok(match m.get(&k) {
807 Some(v) => some(v.clone()),
808 None => none(),
809 })
810 }
811 ("map", "set") => {
812 let mut m = expect_map(args.first())?.clone();
813 let k = MapKey::from_value(args.get(1).ok_or("map.set: missing key")?)?;
814 let v = args.get(2).ok_or("map.set: missing value")?.clone();
815 m.insert(k, v);
816 Ok(Value::Map(m))
817 }
818 ("map", "delete") => {
819 let mut m = expect_map(args.first())?.clone();
820 let k = MapKey::from_value(args.get(1).ok_or("map.delete: missing key")?)?;
821 m.remove(&k);
822 Ok(Value::Map(m))
823 }
824 ("map", "keys") => {
825 let m = expect_map(args.first())?;
826 Ok(Value::List(m.keys().cloned().map(MapKey::into_value).collect()))
827 }
828 ("map", "values") => {
829 let m = expect_map(args.first())?;
830 Ok(Value::List(m.values().cloned().collect()))
831 }
832 ("map", "entries") => {
833 let m = expect_map(args.first())?;
834 Ok(Value::List(m.iter()
835 .map(|(k, v)| Value::Tuple(vec![k.as_value(), v.clone()]))
836 .collect()))
837 }
838 ("map", "from_list") => {
839 let pairs = expect_list(args.first())?;
840 let mut m = BTreeMap::new();
841 for p in pairs {
842 let items = match p {
843 Value::Tuple(items) if items.len() == 2 => items,
844 other => return Err(format!(
845 "map.from_list element must be a 2-tuple, got {other:?}")),
846 };
847 let k = MapKey::from_value(&items[0])?;
848 m.insert(k, items[1].clone());
849 }
850 Ok(Value::Map(m))
851 }
852
853 ("set", "new") => Ok(Value::Set(BTreeSet::new())),
855 ("set", "size") => Ok(Value::Int(expect_set(args.first())?.len() as i64)),
856 ("set", "has") => {
857 let s = expect_set(args.first())?;
858 let k = MapKey::from_value(args.get(1).ok_or("set.has: missing element")?)?;
859 Ok(Value::Bool(s.contains(&k)))
860 }
861 ("set", "add") => {
862 let mut s = expect_set(args.first())?.clone();
863 let k = MapKey::from_value(args.get(1).ok_or("set.add: missing element")?)?;
864 s.insert(k);
865 Ok(Value::Set(s))
866 }
867 ("set", "delete") => {
868 let mut s = expect_set(args.first())?.clone();
869 let k = MapKey::from_value(args.get(1).ok_or("set.delete: missing element")?)?;
870 s.remove(&k);
871 Ok(Value::Set(s))
872 }
873 ("set", "to_list") => {
874 let s = expect_set(args.first())?;
875 Ok(Value::List(s.iter().cloned().map(MapKey::into_value).collect()))
876 }
877 ("set", "from_list") => {
878 let xs = expect_list(args.first())?;
879 let mut s = BTreeSet::new();
880 for x in xs {
881 s.insert(MapKey::from_value(x)?);
882 }
883 Ok(Value::Set(s))
884 }
885 ("set", "union") => {
886 let a = expect_set(args.first())?;
887 let b = expect_set(args.get(1))?;
888 Ok(Value::Set(a.union(b).cloned().collect()))
889 }
890 ("set", "intersect") => {
891 let a = expect_set(args.first())?;
892 let b = expect_set(args.get(1))?;
893 Ok(Value::Set(a.intersection(b).cloned().collect()))
894 }
895 ("set", "diff") => {
896 let a = expect_set(args.first())?;
897 let b = expect_set(args.get(1))?;
898 Ok(Value::Set(a.difference(b).cloned().collect()))
899 }
900 ("set", "is_empty") => Ok(Value::Bool(expect_set(args.first())?.is_empty())),
901 ("set", "is_subset") => {
902 let a = expect_set(args.first())?;
903 let b = expect_set(args.get(1))?;
904 Ok(Value::Bool(a.is_subset(b)))
905 }
906
907 ("map", "merge") => {
909 let a = expect_map(args.first())?.clone();
912 let b = expect_map(args.get(1))?;
913 let mut out = a;
914 for (k, v) in b {
915 out.insert(k.clone(), v.clone());
916 }
917 Ok(Value::Map(out))
918 }
919 ("map", "is_empty") => Ok(Value::Bool(expect_map(args.first())?.is_empty())),
920
921 ("deque", "new") => Ok(Value::Deque(std::collections::VecDeque::new())),
923 ("deque", "size") => Ok(Value::Int(expect_deque(args.first())?.len() as i64)),
924 ("deque", "is_empty") => Ok(Value::Bool(expect_deque(args.first())?.is_empty())),
925 ("deque", "push_back") => {
926 let mut d = expect_deque(args.first())?.clone();
927 let x = args.get(1).ok_or("deque.push_back: missing value")?.clone();
928 d.push_back(x);
929 Ok(Value::Deque(d))
930 }
931 ("deque", "push_front") => {
932 let mut d = expect_deque(args.first())?.clone();
933 let x = args.get(1).ok_or("deque.push_front: missing value")?.clone();
934 d.push_front(x);
935 Ok(Value::Deque(d))
936 }
937 ("deque", "pop_back") => {
938 let mut d = expect_deque(args.first())?.clone();
939 match d.pop_back() {
940 Some(x) => Ok(Value::Variant {
941 name: "Some".into(),
942 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
943 }),
944 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
945 }
946 }
947 ("deque", "pop_front") => {
948 let mut d = expect_deque(args.first())?.clone();
949 match d.pop_front() {
950 Some(x) => Ok(Value::Variant {
951 name: "Some".into(),
952 args: vec![Value::Tuple(vec![x, Value::Deque(d)])],
953 }),
954 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
955 }
956 }
957 ("deque", "peek_back") => {
958 let d = expect_deque(args.first())?;
959 match d.back() {
960 Some(x) => Ok(Value::Variant {
961 name: "Some".into(),
962 args: vec![x.clone()],
963 }),
964 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
965 }
966 }
967 ("deque", "peek_front") => {
968 let d = expect_deque(args.first())?;
969 match d.front() {
970 Some(x) => Ok(Value::Variant {
971 name: "Some".into(),
972 args: vec![x.clone()],
973 }),
974 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
975 }
976 }
977 ("deque", "from_list") => {
978 let xs = expect_list(args.first())?;
979 Ok(Value::Deque(xs.iter().cloned().collect()))
980 }
981 ("deque", "to_list") => {
982 let d = expect_deque(args.first())?;
983 Ok(Value::List(d.iter().cloned().collect()))
984 }
985
986 ("crypto", "sha256") => {
989 use sha2::{Digest, Sha256};
990 let data = expect_bytes(args.first())?;
991 let mut h = Sha256::new();
992 h.update(data);
993 Ok(Value::Bytes(h.finalize().to_vec()))
994 }
995 ("crypto", "sha512") => {
996 use sha2::{Digest, Sha512};
997 let data = expect_bytes(args.first())?;
998 let mut h = Sha512::new();
999 h.update(data);
1000 Ok(Value::Bytes(h.finalize().to_vec()))
1001 }
1002 ("crypto", "md5") => {
1003 use md5::{Digest, Md5};
1004 let data = expect_bytes(args.first())?;
1005 let mut h = Md5::new();
1006 h.update(data);
1007 Ok(Value::Bytes(h.finalize().to_vec()))
1008 }
1009 ("crypto", "hmac_sha256") => {
1010 use hmac::{Hmac, KeyInit, Mac};
1011 type HmacSha256 = Hmac<sha2::Sha256>;
1012 let key = expect_bytes(args.first())?;
1013 let data = expect_bytes(args.get(1))?;
1014 let mut mac = HmacSha256::new_from_slice(key)
1015 .map_err(|e| format!("hmac_sha256 key: {e}"))?;
1016 mac.update(data);
1017 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1018 }
1019 ("crypto", "hmac_sha512") => {
1020 use hmac::{Hmac, KeyInit, Mac};
1021 type HmacSha512 = Hmac<sha2::Sha512>;
1022 let key = expect_bytes(args.first())?;
1023 let data = expect_bytes(args.get(1))?;
1024 let mut mac = HmacSha512::new_from_slice(key)
1025 .map_err(|e| format!("hmac_sha512 key: {e}"))?;
1026 mac.update(data);
1027 Ok(Value::Bytes(mac.finalize().into_bytes().to_vec()))
1028 }
1029 ("crypto", "base64_encode") => {
1030 use base64::{Engine, engine::general_purpose::STANDARD};
1031 let data = expect_bytes(args.first())?;
1032 Ok(Value::Str(STANDARD.encode(data)))
1033 }
1034 ("crypto", "base64_decode") => {
1035 use base64::{Engine, engine::general_purpose::STANDARD};
1036 let s = expect_str(args.first())?;
1037 match STANDARD.decode(s) {
1038 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1039 Err(e) => Ok(err_v(Value::Str(format!("base64: {e}")))),
1040 }
1041 }
1042 ("crypto", "hex_encode") => {
1043 let data = expect_bytes(args.first())?;
1044 Ok(Value::Str(hex::encode(data)))
1045 }
1046 ("crypto", "hex_decode") => {
1047 let s = expect_str(args.first())?;
1048 match hex::decode(s) {
1049 Ok(b) => Ok(ok_v(Value::Bytes(b))),
1050 Err(e) => Ok(err_v(Value::Str(format!("hex: {e}")))),
1051 }
1052 }
1053 ("crypto", "constant_time_eq") => {
1054 use subtle::ConstantTimeEq;
1055 let a = expect_bytes(args.first())?;
1056 let b = expect_bytes(args.get(1))?;
1057 let eq = if a.len() == b.len() {
1062 a.ct_eq(b).into()
1063 } else {
1064 false
1065 };
1066 Ok(Value::Bool(eq))
1067 }
1068
1069 ("random", "seed") => {
1076 let s = args.first().ok_or("random.seed: missing arg")?.as_int();
1077 let mixed = splitmix64(s as u64).0;
1082 Ok(rng_value(mixed))
1083 }
1084 ("random", "int") => {
1085 let state = rng_decode(args.first())?;
1086 let lo = args.get(1).ok_or("random.int: missing lo")?.as_int();
1087 let hi = args.get(2).ok_or("random.int: missing hi")?.as_int();
1088 if hi < lo {
1089 return Err(format!(
1090 "random.int: hi ({hi}) must be >= lo ({lo})"));
1091 }
1092 let span = (hi as i128) - (lo as i128) + 1;
1093 let (raw, next_state) = splitmix64(state);
1094 let drawn = lo as i128 + (raw as u128 % span as u128) as i128;
1099 Ok(Value::Tuple(vec![
1100 Value::Int(drawn as i64),
1101 rng_value(next_state),
1102 ]))
1103 }
1104 ("random", "float") => {
1105 let state = rng_decode(args.first())?;
1106 let (raw, next_state) = splitmix64(state);
1107 let f = ((raw >> 11) as f64) / ((1u64 << 53) as f64);
1110 Ok(Value::Tuple(vec![Value::Float(f), rng_value(next_state)]))
1111 }
1112 ("random", "choose") => {
1113 let state = rng_decode(args.first())?;
1114 let xs = match args.get(1) {
1115 Some(Value::List(xs)) => xs,
1116 _ => return Err("random.choose: expected List".into()),
1117 };
1118 if xs.is_empty() {
1119 return Ok(Value::Variant {
1120 name: "None".into(), args: vec![],
1121 });
1122 }
1123 let (raw, next_state) = splitmix64(state);
1124 let idx = (raw as usize) % xs.len();
1125 let pick = xs[idx].clone();
1126 Ok(Value::Variant {
1127 name: "Some".into(),
1128 args: vec![Value::Tuple(vec![pick, rng_value(next_state)])],
1129 })
1130 }
1131
1132 ("parser", "char") => {
1137 let s = expect_str(args.first())?;
1138 if s.chars().count() != 1 {
1139 return Err(format!(
1140 "parser.char: expected 1-character string, got {s:?}"));
1141 }
1142 Ok(parser_node("Char", &[("ch", Value::Str(s))]))
1143 }
1144 ("parser", "string") => {
1145 let s = expect_str(args.first())?;
1146 Ok(parser_node("String", &[("s", Value::Str(s))]))
1147 }
1148 ("parser", "digit") => Ok(parser_node("Digit", &[])),
1149 ("parser", "alpha") => Ok(parser_node("Alpha", &[])),
1150 ("parser", "whitespace") => Ok(parser_node("Whitespace", &[])),
1151 ("parser", "eof") => Ok(parser_node("Eof", &[])),
1152 ("parser", "seq") => {
1153 let a = args.first().cloned()
1154 .ok_or_else(|| "parser.seq: missing first parser".to_string())?;
1155 let b = args.get(1).cloned()
1156 .ok_or_else(|| "parser.seq: missing second parser".to_string())?;
1157 Ok(parser_node("Seq", &[("a", a), ("b", b)]))
1158 }
1159 ("parser", "alt") => {
1160 let a = args.first().cloned()
1161 .ok_or_else(|| "parser.alt: missing first parser".to_string())?;
1162 let b = args.get(1).cloned()
1163 .ok_or_else(|| "parser.alt: missing second parser".to_string())?;
1164 Ok(parser_node("Alt", &[("a", a), ("b", b)]))
1165 }
1166 ("parser", "many") => {
1167 let p = args.first().cloned()
1168 .ok_or_else(|| "parser.many: missing inner parser".to_string())?;
1169 Ok(parser_node("Many", &[("p", p)]))
1170 }
1171 ("parser", "optional") => {
1172 let p = args.first().cloned()
1173 .ok_or_else(|| "parser.optional: missing inner parser".to_string())?;
1174 Ok(parser_node("Optional", &[("p", p)]))
1175 }
1176 ("parser", "map") => {
1180 let p = args.first().cloned()
1181 .ok_or_else(|| "parser.map: missing parser".to_string())?;
1182 let f = args.get(1).cloned()
1183 .ok_or_else(|| "parser.map: missing closure".to_string())?;
1184 Ok(parser_node("Map", &[("p", p), ("f", f)]))
1185 }
1186 ("parser", "and_then") => {
1187 let p = args.first().cloned()
1188 .ok_or_else(|| "parser.and_then: missing parser".to_string())?;
1189 let f = args.get(1).cloned()
1190 .ok_or_else(|| "parser.and_then: missing closure".to_string())?;
1191 Ok(parser_node("AndThen", &[("p", p), ("f", f)]))
1192 }
1193 ("regex", "compile") => {
1203 let pat = expect_str(args.first())?;
1204 match get_or_compile_regex(&pat) {
1205 Ok(_) => Ok(ok_v(Value::Str(pat))),
1206 Err(e) => Ok(err_v(Value::Str(e))),
1207 }
1208 }
1209 ("regex", "is_match") => {
1210 let pat = expect_str(args.first())?;
1211 let s = expect_str(args.get(1))?;
1212 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.is_match: {e}"))?;
1213 Ok(Value::Bool(re.is_match(&s)))
1214 }
1215 ("regex", "is_match_str") => {
1220 let pat = expect_str(args.first())?;
1221 let s = expect_str(args.get(1))?;
1222 match get_or_compile_regex(&pat) {
1223 Ok(re) => Ok(Value::Bool(re.is_match(&s))),
1224 Err(_) => Ok(Value::Bool(false)),
1225 }
1226 }
1227 ("regex", "find") => {
1228 let pat = expect_str(args.first())?;
1229 let s = expect_str(args.get(1))?;
1230 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find: {e}"))?;
1231 match re.captures(&s) {
1232 Some(caps) => Ok(Value::Variant {
1233 name: "Some".into(),
1234 args: vec![match_value(&caps)],
1235 }),
1236 None => Ok(Value::Variant { name: "None".into(), args: vec![] }),
1237 }
1238 }
1239 ("regex", "find_all") => {
1240 let pat = expect_str(args.first())?;
1241 let s = expect_str(args.get(1))?;
1242 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.find_all: {e}"))?;
1243 let items: Vec<Value> = re.captures_iter(&s).map(|caps| match_value(&caps)).collect();
1244 Ok(Value::List(items))
1245 }
1246 ("regex", "replace") => {
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: {e}"))?;
1251 Ok(Value::Str(re.replace(&s, rep.as_str()).into_owned()))
1252 }
1253 ("regex", "replace_all") => {
1254 let pat = expect_str(args.first())?;
1255 let s = expect_str(args.get(1))?;
1256 let rep = expect_str(args.get(2))?;
1257 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.replace_all: {e}"))?;
1258 Ok(Value::Str(re.replace_all(&s, rep.as_str()).into_owned()))
1259 }
1260 ("datetime", "parse_iso") => {
1263 let s = expect_str(args.first())?;
1264 match chrono::DateTime::parse_from_rfc3339(&s) {
1265 Ok(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1266 Err(e) => Ok(err_v(Value::Str(format!("parse_iso: {e}")))),
1267 }
1268 }
1269 ("datetime", "format_iso") => {
1270 let n = expect_int(args.first())?;
1271 Ok(Value::Str(format_iso(n)))
1272 }
1273 ("datetime", "parse") => {
1274 let s = expect_str(args.first())?;
1275 let fmt = expect_str(args.get(1))?;
1276 match chrono::NaiveDateTime::parse_from_str(&s, &fmt) {
1277 Ok(naive) => {
1278 use chrono::TimeZone;
1279 match chrono::Utc.from_local_datetime(&naive).single() {
1280 Some(dt) => Ok(ok_v(Value::Int(instant_from_chrono(dt)))),
1281 None => Ok(err_v(Value::Str("parse: ambiguous local time".into()))),
1282 }
1283 }
1284 Err(e) => Ok(err_v(Value::Str(format!("parse: {e}")))),
1285 }
1286 }
1287 ("datetime", "format") => {
1288 let n = expect_int(args.first())?;
1289 let fmt = expect_str(args.get(1))?;
1290 let dt = chrono_from_instant(n);
1291 Ok(Value::Str(dt.format(&fmt).to_string()))
1292 }
1293 ("datetime", "to_components") => {
1294 let n = expect_int(args.first())?;
1295 let tz = match parse_tz_arg(args.get(1)) {
1296 Ok(t) => t,
1297 Err(e) => return Ok(err_v(Value::Str(e))),
1298 };
1299 match resolve_tz_to_components(n, &tz) {
1300 Ok(rec) => Ok(ok_v(rec)),
1301 Err(e) => Ok(err_v(Value::Str(e))),
1302 }
1303 }
1304 ("datetime", "from_components") => {
1305 let rec = match args.first() {
1306 Some(Value::Record(r)) => r.clone(),
1307 _ => return Err("from_components: expected DateTime record".into()),
1308 };
1309 match instant_from_components(&rec) {
1310 Ok(n) => Ok(ok_v(Value::Int(n))),
1311 Err(e) => Ok(err_v(Value::Str(e))),
1312 }
1313 }
1314 ("datetime", "add") => {
1315 let a = expect_int(args.first())?;
1316 let d = expect_int(args.get(1))?;
1317 Ok(Value::Int(a.saturating_add(d)))
1318 }
1319 ("datetime", "diff") => {
1320 let a = expect_int(args.first())?;
1321 let b = expect_int(args.get(1))?;
1322 Ok(Value::Int(a.saturating_sub(b)))
1323 }
1324 ("datetime", "duration_seconds") => {
1325 let s = expect_float(args.first())?;
1326 let nanos = (s * 1_000_000_000.0) as i64;
1327 Ok(Value::Int(nanos))
1328 }
1329 ("datetime", "duration_minutes") => {
1330 let m = expect_int(args.first())?;
1331 Ok(Value::Int(m.saturating_mul(60_000_000_000)))
1332 }
1333 ("datetime", "duration_days") => {
1334 let d = expect_int(args.first())?;
1335 Ok(Value::Int(d.saturating_mul(86_400_000_000_000)))
1336 }
1337 ("datetime", "before") => {
1339 let a = expect_int(args.first())?;
1340 let b = expect_int(args.get(1))?;
1341 Ok(Value::Bool(a < b))
1342 }
1343 ("datetime", "after") => {
1344 let a = expect_int(args.first())?;
1345 let b = expect_int(args.get(1))?;
1346 Ok(Value::Bool(a > b))
1347 }
1348 ("datetime", "compare") => {
1349 let a = expect_int(args.first())?;
1350 let b = expect_int(args.get(1))?;
1351 Ok(Value::Int(a.cmp(&b) as i64))
1352 }
1353 ("duration", "seconds") => {
1355 let nanos = expect_int(args.first())?;
1356 Ok(Value::Int(nanos / 1_000_000_000))
1357 }
1358
1359 ("regex", "split") => {
1360 let pat = expect_str(args.first())?;
1361 let s = expect_str(args.get(1))?;
1362 let re = get_or_compile_regex(&pat).map_err(|e| format!("regex.split: {e}"))?;
1363 let parts: Vec<Value> = re.split(&s).map(|p| Value::Str(p.to_string())).collect();
1364 Ok(Value::List(parts))
1365 }
1366
1367 ("http", "with_header") => {
1370 let req = expect_record_pure(args.first())?.clone();
1371 let k = expect_str(args.get(1))?;
1372 let v = expect_str(args.get(2))?;
1373 Ok(Value::Record(http_set_header(req, &k, &v)))
1374 }
1375 ("http", "with_auth") => {
1376 let req = expect_record_pure(args.first())?.clone();
1377 let scheme = expect_str(args.get(1))?;
1378 let token = expect_str(args.get(2))?;
1379 let value = format!("{scheme} {token}");
1380 Ok(Value::Record(http_set_header(req, "Authorization", &value)))
1381 }
1382 ("http", "with_query") => {
1383 let req = expect_record_pure(args.first())?.clone();
1384 let params = match args.get(1) {
1385 Some(Value::Map(m)) => m.clone(),
1386 Some(other) => return Err(format!(
1387 "http.with_query: params must be Map[Str, Str], got {other:?}")),
1388 None => return Err("http.with_query: missing params argument".into()),
1389 };
1390 Ok(Value::Record(http_append_query(req, ¶ms)))
1391 }
1392 ("http", "with_timeout_ms") => {
1393 let req = expect_record_pure(args.first())?.clone();
1394 let ms = expect_int(args.get(1))?;
1395 let mut out = req;
1396 out.insert("timeout_ms".into(), Value::Variant {
1397 name: "Some".into(),
1398 args: vec![Value::Int(ms)],
1399 });
1400 Ok(Value::Record(out))
1401 }
1402 ("http", "json_body") => {
1403 let resp = expect_record_pure(args.first())?;
1404 let body = match resp.get("body") {
1405 Some(Value::Bytes(b)) => b.clone(),
1406 _ => return Err("http.json_body: HttpResponse.body must be Bytes".into()),
1407 };
1408 let s = match std::str::from_utf8(&body) {
1409 Ok(s) => s,
1410 Err(e) => return Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1411 };
1412 match serde_json::from_str::<serde_json::Value>(s) {
1413 Ok(j) => Ok(ok_v(Value::from_json(&j))),
1414 Err(e) => Ok(http_decode_err_pure(format!("json parse: {e}"))),
1415 }
1416 }
1417 ("http", "text_body") => {
1418 let resp = expect_record_pure(args.first())?;
1419 let body = match resp.get("body") {
1420 Some(Value::Bytes(b)) => b.clone(),
1421 _ => return Err("http.text_body: HttpResponse.body must be Bytes".into()),
1422 };
1423 match String::from_utf8(body) {
1424 Ok(s) => Ok(ok_v(Value::Str(s))),
1425 Err(e) => Ok(http_decode_err_pure(format!("body not UTF-8: {e}"))),
1426 }
1427 }
1428
1429 ("cli", "flag") => {
1433 let name = expect_str(args.first())?;
1434 let short = opt_str(args.get(1));
1435 let help = expect_str(args.get(2))?;
1436 Ok(value_from_json(crate::cli::flag_spec(&name, short.as_deref(), &help)))
1437 }
1438 ("cli", "option") => {
1439 let name = expect_str(args.first())?;
1440 let short = opt_str(args.get(1));
1441 let help = expect_str(args.get(2))?;
1442 let default = opt_str(args.get(3));
1443 Ok(value_from_json(crate::cli::option_spec(&name, short.as_deref(), &help, default.as_deref())))
1444 }
1445 ("cli", "positional") => {
1446 let name = expect_str(args.first())?;
1447 let help = expect_str(args.get(1))?;
1448 let required = expect_bool(args.get(2))?;
1449 Ok(value_from_json(crate::cli::positional_spec(&name, &help, required)))
1450 }
1451 ("cli", "spec") => {
1452 let name = expect_str(args.first())?;
1453 let help = expect_str(args.get(1))?;
1454 let arg_specs: Vec<serde_json::Value> = expect_list(args.get(2))?
1455 .iter().map(value_to_json).collect();
1456 let subs: Vec<serde_json::Value> = expect_list(args.get(3))?
1457 .iter().map(value_to_json).collect();
1458 Ok(value_from_json(crate::cli::build_spec(&name, &help, arg_specs, subs)))
1459 }
1460 ("cli", "parse") => {
1461 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1462 let argv: Vec<String> = expect_list(args.get(1))?
1463 .iter().map(|v| match v {
1464 Value::Str(s) => Ok(s.clone()),
1465 other => Err(format!("cli.parse: argv must be List[Str], got {other:?}")),
1466 }).collect::<Result<_, _>>()?;
1467 match crate::cli::parse(&spec, &argv) {
1468 Ok(parsed) => Ok(ok_v(value_from_json(parsed))),
1469 Err(msg) => Ok(err_v(Value::Str(msg))),
1470 }
1471 }
1472 ("cli", "envelope") => {
1473 let ok = expect_bool(args.first())?;
1474 let cmd = expect_str(args.get(1))?;
1475 let data = value_to_json(args.get(2).unwrap_or(&Value::Unit));
1476 Ok(value_from_json(crate::cli::envelope(ok, &cmd, data)))
1477 }
1478 ("cli", "describe") => {
1479 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1480 Ok(value_from_json(crate::cli::describe(&spec)))
1481 }
1482 ("cli", "help") => {
1483 let spec = value_to_json(args.first().unwrap_or(&Value::Unit));
1484 Ok(Value::Str(crate::cli::help_text(&spec)))
1485 }
1486
1487 _ => Err(format!("unknown pure builtin: {kind}.{op}")),
1488 }
1489}
1490
1491fn opt_str(arg: Option<&Value>) -> Option<String> {
1495 match arg {
1496 Some(Value::Variant { name, args }) if name == "Some" => {
1497 args.first().and_then(|v| match v {
1498 Value::Str(s) => Some(s.clone()),
1499 _ => None,
1500 })
1501 }
1502 _ => None,
1503 }
1504}
1505
1506fn value_from_json(v: serde_json::Value) -> Value { Value::from_json(&v) }
1507
1508fn regex_cache() -> &'static Mutex<HashMap<String, regex::Regex>> {
1513 static CACHE: OnceLock<Mutex<HashMap<String, regex::Regex>>> = OnceLock::new();
1514 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
1515}
1516
1517fn get_or_compile_regex(pattern: &str) -> Result<regex::Regex, String> {
1518 let cache = regex_cache();
1519 {
1520 let guard = cache.lock().unwrap();
1521 if let Some(re) = guard.get(pattern) {
1522 return Ok(re.clone());
1523 }
1524 }
1525 let re = regex::Regex::new(pattern).map_err(|e| format!("invalid regex: {e}"))?;
1526 let mut guard = cache.lock().unwrap();
1527 guard.insert(pattern.to_string(), re.clone());
1528 Ok(re)
1529}
1530
1531fn match_value(caps: ®ex::Captures) -> Value {
1535 let m0 = caps.get(0).expect("regex match always has group 0");
1536 let mut rec = indexmap::IndexMap::new();
1537 rec.insert("text".into(), Value::Str(m0.as_str().to_string()));
1538 rec.insert("start".into(), Value::Int(m0.start() as i64));
1539 rec.insert("end".into(), Value::Int(m0.end() as i64));
1540 let groups: Vec<Value> = (1..caps.len())
1541 .map(|i| {
1542 Value::Str(
1543 caps.get(i)
1544 .map(|m| m.as_str().to_string())
1545 .unwrap_or_default(),
1546 )
1547 })
1548 .collect();
1549 rec.insert("groups".into(), Value::List(groups));
1550 Value::Record(rec)
1551}
1552
1553fn expect_map(v: Option<&Value>) -> Result<&BTreeMap<MapKey, Value>, String> {
1554 match v {
1555 Some(Value::Map(m)) => Ok(m),
1556 other => Err(format!("expected Map, got {other:?}")),
1557 }
1558}
1559
1560fn expect_set(v: Option<&Value>) -> Result<&BTreeSet<MapKey>, String> {
1561 match v {
1562 Some(Value::Set(s)) => Ok(s),
1563 other => Err(format!("expected Set, got {other:?}")),
1564 }
1565}
1566
1567fn unpack_matrix(v: &Value) -> Result<(usize, usize, Vec<f64>), String> {
1571 if let Value::F64Array { rows, cols, data } = v {
1572 return Ok((*rows as usize, *cols as usize, data.clone()));
1573 }
1574 let rec = match v {
1575 Value::Record(r) => r,
1576 other => return Err(format!("expected matrix, got {other:?}")),
1577 };
1578 let rows = match rec.get("rows") {
1579 Some(Value::Int(n)) => *n as usize,
1580 _ => return Err("matrix: missing/invalid `rows`".into()),
1581 };
1582 let cols = match rec.get("cols") {
1583 Some(Value::Int(n)) => *n as usize,
1584 _ => return Err("matrix: missing/invalid `cols`".into()),
1585 };
1586 let data = match rec.get("data") {
1587 Some(Value::List(items)) => {
1588 let mut out = Vec::with_capacity(items.len());
1589 for it in items {
1590 out.push(match it {
1591 Value::Float(f) => *f,
1592 Value::Int(n) => *n as f64,
1593 other => return Err(format!("matrix data: not numeric, got {other:?}")),
1594 });
1595 }
1596 out
1597 }
1598 _ => return Err("matrix: missing/invalid `data`".into()),
1599 };
1600 if data.len() != rows * cols {
1601 return Err(format!("matrix: data len {} != {rows}*{cols}", data.len()));
1602 }
1603 Ok((rows, cols, data))
1604}
1605
1606fn expect_bytes(v: Option<&Value>) -> Result<&Vec<u8>, String> {
1607 match v {
1608 Some(Value::Bytes(b)) => Ok(b),
1609 Some(other) => Err(format!("expected Bytes, got {other:?}")),
1610 None => Err("missing argument".into()),
1611 }
1612}
1613
1614fn first_arg(args: &[Value]) -> Result<&Value, String> {
1615 args.first().ok_or_else(|| "missing argument".into())
1616}
1617
1618fn tuple_index(v: &Value, i: usize) -> Result<Value, String> {
1619 match v {
1620 Value::Tuple(items) => items.get(i).cloned()
1621 .ok_or_else(|| format!("tuple index {i} out of range (len={})", items.len())),
1622 other => Err(format!("expected Tuple, got {other:?}")),
1623 }
1624}
1625
1626fn expect_str(v: Option<&Value>) -> Result<String, String> {
1627 match v {
1628 Some(Value::Str(s)) => Ok(s.clone()),
1629 Some(other) => Err(format!("expected Str, got {other:?}")),
1630 None => Err("missing argument".into()),
1631 }
1632}
1633
1634fn expect_int(v: Option<&Value>) -> Result<i64, String> {
1635 match v {
1636 Some(Value::Int(n)) => Ok(*n),
1637 Some(other) => Err(format!("expected Int, got {other:?}")),
1638 None => Err("missing argument".into()),
1639 }
1640}
1641
1642fn expect_float(v: Option<&Value>) -> Result<f64, String> {
1643 match v {
1644 Some(Value::Float(f)) => Ok(*f),
1645 Some(other) => Err(format!("expected Float, got {other:?}")),
1646 None => Err("missing argument".into()),
1647 }
1648}
1649
1650fn expect_list(v: Option<&Value>) -> Result<&Vec<Value>, String> {
1651 match v {
1652 Some(Value::List(xs)) => Ok(xs),
1653 Some(other) => Err(format!("expected List, got {other:?}")),
1654 None => Err("missing argument".into()),
1655 }
1656}
1657
1658fn expect_bool(v: Option<&Value>) -> Result<bool, String> {
1659 match v {
1660 Some(Value::Bool(b)) => Ok(*b),
1661 Some(other) => Err(format!("expected Bool, got {other:?}")),
1662 None => Err("missing argument".into()),
1663 }
1664}
1665
1666fn expect_deque(v: Option<&Value>) -> Result<&std::collections::VecDeque<Value>, String> {
1667 match v {
1668 Some(Value::Deque(d)) => Ok(d),
1669 Some(other) => Err(format!("expected Deque, got {other:?}")),
1670 None => Err("missing argument".into()),
1671 }
1672}
1673
1674fn some(v: Value) -> Value { Value::Variant { name: "Some".into(), args: vec![v] } }
1675fn none() -> Value { Value::Variant { name: "None".into(), args: Vec::new() } }
1676fn ok_v(v: Value) -> Value { Value::Variant { name: "Ok".into(), args: vec![v] } }
1677fn err_v(v: Value) -> Value { Value::Variant { name: "Err".into(), args: vec![v] } }
1678
1679fn parser_node(kind: &str, fields: &[(&str, Value)]) -> Value {
1687 let mut r = indexmap::IndexMap::new();
1688 r.insert("kind".into(), Value::Str(kind.into()));
1689 for (k, v) in fields {
1690 r.insert((*k).into(), v.clone());
1691 }
1692 Value::Record(r)
1693}
1694
1695fn splitmix64(state: u64) -> (u64, u64) {
1706 let next = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
1707 let mut z = next;
1708 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
1709 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
1710 let z = z ^ (z >> 31);
1711 (z, next)
1712}
1713
1714fn rng_value(state: u64) -> Value {
1718 let mut fields = indexmap::IndexMap::new();
1719 fields.insert("state".into(), Value::Int(state as i64));
1720 Value::Record(fields)
1721}
1722
1723fn rng_decode(v: Option<&Value>) -> Result<u64, String> {
1725 let rec = match v {
1726 Some(Value::Record(r)) => r,
1727 Some(other) => return Err(format!("expected Rng, got {other:?}")),
1728 None => return Err("missing Rng arg".into()),
1729 };
1730 match rec.get("state") {
1731 Some(Value::Int(n)) => Ok(*n as u64),
1732 _ => Err("malformed Rng: missing `state :: Int`".into()),
1733 }
1734}
1735
1736fn expect_record_pure(v: Option<&Value>) -> Result<&indexmap::IndexMap<String, Value>, String> {
1739 match v {
1740 Some(Value::Record(r)) => Ok(r),
1741 Some(other) => Err(format!("expected Record, got {other:?}")),
1742 None => Err("missing Record argument".into()),
1743 }
1744}
1745
1746fn http_decode_err_pure(msg: String) -> Value {
1747 let inner = Value::Variant {
1748 name: "DecodeError".into(),
1749 args: vec![Value::Str(msg)],
1750 };
1751 err_v(inner)
1752}
1753
1754fn http_set_header(
1759 mut req: indexmap::IndexMap<String, Value>,
1760 name: &str,
1761 value: &str,
1762) -> indexmap::IndexMap<String, Value> {
1763 use lex_bytecode::MapKey;
1764 let mut headers = match req.shift_remove("headers") {
1765 Some(Value::Map(m)) => m,
1766 _ => std::collections::BTreeMap::new(),
1767 };
1768 let key = MapKey::Str(name.to_lowercase());
1769 let lowered = name.to_lowercase();
1772 headers.retain(|k, _| match k {
1773 MapKey::Str(s) => s.to_lowercase() != lowered,
1774 _ => true,
1775 });
1776 headers.insert(key, Value::Str(value.to_string()));
1777 req.insert("headers".into(), Value::Map(headers));
1778 req
1779}
1780
1781fn http_append_query(
1787 mut req: indexmap::IndexMap<String, Value>,
1788 params: &std::collections::BTreeMap<lex_bytecode::MapKey, Value>,
1789) -> indexmap::IndexMap<String, Value> {
1790 use lex_bytecode::MapKey;
1791 let url = match req.get("url") {
1792 Some(Value::Str(s)) => s.clone(),
1793 _ => return req,
1794 };
1795 let mut pieces = Vec::new();
1796 for (k, v) in params {
1797 let kk = match k { MapKey::Str(s) => s.clone(), _ => continue };
1798 let vv = match v { Value::Str(s) => s.clone(), _ => continue };
1799 pieces.push(format!("{}={}", url_encode(&kk), url_encode(&vv)));
1800 }
1801 if pieces.is_empty() { return req; }
1802 let sep = if url.contains('?') { '&' } else { '?' };
1803 let new_url = format!("{url}{sep}{}", pieces.join("&"));
1804 req.insert("url".into(), Value::Str(new_url));
1805 req
1806}
1807
1808fn url_encode(s: &str) -> String {
1813 let mut out = String::with_capacity(s.len());
1814 for b in s.bytes() {
1815 match b {
1816 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
1817 out.push(b as char);
1818 }
1819 _ => out.push_str(&format!("%{:02X}", b)),
1820 }
1821 }
1822 out
1823}
1824
1825fn value_to_json(v: &Value) -> serde_json::Value { v.to_json() }
1826
1827fn unwrap_toml_datetime_markers(v: &mut serde_json::Value) {
1835 use serde_json::Value as J;
1836 match v {
1837 J::Object(map) => {
1838 if map.len() == 1 {
1842 if let Some(J::String(s)) = map.get("$__toml_private_datetime") {
1843 let s = s.clone();
1844 *v = J::String(s);
1845 return;
1846 }
1847 }
1848 for (_, child) in map.iter_mut() {
1849 unwrap_toml_datetime_markers(child);
1850 }
1851 }
1852 J::Array(items) => {
1853 for item in items.iter_mut() {
1854 unwrap_toml_datetime_markers(item);
1855 }
1856 }
1857 _ => {}
1858 }
1859}
1860
1861fn json_to_value(v: &serde_json::Value) -> Value { Value::from_json(v) }
1862
1863fn required_field_names(arg: Option<&Value>) -> Result<Vec<String>, String> {
1868 let list = expect_list(arg)?;
1869 let mut out = Vec::with_capacity(list.len());
1870 for v in list {
1871 match v {
1872 Value::Str(s) => out.push(s.clone()),
1873 other => return Err(format!(
1874 "parse_strict: required-fields list must contain Str, got {other:?}"
1875 )),
1876 }
1877 }
1878 Ok(out)
1879}
1880
1881fn check_required_fields(
1902 value: &serde_json::Value,
1903 required: &[String],
1904) -> Result<(), String> {
1905 if required.is_empty() {
1906 return Ok(());
1907 }
1908 if !matches!(value, serde_json::Value::Object(_)) {
1909 return Err(format!(
1910 "parse_strict: expected top-level object with fields {:?}, got {value}",
1911 required
1912 ));
1913 }
1914 let mut missing: Vec<String> = Vec::new();
1915 for path in required {
1916 if !path_exists(value, path) {
1917 missing.push(path.clone());
1918 }
1919 }
1920 if missing.is_empty() {
1921 Ok(())
1922 } else {
1923 Err(format!("missing required field(s): {}", missing.join(", ")))
1924 }
1925}
1926
1927fn path_exists(value: &serde_json::Value, path: &str) -> bool {
1932 let mut cursor = value;
1933 let segments = split_dotted_path(path);
1934 for seg in &segments {
1935 match cursor {
1936 serde_json::Value::Object(o) => match o.get(seg.as_str()) {
1937 Some(next) => cursor = next,
1938 None => return false,
1939 },
1940 _ => return false,
1941 }
1942 }
1943 true
1944}
1945
1946fn split_dotted_path(path: &str) -> Vec<String> {
1950 let mut out: Vec<String> = Vec::new();
1951 let mut cur = String::new();
1952 let mut iter = path.chars().peekable();
1953 while let Some(c) = iter.next() {
1954 if c == '\\' {
1955 if let Some(&'.') = iter.peek() {
1957 cur.push('.');
1958 iter.next();
1959 continue;
1960 }
1961 cur.push(c);
1962 } else if c == '.' {
1963 out.push(std::mem::take(&mut cur));
1964 } else {
1965 cur.push(c);
1966 }
1967 }
1968 out.push(cur);
1969 out
1970}
1971
1972fn extract_type_schema(v: Option<&Value>) -> Vec<(String, String)> {
1976 match v {
1977 Some(Value::List(pairs)) => pairs.iter().filter_map(|p| {
1978 if let Value::Tuple(items) = p {
1979 if items.len() == 2 {
1980 if let (Value::Str(name), Value::Str(tag)) = (&items[0], &items[1]) {
1981 return Some((name.clone(), tag.clone()));
1982 }
1983 }
1984 }
1985 None
1986 }).collect(),
1987 _ => vec![],
1988 }
1989}
1990
1991fn validate_field_types(
1997 json: &serde_json::Value,
1998 schema: &[(String, String)],
1999) -> Result<(), String> {
2000 if schema.is_empty() {
2001 return Ok(());
2002 }
2003 let obj = match json.as_object() {
2004 Some(o) => o,
2005 None => return Ok(()), };
2007 for (field, tag) in schema {
2008 if let Some(val) = obj.get(field) {
2009 if let Err(e) = check_json_type(val, tag) {
2010 return Err(format!("field `{field}`: {e}"));
2011 }
2012 }
2013 }
2014 Ok(())
2015}
2016
2017fn check_json_type(val: &serde_json::Value, tag: &str) -> Result<(), String> {
2019 use serde_json::Value as J;
2020 match (tag, val) {
2021 ("Int", J::Number(n)) if n.is_i64() || n.is_u64() => Ok(()),
2022 ("Int", other) => Err(format!("expected Int, got {}", json_type_name(other))),
2023 ("Float", J::Number(_)) => Ok(()),
2024 ("Float", other) => Err(format!("expected Float, got {}", json_type_name(other))),
2025 ("Bool", J::Bool(_)) => Ok(()),
2026 ("Bool", other) => Err(format!("expected Bool, got {}", json_type_name(other))),
2027 ("Str", J::String(_)) => Ok(()),
2028 ("Str", other) => Err(format!("expected Str, got {}", json_type_name(other))),
2029 (tag, J::Null) if tag.starts_with("Option[") => Ok(()),
2031 (tag, val) if tag.starts_with("Option[") && tag.ends_with(']') => {
2032 let inner = &tag[7..tag.len() - 1]; check_json_type(val, inner)
2034 }
2035 (tag, J::Array(items)) if tag.starts_with("List[") && tag.ends_with(']') => {
2037 let inner = &tag[5..tag.len() - 1]; for (i, item) in items.iter().enumerate() {
2039 if let Err(e) = check_json_type(item, inner) {
2040 return Err(format!("[{i}]: {e}"));
2041 }
2042 }
2043 Ok(())
2044 }
2045 ("Record", _) => Ok(()), ("Any", _) => Ok(()), _ => Ok(()), }
2049}
2050
2051fn json_type_name(v: &serde_json::Value) -> &'static str {
2052 match v {
2053 serde_json::Value::Null => "null",
2054 serde_json::Value::Bool(_) => "Bool",
2055 serde_json::Value::Number(_) => "Number",
2056 serde_json::Value::String(_) => "Str",
2057 serde_json::Value::Array(_) => "Array",
2058 serde_json::Value::Object(_) => "Object",
2059 }
2060}
2061
2062fn parse_dotenv(src: &str) -> Result<indexmap::IndexMap<String, String>, String> {
2073 let mut out = indexmap::IndexMap::new();
2074 for (idx, raw) in src.lines().enumerate() {
2075 let line = raw.trim();
2076 if line.is_empty() || line.starts_with('#') {
2077 continue;
2078 }
2079 let after_export = line.strip_prefix("export ").unwrap_or(line);
2082 let (k, v) = match after_export.split_once('=') {
2083 Some(kv) => kv,
2084 None => return Err(format!("dotenv.parse line {}: missing `=`", idx + 1)),
2085 };
2086 let key = k.trim();
2087 if key.is_empty() {
2088 return Err(format!("dotenv.parse line {}: empty key", idx + 1));
2089 }
2090 let v_trim = v.trim();
2091 let value = if let Some(q) = v_trim.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
2092 q.to_string()
2093 } else if let Some(q) = v_trim.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
2094 q.to_string()
2095 } else {
2096 v_trim.to_string()
2097 };
2098 out.insert(key.to_string(), value);
2099 }
2100 Ok(out)
2101}
2102
2103fn instant_from_chrono<Tz: chrono::TimeZone>(dt: chrono::DateTime<Tz>) -> i64 {
2109 dt.timestamp_nanos_opt().unwrap_or(i64::MAX)
2110}
2111
2112fn chrono_from_instant(n: i64) -> chrono::DateTime<chrono::Utc> {
2113 let secs = n.div_euclid(1_000_000_000);
2114 let nanos = n.rem_euclid(1_000_000_000) as u32;
2115 use chrono::TimeZone;
2116 chrono::Utc
2117 .timestamp_opt(secs, nanos)
2118 .single()
2119 .unwrap_or_else(chrono::Utc::now)
2120}
2121
2122fn format_iso(n: i64) -> String {
2123 chrono_from_instant(n).to_rfc3339()
2124}
2125
2126enum TzArg {
2129 Utc,
2130 Local,
2131 Offset(i32),
2133 Iana(String),
2135}
2136
2137fn parse_tz_arg(v: Option<&Value>) -> Result<TzArg, String> {
2138 match v {
2139 Some(Value::Variant { name, args }) => match (name.as_str(), args.as_slice()) {
2140 ("Utc", []) => Ok(TzArg::Utc),
2141 ("Local", []) => Ok(TzArg::Local),
2142 ("Offset", [Value::Int(m)]) => {
2143 let m = i32::try_from(*m).map_err(|_| {
2144 format!("Tz::Offset: minutes out of range: {m}")
2145 })?;
2146 Ok(TzArg::Offset(m))
2147 }
2148 ("Iana", [Value::Str(s)]) => Ok(TzArg::Iana(s.clone())),
2149 (other, _) => Err(format!(
2150 "expected Tz variant (Utc | Local | Offset(Int) | Iana(Str)), got `{other}` with {} arg(s)",
2151 args.len()
2152 )),
2153 },
2154 Some(other) => Err(format!("expected Tz variant, got {other:?}")),
2155 None => Err("missing Tz argument".into()),
2156 }
2157}
2158
2159fn resolve_tz_to_components(n: i64, tz: &TzArg) -> Result<Value, String> {
2160 use chrono::{TimeZone, Datelike, Timelike, Offset};
2161 let utc_dt = chrono_from_instant(n);
2162 let (y, m, d, hh, mm, ss, ns, off_min) = match tz {
2163 TzArg::Utc => {
2164 let d = utc_dt;
2165 (d.year(), d.month() as i32, d.day() as i32,
2166 d.hour() as i32, d.minute() as i32, d.second() as i32,
2167 d.nanosecond() as i32, 0)
2168 }
2169 TzArg::Local => {
2170 let d = utc_dt.with_timezone(&chrono::Local);
2171 let off = d.offset().fix().local_minus_utc() / 60;
2172 (d.year(), d.month() as i32, d.day() as i32,
2173 d.hour() as i32, d.minute() as i32, d.second() as i32,
2174 d.nanosecond() as i32, off)
2175 }
2176 TzArg::Offset(off_min) => {
2177 let off_secs = off_min.saturating_mul(60);
2178 let fixed = chrono::FixedOffset::east_opt(off_secs)
2179 .ok_or("to_components: offset out of range")?;
2180 let d = utc_dt.with_timezone(&fixed);
2181 (d.year(), d.month() as i32, d.day() as i32,
2182 d.hour() as i32, d.minute() as i32, d.second() as i32,
2183 d.nanosecond() as i32, *off_min)
2184 }
2185 TzArg::Iana(name) => {
2186 let tz: chrono_tz::Tz = name.parse()
2187 .map_err(|e| format!("to_components: unknown timezone `{name}`: {e}"))?;
2188 let d = utc_dt.with_timezone(&tz);
2189 let off = d.offset().fix().local_minus_utc() / 60;
2190 (d.year(), d.month() as i32, d.day() as i32,
2191 d.hour() as i32, d.minute() as i32, d.second() as i32,
2192 d.nanosecond() as i32, off)
2193 }
2194 };
2195 let mut rec = indexmap::IndexMap::new();
2196 rec.insert("year".into(), Value::Int(y as i64));
2197 rec.insert("month".into(), Value::Int(m as i64));
2198 rec.insert("day".into(), Value::Int(d as i64));
2199 rec.insert("hour".into(), Value::Int(hh as i64));
2200 rec.insert("minute".into(), Value::Int(mm as i64));
2201 rec.insert("second".into(), Value::Int(ss as i64));
2202 rec.insert("nano".into(), Value::Int(ns as i64));
2203 rec.insert("tz_offset_minutes".into(), Value::Int(off_min as i64));
2204 let _ = chrono::Utc.timestamp_opt(0, 0); Ok(Value::Record(rec))
2206}
2207
2208
2209fn instant_from_components(rec: &indexmap::IndexMap<String, Value>) -> Result<i64, String> {
2210 use chrono::TimeZone;
2211 fn get_int(rec: &indexmap::IndexMap<String, Value>, k: &str) -> Result<i64, String> {
2212 match rec.get(k) {
2213 Some(Value::Int(n)) => Ok(*n),
2214 other => Err(format!("from_components: missing or non-int field `{k}`: {other:?}")),
2215 }
2216 }
2217 let y = get_int(rec, "year")? as i32;
2218 let m = get_int(rec, "month")? as u32;
2219 let d = get_int(rec, "day")? as u32;
2220 let hh = get_int(rec, "hour")? as u32;
2221 let mm = get_int(rec, "minute")? as u32;
2222 let ss = get_int(rec, "second")? as u32;
2223 let ns = get_int(rec, "nano")? as u32;
2224 let off_min = get_int(rec, "tz_offset_minutes")? as i32;
2225 let off = chrono::FixedOffset::east_opt(off_min * 60)
2226 .ok_or("from_components: offset out of range")?;
2227 let dt = off
2228 .with_ymd_and_hms(y, m, d, hh, mm, ss)
2229 .single()
2230 .ok_or("from_components: invalid or ambiguous date/time")?;
2231 let dt = dt + chrono::Duration::nanoseconds(ns as i64);
2232 Ok(instant_from_chrono(dt))
2233}