1use super::*;
4
5impl EffectHandler for DefaultHandler {
6 fn enter_request_scope(&mut self) -> u64 {
10 let id = self.next_scope_id;
11 self.next_scope_id = self.next_scope_id.wrapping_add(1);
12 self.arena_stack.push((id, crate::arena::Arena::new()));
13 id
14 }
15
16 fn exit_request_scope(&mut self, scope_id: u64) {
22 if let Some(pos) = self.arena_stack.iter().position(|(id, _)| *id == scope_id) {
23 self.arena_stack.truncate(pos);
28 }
29 }
30
31 fn note_call_budget(&mut self, cost: u64) -> Result<(), String> {
36 let Some(ceiling) = self.budget_ceiling else { return Ok(()); };
39 loop {
45 let cur = self.budget_remaining.load(Ordering::SeqCst);
46 if cost > cur {
47 let used = ceiling.saturating_sub(cur);
48 return Err(format!(
49 "budget exceeded: requested {cost}, used so far {used}, ceiling {ceiling}"));
50 }
51 let next = cur - cost;
52 if self.budget_remaining.compare_exchange(cur, next,
55 Ordering::SeqCst, Ordering::SeqCst).is_ok() {
56 return Ok(());
57 }
58 }
59 }
60
61 fn dispatch(&mut self, kind: &str, op: &str, args: Vec<Value>) -> Result<Value, String> {
62 if is_pure_call(kind, op) {
66 return call_pure_builtin(kind, op, args);
67 }
68 if kind == "process" {
72 if op == "exit" {
78 self.ensure_kind_allowed("proc_exit")?;
79 let code = match args.first() {
80 Some(Value::Int(n)) => *n,
81 other => {
82 return Err(format!(
83 "process.exit expects an Int status, got {other:?}"
84 ))
85 }
86 };
87 let code = code.clamp(0, 255) as i64;
92 let _ = self.requested_exit.compare_exchange(
95 crate::handler::NO_EXIT,
96 code,
97 Ordering::SeqCst,
98 Ordering::SeqCst,
99 );
100 return Ok(Value::Unit);
101 }
102 self.ensure_kind_allowed("proc")?;
103 return self.dispatch_process(op, args);
104 }
105 if kind == "approval" {
106 self.ensure_kind_allowed("approval")?;
107 return self.dispatch_approval(op, args);
108 }
109 if kind == "log" {
110 let effect_kind = match op {
113 "debug" | "info" | "warn" | "error" => "log",
114 "set_level" | "set_format" => "io",
115 "set_sink" => {
116 self.ensure_kind_allowed("io")?;
117 self.ensure_kind_allowed("fs_write")?;
118 return self.dispatch_log(op, args);
119 }
120 other => return Err(format!("unsupported log.{other}")),
121 };
122 self.ensure_kind_allowed(effect_kind)?;
123 return self.dispatch_log(op, args);
124 }
125 if kind == "fs" {
126 let effect_kind = match op {
127 "exists" | "is_file" | "is_dir" | "stat"
128 | "list_dir" | "walk" | "glob" => "fs_walk",
129 "mkdir_p" | "remove" => "fs_write",
130 "copy" => {
131 self.ensure_kind_allowed("fs_walk")?;
132 self.ensure_kind_allowed("fs_write")?;
133 return self.dispatch_fs(op, args);
134 }
135 other => return Err(format!("unsupported fs.{other}")),
136 };
137 self.ensure_kind_allowed(effect_kind)?;
138 return self.dispatch_fs(op, args);
139 }
140 if kind == "datetime" && op == "now" {
147 self.ensure_kind_allowed("time")?;
148 if let Ok(s) = std::env::var("LEX_TEST_NOW") {
150 if let Ok(secs) = s.trim().parse::<i64>() {
151 return Ok(Value::Int(secs.saturating_mul(1_000_000_000)));
152 }
153 }
154 let now = chrono::Utc::now();
155 let nanos = now.timestamp_nanos_opt().unwrap_or(i64::MAX);
156 return Ok(Value::Int(nanos));
157 }
158 if kind == "crypto" && op == "random" {
159 self.ensure_kind_allowed("random")?;
160 let n = expect_int(args.first())?;
161 if !(0..=1_048_576).contains(&n) {
162 return Err("crypto.random: n must be in 0..=1048576".into());
163 }
164 use rand::{rngs::SysRng, TryRng};
165 let mut buf = vec![0u8; n as usize];
166 SysRng.try_fill_bytes(&mut buf)
167 .map_err(|e| format!("crypto.random: OS RNG: {e}"))?;
168 return Ok(Value::Bytes(buf));
169 }
170 if kind == "crypto" && op == "random_str_hex" {
175 self.ensure_kind_allowed("random")?;
176 let n = expect_int(args.first())?;
177 if !(0..=1_048_576).contains(&n) {
178 return Err("crypto.random_str_hex: n must be in 0..=1048576".into());
179 }
180 use rand::{rngs::SysRng, TryRng};
181 let mut buf = vec![0u8; n as usize];
182 SysRng.try_fill_bytes(&mut buf)
183 .map_err(|e| format!("crypto.random_str_hex: OS RNG: {e}"))?;
184 return Ok(Value::Str(hex::encode(&buf).into()));
185 }
186 if kind == "crypto" && op == "p256_generate" {
199 self.ensure_kind_allowed("random")?;
200 use p256::ecdsa::SigningKey;
201 use rand::{rngs::SysRng, TryRng};
202 for _ in 0..16 {
203 let mut buf = [0u8; 32];
204 SysRng.try_fill_bytes(&mut buf)
205 .map_err(|e| format!("crypto.p256_generate: OS RNG: {e}"))?;
206 if let Ok(sk) = SigningKey::from_slice(&buf) {
207 return Ok(ok(Value::Bytes(sk.to_bytes().to_vec())));
208 }
209 }
210 return Ok(err(Value::Str(
211 "crypto.p256_generate: failed to sample a valid scalar".into())));
212 }
213 if kind == "crypto" && op == "secp256k1_generate" {
220 self.ensure_kind_allowed("random")?;
221 use k256::ecdsa::SigningKey;
222 use rand::{rngs::SysRng, TryRng};
223 for _ in 0..16 {
224 let mut buf = [0u8; 32];
225 SysRng.try_fill_bytes(&mut buf)
226 .map_err(|e| format!("crypto.secp256k1_generate: OS RNG: {e}"))?;
227 if let Ok(sk) = SigningKey::from_slice(&buf) {
228 return Ok(ok(Value::Bytes(sk.to_bytes().to_vec())));
229 }
230 }
231 return Ok(err(Value::Str(
232 "crypto.secp256k1_generate: failed to sample a valid scalar".into())));
233 }
234 if kind == "agent" {
247 let effect_kind = match op {
248 "local_complete" => "llm_local",
249 "cloud_complete" => "llm_cloud",
250 "cloud_stream" => "llm_cloud",
251 "send_a2a" => "a2a",
252 "call_mcp" => "mcp",
253 other => return Err(format!("unsupported agent.{other}")),
254 };
255 self.ensure_kind_allowed(effect_kind)?;
256 return match op {
264 "call_mcp" => Ok(self.dispatch_call_mcp(args)),
265 "local_complete" => Ok(dispatch_llm_local(args)),
266 "cloud_complete" => Ok(dispatch_llm_cloud(args)),
267 "cloud_stream" => Ok(self.dispatch_cloud_stream(args)),
268 _ => Ok(ok(Value::Str(format!("<{effect_kind} stub>").into()))),
269 };
270 }
271 if kind == "stream" {
272 self.ensure_kind_allowed("stream")?;
279 return match op {
280 "next" => Ok(self.dispatch_stream_next(args)),
281 "collect" => Ok(self.dispatch_stream_collect(args)),
282 other => Err(format!("unsupported stream.{other}")),
283 };
284 }
285 if kind == "http" && matches!(op, "send" | "get" | "post" | "stream_lines") {
286 self.ensure_kind_allowed("net")?;
287 return match op {
288 "send" => {
289 let req = expect_record(args.first())?;
290 Ok(http_send_record(self, req))
291 }
292 "get" => {
293 let url = expect_str(args.first())?.to_string();
294 self.ensure_host_allowed(&url)?;
295 Ok(http_send_simple("GET", &url, None, "", None))
296 }
297 "post" => {
298 let url = expect_str(args.first())?.to_string();
299 let body = expect_bytes(args.get(1))?.clone();
300 let content_type = expect_str(args.get(2))?.to_string();
301 self.ensure_host_allowed(&url)?;
302 Ok(http_send_simple("POST", &url, Some(body), &content_type, None))
303 }
304 "stream_lines" => {
305 let url = expect_str(args.first())?.to_string();
306 let headers_val = args.get(1).cloned().unwrap_or(Value::Map(Default::default()));
307 let body = expect_str(args.get(2))?.to_string();
308 self.ensure_host_allowed(&url)?;
309 Ok(http_stream_lines_impl(self, &url, &headers_val, &body))
310 }
311 _ => unreachable!(),
312 };
313 }
314 if kind == "arrow" && op == "read_csv" {
320 self.ensure_kind_allowed("fs_read")?;
321 let path = expect_str(args.first())?.to_string();
322 let resolved = self.resolve_read_path(&path);
323 if !self.policy.allow_fs_read.is_empty()
324 && !self.policy.allow_fs_read.iter().any(|a| resolved.starts_with(a))
325 {
326 return Err(format!("arrow.read_csv: `{path}` outside --allow-fs-read"));
327 }
328 return match crate::arrow::read_csv_at(&resolved) {
329 Ok(v) => Ok(ok(v)),
330 Err(e) => Ok(err(Value::Str(e.into()))),
331 };
332 }
333 if kind == "arrow" && (op == "read_parquet" || op == "read_parquet_cols") {
337 self.ensure_kind_allowed("fs_read")?;
338 let path = expect_str(args.first())?.to_string();
339 let resolved = self.resolve_read_path(&path);
340 if !self.policy.allow_fs_read.is_empty()
341 && !self.policy.allow_fs_read.iter().any(|a| resolved.starts_with(a))
342 {
343 return Err(format!("arrow.{op}: `{path}` outside --allow-fs-read"));
344 }
345 let r = if op == "read_parquet" {
346 crate::arrow::read_parquet_at(&resolved)
347 } else {
348 let cols = match args.get(1) {
349 Some(Value::List(items)) => {
350 let mut out = Vec::with_capacity(items.len());
351 for v in items.iter() {
352 match v {
353 Value::Str(s) => out.push(s.to_string()),
354 other => return Err(format!(
355 "arrow.read_parquet_cols: column name not Str: {other:?}")),
356 }
357 }
358 out
359 }
360 other => return Err(format!(
361 "arrow.read_parquet_cols: expected List[Str], got {other:?}")),
362 };
363 crate::arrow::read_parquet_cols_at(&resolved, &cols)
364 };
365 return match r {
366 Ok(v) => Ok(ok(v)),
367 Err(e) => Ok(err(Value::Str(e.into()))),
368 };
369 }
370 if kind == "arrow" && (op == "write_parquet" || op == "write_csv") {
373 self.ensure_kind_allowed("fs_write")?;
374 let table_v = args.first().cloned().unwrap_or(Value::Unit);
375 let rb = match &table_v {
376 Value::ArrowTable(t) => Arc::clone(t),
377 other => return Err(format!("arrow.{op}: first arg must be arrow.Table, got {other:?}")),
378 };
379 let path = expect_str(args.get(1))?.to_string();
380 if let Err(e) = self.ensure_fs_write_path(&path) {
381 return Ok(err(Value::Str(format!("arrow.{op}: {e}").into())));
382 }
383 let r = if op == "write_parquet" {
384 crate::arrow::write_parquet_at(&rb, std::path::Path::new(&path))
385 } else {
386 crate::arrow::write_csv_at(&rb, std::path::Path::new(&path))
387 };
388 return match r {
389 Ok(_) => Ok(ok(Value::Unit)),
390 Err(e) => Ok(err(Value::Str(e.into()))),
391 };
392 }
393 if kind == "net" && op == "default_opts" {
398 return Ok(ServeOpts::lex_defaults().to_value());
399 }
400 if kind == "tls" {
408 return match op {
409 "from_pem_files" => {
410 self.ensure_kind_allowed("fs_read")?;
411 dispatch_tls_from_pem_files(self, args)
412 }
413 "self_signed" => dispatch_tls_self_signed(args),
414 other => Err(format!("unsupported tls.{other}")),
415 };
416 }
417 if kind == "redis" {
421 self.ensure_kind_allowed("net")?;
422 } else if kind == "rand" {
423 self.ensure_kind_allowed("random")?;
427 } else {
428 self.ensure_kind_allowed(kind)?;
429 }
430 match (kind, op) {
431 ("io", "print") => {
432 let line = expect_str(args.first())?;
433 self.sink.print_line(line);
434 Ok(Value::Unit)
435 }
436 ("io", "read") => {
437 let path = expect_str(args.first())?.to_string();
438 let resolved = self.resolve_read_path(&path);
439 if !self.policy.allow_fs_read.is_empty()
446 && !self.policy.allow_fs_read.iter().any(|a| resolved.starts_with(a))
447 {
448 return Err(format!("read of `{path}` outside --allow-fs-read"));
449 }
450 match std::fs::read_to_string(&resolved) {
451 Ok(s) => Ok(ok(Value::Str(s.into()))),
452 Err(e) => Ok(err(Value::Str(format!("{e}").into()))),
453 }
454 }
455 ("io", "readline") => {
456 use std::io::BufRead;
457 let stdin = std::io::stdin();
458 let mut line = String::new();
459 match stdin.lock().read_line(&mut line) {
460 Ok(0) => Ok(Value::Variant { name: "None".into(), args: vec![] }),
461 Ok(_) => {
462 if line.ends_with('\n') { line.pop(); }
463 if line.ends_with('\r') { line.pop(); }
464 Ok(Value::Variant { name: "Some".into(), args: vec![Value::Str(line.into())] })
465 }
466 Err(_) => Ok(Value::Variant { name: "None".into(), args: vec![] }),
467 }
468 }
469 ("io", "argv") => {
470 let list: Vec<Value> = self.program_args.iter()
471 .map(|s| Value::Str(s.as_str().into()))
472 .collect();
473 Ok(Value::List(list.into()))
474 }
475 ("io", "write") => {
476 let path = expect_str(args.first())?.to_string();
477 let contents = expect_str(args.get(1))?.to_string();
478 if !self.policy.allow_fs_write.is_empty() {
482 let raw = std::env::current_dir()
483 .map(|cwd| cwd.join(&path))
484 .unwrap_or_else(|_| std::path::PathBuf::from(&path));
485 let p = std::fs::canonicalize(&raw).unwrap_or_else(|_| {
489 raw.parent()
490 .and_then(|par| std::fs::canonicalize(par).ok())
491 .map(|par| par.join(raw.file_name().unwrap_or_default()))
492 .unwrap_or(raw)
493 });
494 let allowed = self.policy.allow_fs_write.iter().any(|a| {
495 let ca = std::fs::canonicalize(a).unwrap_or_else(|_| a.clone());
496 p.starts_with(&ca)
497 });
498 if !allowed {
499 return Err(format!("write to `{path}` outside --allow-fs-write"));
500 }
501 }
502 match std::fs::write(&path, contents) {
503 Ok(_) => Ok(ok(Value::Unit)),
504 Err(e) => Ok(err(Value::Str(format!("{e}").into()))),
505 }
506 }
507 ("time", "now") => {
508 if let Ok(s) = std::env::var("LEX_TEST_NOW") {
510 if let Ok(secs) = s.trim().parse::<i64>() {
511 return Ok(Value::Int(secs));
512 }
513 }
514 let secs = SystemTime::now().duration_since(UNIX_EPOCH)
515 .map_err(|e| format!("time: {e}"))?.as_secs();
516 Ok(Value::Int(secs as i64))
517 }
518 ("time", "now_ms") => {
519 if let Ok(s) = std::env::var("LEX_TEST_NOW") {
524 if let Ok(secs) = s.trim().parse::<i64>() {
525 return Ok(Value::Int(secs.saturating_mul(1000)));
526 }
527 }
528 let ms = SystemTime::now().duration_since(UNIX_EPOCH)
529 .map_err(|e| format!("time: {e}"))?.as_millis();
530 Ok(Value::Int(ms as i64))
531 }
532 ("time", "now_str") => {
533 if let Ok(s) = std::env::var("LEX_TEST_NOW") {
537 if let Ok(secs) = s.trim().parse::<i64>() {
538 let dt = chrono::DateTime::<chrono::Utc>::from_timestamp(secs, 0)
539 .unwrap_or_else(chrono::Utc::now);
540 return Ok(Value::Str(dt.to_rfc3339().into()));
541 }
542 }
543 Ok(Value::Str(chrono::Utc::now().to_rfc3339().into()))
544 }
545 ("time", "mono_ns") => {
546 static MONO_START: OnceLock<std::time::Instant> = OnceLock::new();
554 let start = MONO_START.get_or_init(std::time::Instant::now);
555 let dur = std::time::Instant::now().duration_since(*start);
556 Ok(Value::Int(dur.as_nanos() as i64))
557 }
558 ("time", "sleep_ms") => {
559 let n = expect_int(args.first())?;
567 if n > 0 {
568 let ms = (n as u64).min(60_000);
569 std::thread::sleep(std::time::Duration::from_millis(ms));
570 }
571 Ok(Value::Unit)
572 }
573 ("time", "sleep") => {
574 let nanos = expect_int(args.first())?;
580 if nanos > 0 {
581 let bounded_nanos = (nanos as u64).min(60_000 * 1_000_000);
582 std::thread::sleep(std::time::Duration::from_nanos(bounded_nanos));
583 }
584 Ok(Value::Unit)
585 }
586 ("rand", "int_in") => {
587 let lo = expect_int(args.first())?;
591 let hi = expect_int(args.get(1))?;
592 if hi < lo {
593 return Err(format!("rand.int_in: empty range [{lo}, {hi}]"));
594 }
595 use rand::{rngs::SysRng, TryRng};
596 let span = (hi as i128 - lo as i128 + 1) as u128;
599 let mut buf = [0u8; 16];
600 SysRng.try_fill_bytes(&mut buf)
601 .map_err(|e| format!("rand.int_in: OS RNG: {e}"))?;
602 let draw = (u128::from_le_bytes(buf) % span) as i128;
603 Ok(Value::Int((lo as i128 + draw) as i64))
604 }
605 ("env", "get") => {
610 let name = expect_str(args.first())?;
611 Ok(match std::env::var(name) {
612 Ok(v) => Value::Variant {
613 name: "Some".into(),
614 args: vec![Value::Str(v.into())],
615 },
616 Err(_) => Value::Variant { name: "None".into(), args: Vec::new() },
617 })
618 }
619 ("budget", _) => {
620 Ok(Value::Unit)
623 }
624 ("net", "get") => {
625 let url = expect_str(args.first())?.to_string();
626 self.ensure_host_allowed(&url)?;
627 Ok(http_request("GET", &url, None))
628 }
629 ("net", "post") => {
630 let url = expect_str(args.first())?.to_string();
631 let body = expect_str(args.get(1))?.to_string();
632 self.ensure_host_allowed(&url)?;
633 Ok(http_request("POST", &url, Some(&body)))
634 }
635 ("net", "udp_open") => {
637 let port = match args.first() {
638 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
639 _ => return Err("net.udp_open(port): port must be Int 0..=65535".into()),
640 };
641 let mut reg = udp_registry().lock().unwrap();
642 if reg.len() >= MAX_UDP_HANDLES {
643 return Ok(err(Value::Str(format!(
644 "net.udp_open: too many open sockets ({MAX_UDP_HANDLES}); \
645 close them with net.udp_close"
646 ).into())));
647 }
648 match std::net::UdpSocket::bind(("0.0.0.0", port)) {
649 Ok(sock) => {
650 let handle = next_udp_handle();
651 reg.insert(handle, sock);
652 Ok(ok(Value::Int(handle as i64)))
653 }
654 Err(e) => Ok(err(Value::Str(format!("net.udp_open: {e}").into()))),
655 }
656 }
657 ("net", "udp_close") => {
658 let handle = expect_int(args.first()).map_err(|e| format!("net.udp_close(sock): {e}"))?;
659 udp_registry().lock().unwrap().remove(&(handle as u64));
662 Ok(ok(Value::Unit))
663 }
664 ("net", "udp_send") => {
665 let handle = expect_int(args.first()).map_err(|e| format!("net.udp_send(sock, ...): {e}"))?;
666 let host = expect_str(args.get(1))?.to_string();
667 let port = match args.get(2) {
668 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
669 _ => return Err("net.udp_send(sock, host, port, data): port must be Int 0..=65535".into()),
670 };
671 let data = match args.get(3) {
672 Some(Value::Bytes(b)) => b.clone(),
673 _ => return Err("net.udp_send(sock, host, port, data): data must be Bytes".into()),
674 };
675 if let Err(e) = self.ensure_udp_dest_allowed(&host) {
682 return Ok(err(Value::Str(e.into())));
683 }
684 let res = with_udp(handle, "net.udp_send", |sock| {
685 sock.send_to(&data, (host.as_str(), port))
686 .map_err(|e| format!("net.udp_send: {e}"))
687 });
688 match res {
689 Ok(n) => Ok(ok(Value::Int(n as i64))),
690 Err(e) => Ok(err(Value::Str(e.into()))),
691 }
692 }
693 ("net", "udp_recv") => {
694 let handle = expect_int(args.first()).map_err(|e| format!("net.udp_recv(sock, ...): {e}"))?;
695 let timeout_ms = expect_int(args.get(1)).map_err(|e| format!("net.udp_recv(..., timeout_ms): {e}"))?;
696 if timeout_ms < 0 {
697 return Err("net.udp_recv(sock, timeout_ms): timeout_ms must be >= 0".into());
698 }
699 let res = with_udp(handle, "net.udp_recv", |sock| {
700 let d = std::time::Duration::from_millis(
704 if timeout_ms == 0 { 1 } else { timeout_ms as u64 });
705 sock.set_read_timeout(Some(d))
706 .map_err(|e| format!("net.udp_recv: setting timeout: {e}"))?;
707 let mut buf = vec![0u8; 65_507];
712 match sock.recv_from(&mut buf) {
713 Ok((n, addr)) => {
714 buf.truncate(n);
715 Ok(udp_datagram_value(buf, addr))
716 }
717 Err(e) if matches!(e.kind(),
718 std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) =>
719 Err(format!("net.udp_recv: timed out after {timeout_ms}ms")),
724 Err(e) => Err(format!("net.udp_recv: {e}")),
725 }
726 });
727 match res {
728 Ok(v) => Ok(ok(v)),
729 Err(e) => Ok(err(Value::Str(e.into()))),
730 }
731 }
732 ("net", "udp_broadcast") => {
733 let handle = expect_int(args.first()).map_err(|e| format!("net.udp_broadcast(sock, on): {e}"))?;
734 let on = matches!(args.get(1), Some(Value::Bool(true)));
735 let res = with_udp(handle, "net.udp_broadcast", |sock| {
736 sock.set_broadcast(on)
737 .map_err(|e| format!("net.udp_broadcast: {e}"))
738 });
739 match res {
740 Ok(()) => Ok(ok(Value::Unit)),
741 Err(e) => Ok(err(Value::Str(e.into()))),
742 }
743 }
744 ("net", "udp_join_multicast") => {
745 let handle = expect_int(args.first()).map_err(|e| format!("net.udp_join_multicast(sock, group): {e}"))?;
746 let group = expect_str(args.get(1))?.to_string();
747 let res = with_udp(handle, "net.udp_join_multicast", |sock| {
748 let g: std::net::Ipv4Addr = group.parse().map_err(|_| format!(
749 "net.udp_join_multicast: `{group}` is not an IPv4 address"))?;
750 if !g.is_multicast() {
751 return Err(format!(
752 "net.udp_join_multicast: {g} is not a multicast address \
753 (224.0.0.0/4)"));
754 }
755 sock.join_multicast_v4(&g, &std::net::Ipv4Addr::UNSPECIFIED)
756 .map_err(|e| format!("net.udp_join_multicast: {e}"))
757 });
758 match res {
759 Ok(()) => Ok(ok(Value::Unit)),
760 Err(e) => Ok(err(Value::Str(e.into()))),
761 }
762 }
763 ("net", "serve") => {
764 let port = match args.first() {
765 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
766 _ => return Err("net.serve(port, handler): port must be Int 0..=65535".into()),
767 };
768 let handler_name = expect_str(args.get(1))?.to_string();
769 let program = self.program.clone()
770 .ok_or_else(|| "net.serve requires a Program reference; use DefaultHandler::with_program".to_string())?;
771 let policy = self.policy.clone();
772 serve_http(port, handler_name, program, policy, None, ServeOpts::from_env())
773 }
774 ("net", "serve_fn") => {
775 let port = match args.first() {
776 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
777 _ => return Err("net.serve_fn(port, handler): port must be Int 0..=65535".into()),
778 };
779 let closure = match args.into_iter().nth(1) {
780 Some(c @ Value::Closure { .. }) => c,
781 _ => return Err("net.serve_fn(port, handler): handler must be a closure".into()),
782 };
783 let program = self.program.clone()
784 .ok_or_else(|| "net.serve_fn requires a Program reference; use DefaultHandler::with_program".to_string())?;
785 let policy = self.policy.clone();
786 serve_http_fn(port, closure, program, policy, ServeOpts::from_env())
787 }
788 ("net", "serve_routed") => {
789 let port = match args.first() {
790 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
791 _ => return Err("net.serve_routed(port, routes, fallback): port must be Int 0..=65535".into()),
792 };
793 let routes_val = args.get(1).cloned()
794 .ok_or_else(|| "net.serve_routed(port, routes, fallback): missing routes".to_string())?;
795 let fallback = match args.into_iter().nth(2) {
796 Some(c @ Value::Closure { .. }) => c,
797 _ => return Err("net.serve_routed(port, routes, fallback): fallback must be a closure".into()),
798 };
799 let routes = decode_routes_arg(routes_val)?;
800 let program = self.program.clone()
801 .ok_or_else(|| "net.serve_routed requires a Program reference; use DefaultHandler::with_program".to_string())?;
802 let policy = self.policy.clone();
803 serve_http_routed(port, routes, fallback, program, policy, ServeOpts::from_env())
804 }
805 ("net", "serve_with") => {
806 let port = match args.first() {
808 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
809 _ => return Err("net.serve_with(port, handler, opts): port must be Int 0..=65535".into()),
810 };
811 let handler_name = expect_str(args.get(1))?.to_string();
812 let opts = decode_serve_opts(args.get(2)
813 .ok_or_else(|| "net.serve_with(port, handler, opts): missing opts".to_string())?)?;
814 let program = self.program.clone()
815 .ok_or_else(|| "net.serve_with requires a Program reference; use DefaultHandler::with_program".to_string())?;
816 let policy = self.policy.clone();
817 serve_http(port, handler_name, program, policy, None, opts)
818 }
819 ("net", "serve_fn_with") => {
820 let port = match args.first() {
822 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
823 _ => return Err("net.serve_fn_with(port, handler, opts): port must be Int 0..=65535".into()),
824 };
825 let opts = decode_serve_opts(args.get(2)
826 .ok_or_else(|| "net.serve_fn_with(port, handler, opts): missing opts".to_string())?)?;
827 let closure = match args.into_iter().nth(1) {
828 Some(c @ Value::Closure { .. }) => c,
829 _ => return Err("net.serve_fn_with(port, handler, opts): handler must be a closure".into()),
830 };
831 let program = self.program.clone()
832 .ok_or_else(|| "net.serve_fn_with requires a Program reference; use DefaultHandler::with_program".to_string())?;
833 let policy = self.policy.clone();
834 serve_http_fn(port, closure, program, policy, opts)
835 }
836 ("net", "serve_routed_with") => {
837 let port = match args.first() {
839 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
840 _ => return Err("net.serve_routed_with(port, routes, fallback, opts): port must be Int 0..=65535".into()),
841 };
842 let routes_val = args.get(1).cloned()
843 .ok_or_else(|| "net.serve_routed_with(port, routes, fallback, opts): missing routes".to_string())?;
844 let opts = decode_serve_opts(args.get(3)
845 .ok_or_else(|| "net.serve_routed_with(port, routes, fallback, opts): missing opts".to_string())?)?;
846 let fallback = match args.into_iter().nth(2) {
847 Some(c @ Value::Closure { .. }) => c,
848 _ => return Err("net.serve_routed_with(port, routes, fallback, opts): fallback must be a closure".into()),
849 };
850 let routes = decode_routes_arg(routes_val)?;
851 let program = self.program.clone()
852 .ok_or_else(|| "net.serve_routed_with requires a Program reference; use DefaultHandler::with_program".to_string())?;
853 let policy = self.policy.clone();
854 serve_http_routed(port, routes, fallback, program, policy, opts)
855 }
856 ("net", "serve_quic") => self.dispatch_serve_quic_named(args),
857 ("net", "serve_quic_fn") => self.dispatch_serve_quic_fn(args),
858 ("net", "serve_quic_routed") => self.dispatch_serve_quic_routed(args),
859 ("net", "serve_tls") => {
860 let port = match args.first() {
861 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
862 _ => return Err("net.serve_tls(port, cert, key, handler): port must be Int 0..=65535".into()),
863 };
864 let cert_path = expect_str(args.get(1))?.to_string();
865 let key_path = expect_str(args.get(2))?.to_string();
866 let handler_name = expect_str(args.get(3))?.to_string();
867 let program = self.program.clone()
868 .ok_or_else(|| "net.serve_tls requires a Program reference".to_string())?;
869 let policy = self.policy.clone();
870 let cert = std::fs::read(&cert_path)
871 .map_err(|e| format!("net.serve_tls: read cert {cert_path}: {e}"))?;
872 let key = std::fs::read(&key_path)
873 .map_err(|e| format!("net.serve_tls: read key {key_path}: {e}"))?;
874 serve_http(port, handler_name, program, policy, Some(TlsConfig { cert, key }), ServeOpts::from_env())
875 }
876 ("net", "serve_ws") => {
877 let port = match args.first() {
878 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
879 _ => return Err("net.serve_ws(port, on_message): port must be Int 0..=65535".into()),
880 };
881 let handler_name = expect_str(args.get(1))?.to_string();
882 let program = self.program.clone()
883 .ok_or_else(|| "net.serve_ws requires a Program reference".to_string())?;
884 let policy = self.policy.clone();
885 let registry = Arc::new(crate::ws::ChatRegistry::default());
886 crate::ws::serve_ws(port, handler_name, program, policy, registry)
887 }
888 ("net", "serve_ws_fn") => {
889 let port = match args.first() {
890 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
891 _ => return Err("net.serve_ws_fn(port, subprotocol, handler): port must be Int 0..=65535".into()),
892 };
893 let subprotocol = expect_str(args.get(1))?.to_string();
894 let closure = match args.into_iter().nth(2) {
895 Some(c @ Value::Closure { .. }) => c,
896 _ => return Err("net.serve_ws_fn(port, subprotocol, handler): handler must be a closure".into()),
897 };
898 let program = self.program.clone()
899 .ok_or_else(|| "net.serve_ws_fn requires a Program reference; use DefaultHandler::with_program".to_string())?;
900 let policy = self.policy.clone();
901 let registry = Arc::new(crate::ws::ChatRegistry::default());
902 crate::ws::serve_ws_fn(port, subprotocol, closure, program, policy, registry)
903 }
904 ("net", "serve_ws_fn_auth") => {
905 let port = match args.first() {
907 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
908 _ => return Err("net.serve_ws_fn_auth(port, subprotocol, auth, on_message): port must be Int 0..=65535".into()),
909 };
910 let subprotocol = expect_str(args.get(1))?.to_string();
911 let mut it = args.into_iter().skip(2);
912 let auth_closure = match it.next() {
913 Some(c @ Value::Closure { .. }) => c,
914 _ => return Err("net.serve_ws_fn_auth(port, subprotocol, auth, on_message): auth must be a closure".into()),
915 };
916 let handler_closure = match it.next() {
917 Some(c @ Value::Closure { .. }) => c,
918 _ => return Err("net.serve_ws_fn_auth(port, subprotocol, auth, on_message): on_message must be a closure".into()),
919 };
920 let program = self.program.clone()
921 .ok_or_else(|| "net.serve_ws_fn_auth requires a Program reference; use DefaultHandler::with_program".to_string())?;
922 let policy = self.policy.clone();
923 let registry = Arc::new(crate::ws::ChatRegistry::default());
924 crate::ws::serve_ws_fn_auth(
925 port, subprotocol, auth_closure, handler_closure,
926 program, policy, registry,
927 )
928 }
929 ("net", "serve_ws_fn_actor_with") => {
930 let port = match args.first() {
942 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
943 _ => return Err("net.serve_ws_fn_actor_with(port, subprotocol, name_of, on_message, opts): port must be Int 0..=65535".into()),
944 };
945 let subprotocol = expect_str(args.get(1))?.to_string();
946 let opts = match args.get(4) {
950 Some(v) => crate::handler::http_serve::decode_serve_opts(v)
951 .map_err(|e| format!("net.serve_ws_fn_actor_with: {e} — use net.default_opts()"))?,
952 None => return Err("net.serve_ws_fn_actor_with(port, subprotocol, name_of, on_message, opts): opts is required — use net.default_opts()".into()),
953 };
954 let host = if opts.host.trim().is_empty() {
958 crate::ws::ws_bind_host()
959 } else {
960 opts.host.clone()
961 };
962 let mut it = args.into_iter().skip(2);
963 let name_of_closure = match it.next() {
964 Some(c @ Value::Closure { .. }) => c,
965 _ => return Err("net.serve_ws_fn_actor_with(port, subprotocol, name_of, on_message, opts): name_of must be a closure".into()),
966 };
967 let on_message_closure = match it.next() {
968 Some(c @ Value::Closure { .. }) => c,
969 _ => return Err("net.serve_ws_fn_actor_with(port, subprotocol, name_of, on_message, opts): on_message must be a closure".into()),
970 };
971 let program = self.program.clone()
972 .ok_or_else(|| "net.serve_ws_fn_actor_with requires a Program reference; use DefaultHandler::with_program".to_string())?;
973 let policy = self.policy.clone();
974 let registry = Arc::new(crate::ws::ChatRegistry::default());
975 crate::ws::serve_ws_fn_actor_on(
976 host, port, subprotocol, name_of_closure, on_message_closure,
977 program, policy, registry,
978 )
979 }
980 ("net", "serve_ws_fn_actor") => {
981 let port = match args.first() {
983 Some(Value::Int(n)) if (0..=65535).contains(n) => *n as u16,
984 _ => return Err("net.serve_ws_fn_actor(port, subprotocol, name_of, on_message): port must be Int 0..=65535".into()),
985 };
986 let subprotocol = expect_str(args.get(1))?.to_string();
987 let mut it = args.into_iter().skip(2);
988 let name_of_closure = match it.next() {
989 Some(c @ Value::Closure { .. }) => c,
990 _ => return Err("net.serve_ws_fn_actor(port, subprotocol, name_of, on_message): name_of must be a closure".into()),
991 };
992 let on_message_closure = match it.next() {
993 Some(c @ Value::Closure { .. }) => c,
994 _ => return Err("net.serve_ws_fn_actor(port, subprotocol, name_of, on_message): on_message must be a closure".into()),
995 };
996 let program = self.program.clone()
997 .ok_or_else(|| "net.serve_ws_fn_actor requires a Program reference; use DefaultHandler::with_program".to_string())?;
998 let policy = self.policy.clone();
999 let registry = Arc::new(crate::ws::ChatRegistry::default());
1000 crate::ws::serve_ws_fn_actor(
1001 port, subprotocol, name_of_closure, on_message_closure,
1002 program, policy, registry,
1003 )
1004 }
1005 ("net", "dial_ws") => {
1006 let url = expect_str(args.first())?.to_string();
1008 let subprotocol = expect_str(args.get(1))?.to_string();
1009 let on_open = match args.get(2).cloned() {
1010 Some(c @ Value::Closure { .. }) => c,
1011 _ => return Err(
1012 "net.dial_ws(url, subprotocol, on_open, on_message): on_open must be a closure".into(),
1013 ),
1014 };
1015 let on_message = match args.into_iter().nth(3) {
1016 Some(c @ Value::Closure { .. }) => c,
1017 _ => return Err(
1018 "net.dial_ws(url, subprotocol, on_open, on_message): on_message must be a closure".into(),
1019 ),
1020 };
1021 let program = self.program.clone().ok_or_else(|| {
1022 "net.dial_ws requires a Program reference; use DefaultHandler::with_program".to_string()
1023 })?;
1024 let policy = self.policy.clone();
1025 crate::ws::dial_ws(url, subprotocol, on_open, on_message, program, policy)
1026 }
1027 ("net", "dial_ws_actor") => {
1028 let url = expect_str(args.first())?.to_string();
1030 let subprotocol = expect_str(args.get(1))?.to_string();
1031 let name = expect_str(args.get(2))?.to_string();
1032 let on_open = match args.get(3).cloned() {
1033 Some(c @ Value::Closure { .. }) => c,
1034 _ => return Err(
1035 "net.dial_ws_actor(url, subprotocol, name, on_open, on_message): on_open must be a closure".into(),
1036 ),
1037 };
1038 let on_message = match args.into_iter().nth(4) {
1039 Some(c @ Value::Closure { .. }) => c,
1040 _ => return Err(
1041 "net.dial_ws_actor(url, subprotocol, name, on_open, on_message): on_message must be a closure".into(),
1042 ),
1043 };
1044 let program = self.program.clone().ok_or_else(|| {
1045 "net.dial_ws_actor requires a Program reference; use DefaultHandler::with_program".to_string()
1046 })?;
1047 let policy = self.policy.clone();
1048 crate::ws::dial_ws_actor(url, subprotocol, name, on_open, on_message, program, policy)
1049 }
1050 ("chat", "broadcast") => {
1051 let registry = self.chat_registry.as_ref()
1052 .ok_or_else(|| "chat.broadcast called outside a net.serve_ws handler".to_string())?;
1053 let room = expect_str(args.first())?;
1054 let body = expect_str(args.get(1))?;
1055 crate::ws::chat_broadcast(registry, room, body);
1056 Ok(Value::Unit)
1057 }
1058 ("chat", "send") => {
1059 let registry = self.chat_registry.as_ref()
1060 .ok_or_else(|| "chat.send called outside a net.serve_ws handler".to_string())?;
1061 let conn_id = match args.first() {
1062 Some(Value::Int(n)) if *n >= 0 => *n as u64,
1063 _ => return Err("chat.send: conn_id must be non-negative Int".into()),
1064 };
1065 let body = expect_str(args.get(1))?;
1066 Ok(Value::Bool(crate::ws::chat_send(registry, conn_id, body)))
1067 }
1068 ("kv", "open") => {
1069 let path = expect_str(args.first())?.to_string();
1070 if !self.policy.allow_fs_write.is_empty() {
1074 let p = std::path::Path::new(&path);
1075 if !self.policy.allow_fs_write.iter().any(|a| p.starts_with(a)) {
1076 return Ok(err(Value::Str(format!(
1077 "kv.open: `{path}` outside --allow-fs-write").into())));
1078 }
1079 }
1080 match sled::open(&path) {
1081 Ok(db) => {
1082 let handle = next_kv_handle();
1083 kv_registry().lock().unwrap().insert(handle, db);
1084 Ok(ok(Value::Int(handle as i64)))
1085 }
1086 Err(e) => Ok(err(Value::Str(format!("kv.open: {e}").into()))),
1087 }
1088 }
1089 ("kv", "close") => {
1090 let h = expect_kv_handle(args.first())?;
1091 kv_registry().lock().unwrap().remove(h);
1092 Ok(Value::Unit)
1093 }
1094 ("kv", "get") => {
1095 let h = expect_kv_handle(args.first())?;
1096 let key = expect_str(args.get(1))?;
1097 let mut reg = kv_registry().lock().unwrap();
1098 let db = reg.touch_get(h).ok_or_else(|| "kv.get: closed or unknown Kv handle".to_string())?;
1099 match db.get(key.as_bytes()) {
1100 Ok(Some(ivec)) => Ok(some(Value::Bytes(ivec.to_vec()))),
1101 Ok(None) => Ok(none()),
1102 Err(e) => Err(format!("kv.get: {e}")),
1103 }
1104 }
1105 ("kv", "put") => {
1106 let h = expect_kv_handle(args.first())?;
1107 let key = expect_str(args.get(1))?.to_string();
1108 let val = expect_bytes(args.get(2))?.clone();
1109 let mut reg = kv_registry().lock().unwrap();
1110 let db = reg.touch_get(h).ok_or_else(|| "kv.put: closed or unknown Kv handle".to_string())?;
1111 match db.insert(key.as_bytes(), val) {
1112 Ok(_) => Ok(ok(Value::Unit)),
1113 Err(e) => Ok(err(Value::Str(format!("kv.put: {e}").into()))),
1114 }
1115 }
1116 ("kv", "delete") => {
1117 let h = expect_kv_handle(args.first())?;
1118 let key = expect_str(args.get(1))?;
1119 let mut reg = kv_registry().lock().unwrap();
1120 let db = reg.touch_get(h).ok_or_else(|| "kv.delete: closed or unknown Kv handle".to_string())?;
1121 match db.remove(key.as_bytes()) {
1122 Ok(_) => Ok(ok(Value::Unit)),
1123 Err(e) => Ok(err(Value::Str(format!("kv.delete: {e}").into()))),
1124 }
1125 }
1126 ("kv", "contains") => {
1127 let h = expect_kv_handle(args.first())?;
1128 let key = expect_str(args.get(1))?;
1129 let mut reg = kv_registry().lock().unwrap();
1130 let db = reg.touch_get(h).ok_or_else(|| "kv.contains: closed or unknown Kv handle".to_string())?;
1131 match db.contains_key(key.as_bytes()) {
1132 Ok(present) => Ok(Value::Bool(present)),
1133 Err(e) => Err(format!("kv.contains: {e}")),
1134 }
1135 }
1136 ("kv", "list_prefix") => {
1137 let h = expect_kv_handle(args.first())?;
1138 let prefix = expect_str(args.get(1))?;
1139 let mut reg = kv_registry().lock().unwrap();
1140 let db = reg.touch_get(h).ok_or_else(|| "kv.list_prefix: closed or unknown Kv handle".to_string())?;
1141 let mut keys: Vec<Value> = Vec::new();
1142 for kv in db.scan_prefix(prefix.as_bytes()) {
1143 let (k, _) = kv.map_err(|e| format!("kv.list_prefix: {e}"))?;
1144 let s = String::from_utf8_lossy(&k).to_string();
1145 keys.push(Value::Str(s.into()));
1146 }
1147 Ok(Value::List(keys.into()))
1148 }
1149 ("vcs", "put_blob") => {
1157 let content = expect_str(args.first())?.to_string();
1158 match lex_store::Store::open(vcs_store_root())
1159 .and_then(|s| s.put_blob(&content)) {
1160 Ok(sha) => Ok(ok(Value::Str(sha.into()))),
1161 Err(e) => Ok(err(Value::Str(format!("vcs.put_blob: {e}").into()))),
1162 }
1163 }
1164 ("vcs", "get_blob") => {
1165 let sha = expect_str(args.first())?.to_string();
1166 match lex_store::Store::open(vcs_store_root())
1167 .and_then(|s| s.get_blob(&sha)) {
1168 Ok(content) => Ok(ok(Value::Str(content.into()))),
1169 Err(e) => Ok(err(Value::Str(format!("vcs.get_blob: {e}").into()))),
1170 }
1171 }
1172 ("vcs", "has_blob") => {
1173 let sha = expect_str(args.first())?.to_string();
1174 let has = lex_store::Store::open(vcs_store_root())
1175 .map(|s| s.has_blob(&sha)).unwrap_or(false);
1176 Ok(Value::Bool(has))
1177 }
1178 ("vcs", "ref_set") => {
1179 let ns = expect_str(args.first())?.to_string();
1180 let key = expect_str(args.get(1))?.to_string();
1181 let sha = expect_str(args.get(2))?.to_string();
1182 match lex_store::Store::open(vcs_store_root())
1183 .and_then(|s| s.set_blob_ref(&ns, &key, &sha)) {
1184 Ok(()) => Ok(ok(Value::Unit)),
1185 Err(e) => Ok(err(Value::Str(format!("vcs.ref_set: {e}").into()))),
1186 }
1187 }
1188 ("vcs", "ref_get") => {
1189 let ns = expect_str(args.first())?.to_string();
1190 let key = expect_str(args.get(1))?.to_string();
1191 match lex_store::Store::open(vcs_store_root())
1192 .and_then(|s| s.get_blob_ref(&ns, &key)) {
1193 Ok(sha) => Ok(ok(Value::Str(sha.into()))),
1194 Err(e) => Ok(err(Value::Str(format!("vcs.ref_get: {e}").into()))),
1195 }
1196 }
1197 ("sql", "open") => {
1198 let path = expect_str(args.first())?.to_string();
1199 if path.starts_with("postgres://") || path.starts_with("postgresql://") {
1200 match postgres::Client::connect(&path, postgres::NoTls) {
1202 Ok(client) => {
1203 let handle = next_sql_handle();
1204 sql_registry().lock().unwrap().insert(handle, SqlConn::Postgres(client));
1205 Ok(ok(Value::Int(handle as i64)))
1206 }
1207 Err(e) => Ok(err(pg_err_to_sql_error(e, "sql.open"))),
1208 }
1209 } else {
1210 if path != ":memory:" && !self.policy.allow_fs_write.is_empty() {
1213 let p = std::path::Path::new(&path);
1214 if !self.policy.allow_fs_write.iter().any(|a| p.starts_with(a)) {
1215 return Ok(err(sql_error(
1216 format!("sql.open: `{path}` outside --allow-fs-write"),
1217 None, None,
1218 )));
1219 }
1220 }
1221 match rusqlite::Connection::open(&path) {
1222 Ok(conn) => {
1223 let handle = next_sql_handle();
1224 sql_registry().lock().unwrap().insert(handle, SqlConn::Sqlite(conn));
1225 Ok(ok(Value::Int(handle as i64)))
1226 }
1227 Err(e) => Ok(err(sqlite_err_to_sql_error(e, "sql.open"))),
1228 }
1229 }
1230 }
1231 ("sql", "close") => {
1232 let h = expect_sql_handle(args.first())?;
1233 sql_registry().lock().unwrap().remove(h);
1234 Ok(Value::Unit)
1235 }
1236 ("sql", "exec") => {
1237 let h = expect_sql_handle(args.first())?;
1238 let stmt = expect_str(args.get(1))?.to_string();
1239 let params = expect_sql_params(args.get(2))?;
1240 let arc = sql_registry().lock().unwrap()
1241 .touch_get(h)
1242 .ok_or_else(|| "sql.exec: closed or unknown Db handle".to_string())?;
1243 let mut conn = arc.lock().unwrap();
1244 match &mut *conn {
1245 SqlConn::Sqlite(c) => {
1246 let bound = sqlite_params(¶ms);
1247 let bind: Vec<&dyn rusqlite::ToSql> =
1248 bound.iter().map(|p| p as &dyn rusqlite::ToSql).collect();
1249 match c.execute(&stmt, rusqlite::params_from_iter(bind.iter())) {
1250 Ok(n) => Ok(ok(Value::Int(n as i64))),
1251 Err(e) => Ok(err(sqlite_err_to_sql_error(e, "sql.exec"))),
1252 }
1253 }
1254 SqlConn::Postgres(c) => {
1255 let pg = pg_param_refs(¶ms);
1256 let refs: Vec<&(dyn postgres::types::ToSql + Sync)> =
1257 pg.iter().map(|b| b.as_ref()).collect();
1258 match c.execute(pg_rewrite_placeholders(stmt.as_str()).as_str(), &refs) {
1259 Ok(n) => Ok(ok(Value::Int(n as i64))),
1260 Err(e) => Ok(err(pg_err_to_sql_error(e, "sql.exec"))),
1261 }
1262 }
1263 }
1264 }
1265 ("sql", "query") => {
1266 let h = expect_sql_handle(args.first())?;
1267 let stmt_str = expect_str(args.get(1))?.to_string();
1268 let params = expect_sql_params(args.get(2))?;
1269 let arc = sql_registry().lock().unwrap()
1270 .touch_get(h)
1271 .ok_or_else(|| "sql.query: closed or unknown Db handle".to_string())?;
1272 let mut conn = arc.lock().unwrap();
1273 Ok(match &mut *conn {
1274 SqlConn::Sqlite(c) => sql_run_query_sqlite(c, &stmt_str, ¶ms),
1275 SqlConn::Postgres(c) => sql_run_query_pg(c, &stmt_str, ¶ms),
1276 })
1277 }
1278 ("sql", "query_iter") => {
1284 let h = expect_sql_handle(args.first())?;
1285 let stmt_str = expect_str(args.get(1))?.to_string();
1286 let params = expect_sql_params(args.get(2))?;
1287 let arc = sql_registry().lock().unwrap()
1288 .touch_get(h)
1289 .ok_or_else(|| "sql.query_iter: closed or unknown Db handle".to_string())?;
1290
1291 let (sender, receiver) = std::sync::mpsc::sync_channel::<Result<Value, String>>(
1295 CURSOR_CHANNEL_CAPACITY,
1296 );
1297 let cursor_h = next_cursor_handle();
1298 cursor_registry().lock().unwrap().insert(cursor_h, receiver);
1299
1300 let arc_for_thread = Arc::clone(&arc);
1301 let is_sqlite = matches!(*arc.lock().unwrap(), SqlConn::Sqlite(_));
1307 std::thread::spawn(move || {
1308 if is_sqlite {
1309 sqlite_cursor_producer(arc_for_thread, stmt_str, params, sender);
1310 } else {
1311 pg_cursor_producer(arc_for_thread, stmt_str, params, sender);
1312 }
1313 });
1314
1315 Ok(ok(Value::Variant {
1316 name: "__IterCursor".into(),
1317 args: vec![Value::Int(cursor_h as i64)],
1318 }))
1319 }
1320 ("sql", "cursor_next") => {
1326 let h = match args.first() {
1327 Some(Value::Int(n)) if *n >= 0 => *n as u64,
1328 _ => return Err("sql.cursor_next: expected cursor handle (Int)".into()),
1329 };
1330 let rx_arc = match cursor_registry().lock().unwrap().touch_get(h) {
1331 Some(a) => a,
1332 None => return Ok(Value::Variant { name: "None".into(), args: vec![] }),
1333 };
1334 let recv_result = {
1339 let rx = match rx_arc.lock() {
1340 Ok(g) => g,
1341 Err(p) => p.into_inner(),
1342 };
1343 rx.recv()
1344 };
1345 match recv_result {
1346 Ok(Ok(row)) => Ok(Value::Variant {
1347 name: "Some".into(),
1348 args: vec![row],
1349 }),
1350 Ok(Err(_)) | Err(_) => {
1351 cursor_registry().lock().unwrap().remove(h);
1355 Ok(Value::Variant { name: "None".into(), args: vec![] })
1356 }
1357 }
1358 }
1359 ("sql", "begin") => {
1364 let h = expect_sql_handle(args.first())?;
1365 let arc = sql_registry().lock().unwrap()
1366 .touch_get(h)
1367 .ok_or_else(|| "sql.begin: closed or unknown Db handle".to_string())?;
1368 let mut conn = arc.lock().unwrap();
1369 match &mut *conn {
1370 SqlConn::Sqlite(c) => match c.execute_batch("BEGIN") {
1371 Ok(()) => Ok(ok(Value::Int(h as i64))),
1372 Err(e) => Ok(err(sqlite_err_to_sql_error(e, "sql.begin"))),
1373 },
1374 SqlConn::Postgres(c) => match c.batch_execute("BEGIN") {
1375 Ok(()) => Ok(ok(Value::Int(h as i64))),
1376 Err(e) => Ok(err(pg_err_to_sql_error(e, "sql.begin"))),
1377 },
1378 }
1379 }
1380 ("sql", "commit") => {
1381 let h = expect_sql_handle(args.first())?;
1382 let arc = sql_registry().lock().unwrap()
1383 .touch_get(h)
1384 .ok_or_else(|| "sql.commit: closed or unknown SqlTx handle".to_string())?;
1385 let mut conn = arc.lock().unwrap();
1386 match &mut *conn {
1387 SqlConn::Sqlite(c) => match c.execute_batch("COMMIT") {
1388 Ok(()) => Ok(ok(Value::Unit)),
1389 Err(e) => Ok(err(sqlite_err_to_sql_error(e, "sql.commit"))),
1390 },
1391 SqlConn::Postgres(c) => match c.batch_execute("COMMIT") {
1392 Ok(()) => Ok(ok(Value::Unit)),
1393 Err(e) => Ok(err(pg_err_to_sql_error(e, "sql.commit"))),
1394 },
1395 }
1396 }
1397 ("sql", "rollback") => {
1398 let h = expect_sql_handle(args.first())?;
1399 let arc = sql_registry().lock().unwrap()
1400 .touch_get(h)
1401 .ok_or_else(|| "sql.rollback: closed or unknown SqlTx handle".to_string())?;
1402 let mut conn = arc.lock().unwrap();
1403 match &mut *conn {
1404 SqlConn::Sqlite(c) => match c.execute_batch("ROLLBACK") {
1405 Ok(()) => Ok(ok(Value::Unit)),
1406 Err(e) => Ok(err(sqlite_err_to_sql_error(e, "sql.rollback"))),
1407 },
1408 SqlConn::Postgres(c) => match c.batch_execute("ROLLBACK") {
1409 Ok(()) => Ok(ok(Value::Unit)),
1410 Err(e) => Ok(err(pg_err_to_sql_error(e, "sql.rollback"))),
1411 },
1412 }
1413 }
1414 ("sql", "exec_tx") => {
1415 let h = expect_sql_handle(args.first())?;
1416 let stmt = expect_str(args.get(1))?.to_string();
1417 let params = expect_sql_params(args.get(2))?;
1418 let arc = sql_registry().lock().unwrap()
1419 .touch_get(h)
1420 .ok_or_else(|| "sql.exec_tx: closed or unknown SqlTx handle".to_string())?;
1421 let mut conn = arc.lock().unwrap();
1422 match &mut *conn {
1423 SqlConn::Sqlite(c) => {
1424 let bound = sqlite_params(¶ms);
1425 let bind: Vec<&dyn rusqlite::ToSql> =
1426 bound.iter().map(|p| p as &dyn rusqlite::ToSql).collect();
1427 match c.execute(&stmt, rusqlite::params_from_iter(bind.iter())) {
1428 Ok(n) => Ok(ok(Value::Int(n as i64))),
1429 Err(e) => Ok(err(sqlite_err_to_sql_error(e, "sql.exec_tx"))),
1430 }
1431 }
1432 SqlConn::Postgres(c) => {
1433 let pg = pg_param_refs(¶ms);
1434 let refs: Vec<&(dyn postgres::types::ToSql + Sync)> =
1435 pg.iter().map(|b| b.as_ref()).collect();
1436 match c.execute(pg_rewrite_placeholders(stmt.as_str()).as_str(), &refs) {
1437 Ok(n) => Ok(ok(Value::Int(n as i64))),
1438 Err(e) => Ok(err(pg_err_to_sql_error(e, "sql.exec_tx"))),
1439 }
1440 }
1441 }
1442 }
1443 ("sql", "query_tx") => {
1444 let h = expect_sql_handle(args.first())?;
1445 let stmt_str = expect_str(args.get(1))?.to_string();
1446 let params = expect_sql_params(args.get(2))?;
1447 let arc = sql_registry().lock().unwrap()
1448 .touch_get(h)
1449 .ok_or_else(|| "sql.query_tx: closed or unknown SqlTx handle".to_string())?;
1450 let mut conn = arc.lock().unwrap();
1451 Ok(match &mut *conn {
1452 SqlConn::Sqlite(c) => sql_run_query_sqlite(c, &stmt_str, ¶ms),
1453 SqlConn::Postgres(c) => sql_run_query_pg(c, &stmt_str, ¶ms),
1454 })
1455 }
1456 ("sql", "get_str") => Ok(sql_get_col(&args, |v| match v {
1457 Value::Str(s) => Some(Value::Str(s.clone())),
1458 Value::Int(n) => Some(Value::Str(n.to_string().into())),
1459 _ => None,
1460 })?),
1461 ("sql", "get_int") => Ok(sql_get_col(&args, |v| match v {
1462 Value::Int(n) => Some(Value::Int(*n)),
1463 Value::Float(f) => Some(Value::Int(*f as i64)),
1464 _ => None,
1465 })?),
1466 ("sql", "get_float") => Ok(sql_get_col(&args, |v| match v {
1467 Value::Float(f) => Some(Value::Float(*f)),
1468 Value::Int(n) => Some(Value::Float(*n as f64)),
1469 _ => None,
1470 })?),
1471 ("sql", "get_bool") => Ok(sql_get_col(&args, |v| match v {
1472 Value::Bool(b) => Some(Value::Bool(*b)),
1473 Value::Int(n) => Some(Value::Bool(*n != 0)),
1474 _ => None,
1475 })?),
1476
1477 ("redis", "connect") => {
1486 let url = expect_str(args.first())?.to_string();
1487 self.ensure_host_allowed(&url)?;
1488 match redis::Client::open(url.as_str()) {
1489 Ok(client) => match client.get_connection() {
1490 Ok(conn) => {
1491 let handle = next_redis_handle();
1492 redis_registry().lock().unwrap().insert(handle, RedisEntry { url, conn });
1493 Ok(ok(Value::Int(handle as i64)))
1494 }
1495 Err(e) => Ok(err(Value::Str(format!("redis.connect: {e}").into()))),
1496 },
1497 Err(e) => Ok(err(Value::Str(format!("redis.connect: {e}").into()))),
1498 }
1499 }
1500 ("redis", "close") => {
1501 let h = expect_redis_handle(args.first())?;
1502 redis_registry().lock().unwrap().remove(h);
1503 Ok(Value::Unit)
1504 }
1505 ("redis", "get") => {
1506 let h = expect_redis_handle(args.first())?;
1507 let key = expect_str(args.get(1))?.to_string();
1508 let mut reg = redis_registry().lock().unwrap();
1509 let entry = reg.touch_get_mut(h)
1510 .ok_or_else(|| "redis.get: closed or unknown ConnRedis handle".to_string())?;
1511 use redis::Commands;
1512 match entry.conn.get::<_, Option<String>>(&key) {
1513 Ok(Some(v)) => Ok(some(Value::Str(v.into()))),
1514 Ok(None) => Ok(none()),
1515 Err(e) => Err(format!("redis.get: {e}")),
1516 }
1517 }
1518 ("redis", "set") => {
1519 let h = expect_redis_handle(args.first())?;
1520 let key = expect_str(args.get(1))?.to_string();
1521 let val = expect_str(args.get(2))?.to_string();
1522 let mut reg = redis_registry().lock().unwrap();
1523 let entry = reg.touch_get_mut(h)
1524 .ok_or_else(|| "redis.set: closed or unknown ConnRedis handle".to_string())?;
1525 use redis::Commands;
1526 entry.conn.set::<_, _, ()>(&key, &val)
1527 .map_err(|e| format!("redis.set: {e}"))?;
1528 Ok(Value::Unit)
1529 }
1530 ("redis", "set_ex") => {
1531 let h = expect_redis_handle(args.first())?;
1532 let key = expect_str(args.get(1))?.to_string();
1533 let val = expect_str(args.get(2))?.to_string();
1534 let ttl = expect_int(args.get(3))?;
1535 let mut reg = redis_registry().lock().unwrap();
1536 let entry = reg.touch_get_mut(h)
1537 .ok_or_else(|| "redis.set_ex: closed or unknown ConnRedis handle".to_string())?;
1538 use redis::Commands;
1539 entry.conn.set_ex::<_, _, ()>(&key, &val, ttl as u64)
1540 .map_err(|e| format!("redis.set_ex: {e}"))?;
1541 Ok(Value::Unit)
1542 }
1543 ("redis", "del") => {
1544 let h = expect_redis_handle(args.first())?;
1545 let key = expect_str(args.get(1))?.to_string();
1546 let mut reg = redis_registry().lock().unwrap();
1547 let entry = reg.touch_get_mut(h)
1548 .ok_or_else(|| "redis.del: closed or unknown ConnRedis handle".to_string())?;
1549 use redis::Commands;
1550 entry.conn.del::<_, ()>(&key)
1551 .map_err(|e| format!("redis.del: {e}"))?;
1552 Ok(Value::Unit)
1553 }
1554 ("redis", "exists") => {
1555 let h = expect_redis_handle(args.first())?;
1556 let key = expect_str(args.get(1))?.to_string();
1557 let mut reg = redis_registry().lock().unwrap();
1558 let entry = reg.touch_get_mut(h)
1559 .ok_or_else(|| "redis.exists: closed or unknown ConnRedis handle".to_string())?;
1560 use redis::Commands;
1561 let present: bool = entry.conn.exists(&key)
1562 .map_err(|e| format!("redis.exists: {e}"))?;
1563 Ok(Value::Bool(present))
1564 }
1565 ("redis", "expire") => {
1566 let h = expect_redis_handle(args.first())?;
1567 let key = expect_str(args.get(1))?.to_string();
1568 let ttl = expect_int(args.get(2))?;
1569 let mut reg = redis_registry().lock().unwrap();
1570 let entry = reg.touch_get_mut(h)
1571 .ok_or_else(|| "redis.expire: closed or unknown ConnRedis handle".to_string())?;
1572 use redis::Commands;
1573 entry.conn.expire::<_, ()>(&key, ttl)
1574 .map_err(|e| format!("redis.expire: {e}"))?;
1575 Ok(Value::Unit)
1576 }
1577 ("redis", "publish") => {
1578 let h = expect_redis_handle(args.first())?;
1579 let channel = expect_str(args.get(1))?.to_string();
1580 let msg = expect_str(args.get(2))?.to_string();
1581 let mut reg = redis_registry().lock().unwrap();
1582 let entry = reg.touch_get_mut(h)
1583 .ok_or_else(|| "redis.publish: closed or unknown ConnRedis handle".to_string())?;
1584 use redis::Commands;
1585 let n: i64 = entry.conn.publish(&channel, &msg)
1586 .map_err(|e| format!("redis.publish: {e}"))?;
1587 Ok(Value::Int(n))
1588 }
1589 ("redis", "subscribe") => {
1594 let h = expect_redis_handle(args.first())?;
1595 let channel = expect_str(args.get(1))?.to_string();
1596 let closure = match args.into_iter().nth(2) {
1597 Some(c @ Value::Closure { .. }) => c,
1598 _ => return Err("redis.subscribe: handler must be a Closure".into()),
1599 };
1600 let program = self.program.clone()
1601 .ok_or("redis.subscribe: no program; call DefaultHandler::with_program")?;
1602 let policy = self.policy.clone();
1603 let url = redis_registry().lock().unwrap()
1604 .get_url(h)
1605 .ok_or("redis.subscribe: closed or unknown ConnRedis handle")?;
1606 let client = redis::Client::open(url.as_str())
1607 .map_err(|e| format!("redis.subscribe: {e}"))?;
1608 let mut conn = client.get_connection()
1609 .map_err(|e| format!("redis.subscribe: {e}"))?;
1610 let mut pubsub = conn.as_pubsub();
1611 pubsub.subscribe(&channel)
1612 .map_err(|e| format!("redis.subscribe: {e}"))?;
1613 loop {
1614 let msg = pubsub.get_message()
1615 .map_err(|e| format!("redis.subscribe: {e}"))?;
1616 let ch: String = msg.get_channel_name().to_string();
1617 let payload: String = msg.get_payload()
1618 .map_err(|e| format!("redis.subscribe: payload: {e}"))?;
1619 let handler = DefaultHandler::new(policy.clone())
1620 .with_program(Arc::clone(&program));
1621 let mut vm = Vm::with_handler(&program, Box::new(handler));
1622 vm.invoke_closure_value(closure.clone(), vec![
1623 Value::Str(ch.into()),
1624 Value::Str(payload.into()),
1625 ]).map_err(|e| format!("redis.subscribe: handler: {e:?}"))?;
1626 }
1627 }
1628 ("redis", "psubscribe") => {
1629 let h = expect_redis_handle(args.first())?;
1630 let pattern = expect_str(args.get(1))?.to_string();
1631 let closure = match args.into_iter().nth(2) {
1632 Some(c @ Value::Closure { .. }) => c,
1633 _ => return Err("redis.psubscribe: handler must be a Closure".into()),
1634 };
1635 let program = self.program.clone()
1636 .ok_or("redis.psubscribe: no program; call DefaultHandler::with_program")?;
1637 let policy = self.policy.clone();
1638 let url = redis_registry().lock().unwrap()
1639 .get_url(h)
1640 .ok_or("redis.psubscribe: closed or unknown ConnRedis handle")?;
1641 let client = redis::Client::open(url.as_str())
1642 .map_err(|e| format!("redis.psubscribe: {e}"))?;
1643 let mut conn = client.get_connection()
1644 .map_err(|e| format!("redis.psubscribe: {e}"))?;
1645 let mut pubsub = conn.as_pubsub();
1646 pubsub.psubscribe(&pattern)
1647 .map_err(|e| format!("redis.psubscribe: {e}"))?;
1648 loop {
1649 let msg = pubsub.get_message()
1650 .map_err(|e| format!("redis.psubscribe: {e}"))?;
1651 let pat: String = msg.get_pattern()
1652 .ok()
1653 .and_then(|v: Option<String>| v)
1654 .unwrap_or_else(|| pattern.clone());
1655 let ch: String = msg.get_channel_name().to_string();
1656 let payload: String = msg.get_payload()
1657 .map_err(|e| format!("redis.psubscribe: payload: {e}"))?;
1658 let handler = DefaultHandler::new(policy.clone())
1659 .with_program(Arc::clone(&program));
1660 let mut vm = Vm::with_handler(&program, Box::new(handler));
1661 vm.invoke_closure_value(closure.clone(), vec![
1662 Value::Str(pat.into()),
1663 Value::Str(ch.into()),
1664 Value::Str(payload.into()),
1665 ]).map_err(|e| format!("redis.psubscribe: handler: {e:?}"))?;
1666 }
1667 }
1668 ("redis", "lpush") => {
1669 let h = expect_redis_handle(args.first())?;
1670 let key = expect_str(args.get(1))?.to_string();
1671 let val = expect_str(args.get(2))?.to_string();
1672 let mut reg = redis_registry().lock().unwrap();
1673 let entry = reg.touch_get_mut(h)
1674 .ok_or_else(|| "redis.lpush: closed or unknown ConnRedis handle".to_string())?;
1675 use redis::Commands;
1676 let n: i64 = entry.conn.lpush(&key, &val)
1677 .map_err(|e| format!("redis.lpush: {e}"))?;
1678 Ok(Value::Int(n))
1679 }
1680 ("redis", "rpush") => {
1681 let h = expect_redis_handle(args.first())?;
1682 let key = expect_str(args.get(1))?.to_string();
1683 let val = expect_str(args.get(2))?.to_string();
1684 let mut reg = redis_registry().lock().unwrap();
1685 let entry = reg.touch_get_mut(h)
1686 .ok_or_else(|| "redis.rpush: closed or unknown ConnRedis handle".to_string())?;
1687 use redis::Commands;
1688 let n: i64 = entry.conn.rpush(&key, &val)
1689 .map_err(|e| format!("redis.rpush: {e}"))?;
1690 Ok(Value::Int(n))
1691 }
1692 ("redis", "brpop") => {
1693 let h = expect_redis_handle(args.first())?;
1696 let key = expect_str(args.get(1))?.to_string();
1697 let timeout = expect_int(args.get(2))?;
1698 let mut reg = redis_registry().lock().unwrap();
1699 let entry = reg.touch_get_mut(h)
1700 .ok_or_else(|| "redis.brpop: closed or unknown ConnRedis handle".to_string())?;
1701 use redis::Commands;
1702 let result: Option<(String, String)> = entry.conn
1705 .brpop(&key, timeout as f64)
1706 .map_err(|e| format!("redis.brpop: {e}"))?;
1707 match result {
1708 Some((_, v)) => Ok(some(Value::Str(v.into()))),
1709 None => Ok(none()),
1710 }
1711 }
1712 ("redis", "llen") => {
1713 let h = expect_redis_handle(args.first())?;
1714 let key = expect_str(args.get(1))?.to_string();
1715 let mut reg = redis_registry().lock().unwrap();
1716 let entry = reg.touch_get_mut(h)
1717 .ok_or_else(|| "redis.llen: closed or unknown ConnRedis handle".to_string())?;
1718 use redis::Commands;
1719 let n: i64 = entry.conn.llen(&key)
1720 .map_err(|e| format!("redis.llen: {e}"))?;
1721 Ok(Value::Int(n))
1722 }
1723 ("redis", "hset") => {
1724 let h = expect_redis_handle(args.first())?;
1725 let key = expect_str(args.get(1))?.to_string();
1726 let field = expect_str(args.get(2))?.to_string();
1727 let val = expect_str(args.get(3))?.to_string();
1728 let mut reg = redis_registry().lock().unwrap();
1729 let entry = reg.touch_get_mut(h)
1730 .ok_or_else(|| "redis.hset: closed or unknown ConnRedis handle".to_string())?;
1731 use redis::Commands;
1732 entry.conn.hset::<_, _, _, ()>(&key, &field, &val)
1733 .map_err(|e| format!("redis.hset: {e}"))?;
1734 Ok(Value::Unit)
1735 }
1736 ("redis", "hget") => {
1737 let h = expect_redis_handle(args.first())?;
1738 let key = expect_str(args.get(1))?.to_string();
1739 let field = expect_str(args.get(2))?.to_string();
1740 let mut reg = redis_registry().lock().unwrap();
1741 let entry = reg.touch_get_mut(h)
1742 .ok_or_else(|| "redis.hget: closed or unknown ConnRedis handle".to_string())?;
1743 use redis::Commands;
1744 match entry.conn.hget::<_, _, Option<String>>(&key, &field) {
1745 Ok(Some(v)) => Ok(some(Value::Str(v.into()))),
1746 Ok(None) => Ok(none()),
1747 Err(e) => Err(format!("redis.hget: {e}")),
1748 }
1749 }
1750 ("redis", "hdel") => {
1751 let h = expect_redis_handle(args.first())?;
1752 let key = expect_str(args.get(1))?.to_string();
1753 let field = expect_str(args.get(2))?.to_string();
1754 let mut reg = redis_registry().lock().unwrap();
1755 let entry = reg.touch_get_mut(h)
1756 .ok_or_else(|| "redis.hdel: closed or unknown ConnRedis handle".to_string())?;
1757 use redis::Commands;
1758 entry.conn.hdel::<_, _, ()>(&key, &field)
1759 .map_err(|e| format!("redis.hdel: {e}"))?;
1760 Ok(Value::Unit)
1761 }
1762 ("redis", "hgetall") => {
1763 let h = expect_redis_handle(args.first())?;
1764 let key = expect_str(args.get(1))?.to_string();
1765 let mut reg = redis_registry().lock().unwrap();
1766 let entry = reg.touch_get_mut(h)
1767 .ok_or_else(|| "redis.hgetall: closed or unknown ConnRedis handle".to_string())?;
1768 use redis::Commands;
1769 let map: std::collections::HashMap<String, String> = entry.conn
1770 .hgetall(&key)
1771 .map_err(|e| format!("redis.hgetall: {e}"))?;
1772 let pairs: Vec<Value> = map.into_iter()
1773 .map(|(k, v)| Value::Tuple(vec![Value::Str(k.into()), Value::Str(v.into())]))
1774 .collect();
1775 Ok(Value::List(pairs.into()))
1776 }
1777
1778 other => Err(format!("unsupported effect {}.{}", other.0, other.1)),
1782 }
1783 }
1784
1785 fn take_exit(&mut self) -> Option<i32> {
1808 match self.requested_exit.load(Ordering::SeqCst) {
1809 crate::handler::NO_EXIT => None,
1810 code => Some(code as i32),
1811 }
1812 }
1813
1814 fn spawn_for_worker(&self) -> Option<Box<dyn lex_bytecode::vm::EffectHandler + Send>> {
1815 let mut fresh = DefaultHandler::new(self.policy.clone());
1816 fresh.budget_remaining = std::sync::Arc::clone(&self.budget_remaining);
1819 fresh.budget_ceiling = self.budget_ceiling;
1820 fresh.read_root = self.read_root.clone();
1821 fresh.program = self.program.clone();
1822 fresh.chat_registry = self.chat_registry.clone();
1823 fresh.streams = std::sync::Arc::clone(&self.streams);
1828 fresh.next_stream_id = std::sync::Arc::clone(&self.next_stream_id);
1829 fresh.program_args = self.program_args.clone();
1830 fresh.requested_exit = std::sync::Arc::clone(&self.requested_exit);
1833 Some(Box::new(fresh))
1834 }
1835}