Skip to main content

hara_native/core/
native.rs

1fn os_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
2    let operation = operation.strip_prefix("os/").unwrap_or(operation);
3    let operation = operation
4        .strip_prefix("std.native.OS/")
5        .unwrap_or(operation);
6    let process_operation = operation.strip_prefix("std.native.Process/");
7    let operation = match process_operation.unwrap_or(operation) {
8        "alive?" if process_operation.is_some() => "process-alive?",
9        "write" if process_operation.is_some() => "process-write",
10        "close-input" if process_operation.is_some() => "process-close-input",
11        "stdout" if process_operation.is_some() => "process-stdout",
12        "stderr" if process_operation.is_some() => "process-stderr",
13        "stdout-stream" if process_operation.is_some() => "process-stdout-stream",
14        "stderr-stream" if process_operation.is_some() => "process-stderr-stream",
15        "wait" if process_operation.is_some() => "process-wait",
16        "kill" if process_operation.is_some() => "process-kill",
17        value => value,
18    };
19    match operation {
20        "time-ms" => {
21            if !values.is_empty() {
22                return Err("os/time-ms expects no arguments".into());
23            }
24            return Ok(Value::Number(crate::clock::time_ms()));
25        }
26        "time-ns" => {
27            if !values.is_empty() {
28                return Err("os/time-ns expects no arguments".into());
29            }
30            return Ok(Value::Number(crate::clock::time_ns()));
31        }
32        "platform" => {
33            if !values.is_empty() {
34                return Err("os/platform expects no arguments".into());
35            }
36            let platform = if cfg!(target_os = "linux") {
37                "linux"
38            } else if cfg!(target_os = "macos") {
39                "macos"
40            } else if cfg!(target_os = "windows") {
41                "windows"
42            } else {
43                "unknown"
44            };
45            return Ok(Value::Keyword(platform.into()));
46        }
47        "arch" => {
48            if !values.is_empty() {
49                return Err("os/arch expects no arguments".into());
50            }
51            let arch = match std::env::consts::ARCH {
52                "x86_64" => "x86-64",
53                value => value,
54            };
55            return Ok(Value::Keyword(arch.into()));
56        }
57        "cwd" => {
58            if !values.is_empty() {
59                return Err("os/cwd expects no arguments".into());
60            }
61            #[cfg(target_arch = "wasm32")]
62            return Ok(Value::String("/".into()));
63            #[cfg(not(target_arch = "wasm32"))]
64            return std::env::current_dir()
65                .map(|path| Value::String(path.to_string_lossy().into_owned()))
66                .map_err(|error| format!("os/cwd failed: {error}"));
67        }
68        "env" => {
69            if !values.is_empty() {
70                return Err("os/env expects no arguments".into());
71            }
72            #[cfg(target_arch = "wasm32")]
73            return Ok(Value::Map(PMap::new()));
74            #[cfg(not(target_arch = "wasm32"))]
75            return Ok(Value::Map(PMap::from_iter(
76                std::env::vars().map(|(key, value)| (Value::String(key), Value::String(value))),
77            )));
78        }
79        "getenv" => {
80            if values.len() != 1 {
81                return Err("os/getenv expects a name".into());
82            }
83            let Value::String(name) = &values[0] else {
84                return Err("os/getenv expects a string".into());
85            };
86            #[cfg(target_arch = "wasm32")]
87            {
88                let _ = name;
89                return Ok(Value::Nil);
90            }
91            #[cfg(not(target_arch = "wasm32"))]
92            return Ok(std::env::var(name).map(Value::String).unwrap_or(Value::Nil));
93        }
94        "process?" => {
95            if values.len() != 1 {
96                return Err("os/process? expects one argument".into());
97            }
98            let value = values[0].clone();
99            #[cfg(not(target_arch = "wasm32"))]
100            return Ok(Value::Bool(crate::native_process::is_process(&value)));
101            #[cfg(target_arch = "wasm32")]
102            return Ok(Value::Bool(false));
103        }
104        _ => {}
105    }
106    require_process_access(&format!("os/{operation}"))?;
107    #[cfg(target_arch = "wasm32")]
108    return Err(format!("os/{operation} is unsupported on wasm"));
109    #[cfg(not(target_arch = "wasm32"))]
110    match operation {
111        "spawn" => {
112            if !(1..=2).contains(&values.len()) {
113                return Err("os/spawn expects argv and optional options".into());
114            }
115            let argv = iterator_values(values[0].clone())?
116                .into_iter()
117                .map(|value| match value {
118                    Value::String(value) => Ok(value),
119                    _ => Err("os/spawn argv must contain strings".to_owned()),
120                })
121                .collect::<Result<Vec<_>, _>>()?;
122            let mut cwd = None;
123            let mut environment = Vec::new();
124            if values.len() == 2 {
125                let options = values[1].clone();
126                for (key, value) in map_entries(&options)
127                    .ok_or_else(|| "os/spawn options must be a map".to_owned())?
128                {
129                    match (key, value) {
130                        (Value::Keyword(key), Value::String(value)) if key.as_str() == "cwd" => {
131                            cwd = Some(value);
132                        }
133                        (Value::Keyword(key), value) if key.as_str() == "env" => {
134                            for (name, value) in map_entries(&value)
135                                .ok_or_else(|| "os/spawn :env must be a map".to_owned())?
136                            {
137                                let (Value::String(name), Value::String(value)) = (name, value)
138                                else {
139                                    return Err("os/spawn :env must contain string pairs".into());
140                                };
141                                environment.push((name, value));
142                            }
143                        }
144                        _ => {}
145                    }
146                }
147            }
148            crate::native_process::spawn(&argv, cwd.as_deref(), &environment)
149        }
150        method @ ("process-alive?"
151        | "process-close-input"
152        | "process-stdout"
153        | "process-stderr"
154        | "process-wait"
155        | "process-kill") => {
156            if values.len() != 1 {
157                return Err(format!("os/{method} expects a process"));
158            }
159            let process = values[0].clone();
160            match method {
161                "process-alive?" => crate::native_process::alive(&process).map(Value::Bool),
162                "process-close-input" => {
163                    crate::native_process::close_input(&process).map(|()| Value::Nil)
164                }
165                "process-stdout" => {
166                    crate::native_process::promise(&process, "stdout").map(Value::Promise)
167                }
168                "process-stderr" => {
169                    crate::native_process::promise(&process, "stderr").map(Value::Promise)
170                }
171                "process-wait" => {
172                    crate::native_process::promise(&process, "wait").map(Value::Promise)
173                }
174                "process-kill" => crate::native_process::kill(&process).map(|()| process),
175                _ => unreachable!(),
176            }
177        }
178        method @ ("process-stdout-stream" | "process-stderr-stream") => {
179            if values.len() != 1 {
180                return Err(format!("os/{method} expects a process"));
181            }
182            let process = values[0].clone();
183            let kind = if method == "process-stderr-stream" {
184                "stderr"
185            } else {
186                "stdout"
187            };
188            let handle = crate::native_process::take_stream(&process, kind)?;
189            Ok(host_stream(
190                Rc::new(move || Ok(crate::native_process::stream_promise(handle, kind))),
191                Rc::new(|| Ok(())),
192            ))
193        }
194        "process-write" => {
195            if values.len() != 2 {
196                return Err("os/process-write expects a process and bytes".into());
197            }
198            let process = values[0].clone();
199            let bytes = match &values[1] {
200                Value::Bytes(value) => value.clone(),
201                Value::ByteBuffer(value) => value.borrow().clone(),
202                _ => return Err("os/process-write expects bytes".into()),
203            };
204            crate::native_process::write(&process, &bytes).map(|count| Value::Number(count as i64))
205        }
206        _ => Err(format!("unknown os operation: {operation}")),
207    }
208}
209
210fn native_test_events() -> Value {
211    Value::Vector(PVector::from_iter([
212        Value::Keyword("test/run-started".into()),
213        Value::Keyword("test/fact-started".into()),
214        Value::Keyword("test/fact-completed".into()),
215        Value::Keyword("test/run-completed".into()),
216    ]))
217}
218
219fn native_test_runner(value: Value) -> Result<Value, String> {
220    match value {
221        Value::Keyword(runner) if matches!(runner.as_str(), "code.test" | "native") => {
222            Ok(Value::Keyword(runner))
223        }
224        _ => Err("runtime test runner must be :code.test or :native".into()),
225    }
226}
227
228fn native_test_active_runner() -> Result<Value, String> {
229    ACTIVE_TEST_RUNNER
230        .with(|runner| native_test_runner(Value::Keyword(runner.borrow().clone().into())))
231}
232
233fn native_test_config(runner: Value, options: Value) -> Result<Value, String> {
234    if map_entries(&options).is_none() {
235        return Err("std.native.Test/config options must be a map".into());
236    }
237    if map_value(&options, &Value::Keyword("runner".into())).is_some() {
238        return Err("std.native.Test/config runner is owned by the runtime".into());
239    }
240    Ok(Value::Map(PMap::from_iter([
241        (Value::Keyword("runner".into()), runner),
242        (Value::Keyword("options".into()), options),
243    ])))
244}
245
246fn native_test_context(desc: Value, actual: Value, expected: Value, failures: Value) -> Value {
247    Value::Map(PMap::from_iter([
248        (
249            Value::Keyword("test".into()),
250            Value::Map(PMap::from_iter([
251                (Value::Keyword("desc".into()), desc.clone()),
252                // `:name` remains an output alias while source packages move
253                // their fact identity to `:desc`.
254                (Value::Keyword("name".into()), desc),
255                (Value::Keyword("actual".into()), actual),
256                (Value::Keyword("expected".into()), expected),
257            ])),
258        ),
259        (Value::Keyword("failures".into()), failures),
260    ]))
261}
262
263fn native_test_failure(actual: Value, expected: Value) -> Value {
264    Value::Map(PMap::from_iter([
265        (
266            Value::Keyword("failure/code".into()),
267            Value::Keyword("test/not-equal".into()),
268        ),
269        (
270            Value::Keyword("failure/path".into()),
271            Value::Vector(PVector::new()),
272        ),
273        (
274            Value::Keyword("failure/in".into()),
275            Value::Vector(PVector::new()),
276        ),
277        (Value::Keyword("failure/actual".into()), actual),
278        (Value::Keyword("failure/expected".into()), expected),
279        (
280            Value::Keyword("failure/message".into()),
281            Value::String("values are not equal".into()),
282        ),
283        (
284            Value::Keyword("failure/context".into()),
285            Value::Map(PMap::new()),
286        ),
287        (
288            Value::Keyword("failure/children".into()),
289            Value::Vector(PVector::new()),
290        ),
291    ]))
292}
293
294fn native_test_compare(actual: Value, expected: Value) -> Result<Value, String> {
295    let pass = actual == expected;
296    let failures = if pass {
297        Value::Vector(PVector::new())
298    } else {
299        Value::Vector(PVector::from_iter([native_test_failure(
300            actual.clone(),
301            expected.clone(),
302        )]))
303    };
304    Ok(Value::Result(Rc::new(ResultValue::success(
305        Value::Bool(pass),
306        native_test_context(Value::Nil, actual, expected, failures),
307    )?)))
308}
309
310fn native_test_result(
311    desc: Value,
312    actual: Value,
313    expected: Value,
314    comparison: Value,
315) -> Result<Value, String> {
316    let Value::Result(comparison) = comparison else {
317        return Err("std.native.Test/result expects a comparison Result".into());
318    };
319    let failures = map_value(&comparison.context, &Value::Keyword("failures".into()))
320        .cloned()
321        .unwrap_or_else(|| Value::Vector(PVector::new()));
322    Ok(Value::Result(Rc::new(comparison.with_context(
323        native_test_context(desc, actual, expected, failures),
324    )?)))
325}
326
327fn native_test_error(desc: Value, actual: Value, expected: Value, error: String) -> Value {
328    Value::Result(Rc::new(
329        ResultValue::error(
330            caught_error(&error),
331            native_test_context(desc, actual, expected, Value::Vector(PVector::new())),
332        )
333        .expect("native Test error context is a map"),
334    ))
335}
336
337fn native_test_checked_result(desc: Value, metadata: Option<Value>, checked: Value) -> Value {
338    let Value::Result(result) = checked else {
339        return native_test_error(
340            desc,
341            Value::Nil,
342            Value::Nil,
343            "Test/check check function must return a Result".into(),
344        );
345    };
346    let mut context =
347        PMap::from_iter(map_entries(&result.context).expect("Result context is a map"));
348    let test = map_value(&result.context, &Value::Keyword("test".into()))
349        .cloned()
350        .unwrap_or_else(|| Value::Map(PMap::new()));
351    let mut test = PMap::from_iter(map_entries(&test).unwrap_or_default());
352    test = test.assoc_value(Value::Keyword("desc".into()), desc.clone());
353    test = test.assoc_value(Value::Keyword("name".into()), desc);
354    context = context.assoc_value(Value::Keyword("test".into()), Value::Map(test));
355    if let Some(metadata) = metadata {
356        context = context.assoc_value(Value::Keyword("meta".into()), metadata);
357    }
358    Value::Result(Rc::new(
359        result
360            .with_context(Value::Map(context))
361            .expect("native Test checked context is a map"),
362    ))
363}
364
365fn native_test_lifecycle_error(phase: &str, error: String) -> Value {
366    let desc = Value::String(format!("test {phase}"));
367    let phase = Value::Keyword(phase.into());
368    let Value::Result(result) = native_test_error(desc, Value::Nil, Value::Nil, error) else {
369        unreachable!()
370    };
371    Value::Result(Rc::new(
372        result
373            .with_context(Value::Map(PMap::from_iter([(
374                Value::Keyword("phase".into()),
375                phase,
376            )])))
377            .expect("native Test lifecycle context is a map"),
378    ))
379}
380
381fn native_test_lifecycle(lifecycle: &Value, phase: &str) -> Result<(), String> {
382    let Some(function) = map_value(lifecycle, &Value::Keyword(phase.into())).cloned() else {
383        return Ok(());
384    };
385    call_value(function, Vec::new())
386        .and_then(native_test_await)
387        .map(|_| ())
388}
389
390fn native_test_state() -> Result<crate::kernel::Namespace<Value>, String> {
391    Ok(namespace_registry()?.find_or_create("std.native.Test.state"))
392}
393
394fn native_test_state_value(key: &str) -> Result<Value, String> {
395    let state = native_test_state()?;
396    Ok(state
397        .resolve(&crate::lang::data::Symbol::parse(key))
398        .map(|var| var.deref_value())
399        .unwrap_or(Value::Nil))
400}
401
402fn native_test_set_state(key: &str, value: Value) -> Result<(), String> {
403    native_test_state()?.intern(key, value);
404    Ok(())
405}
406
407fn native_test_facts() -> Result<Vec<Value>, String> {
408    match native_test_state_value("facts")? {
409        Value::Vector(facts) => Ok(facts.iter().cloned().collect()),
410        Value::Nil => Ok(Vec::new()),
411        _ => Err("std.native.Test state facts must be a vector".into()),
412    }
413}
414
415fn native_test_set_facts(facts: Vec<Value>) -> Result<(), String> {
416    native_test_set_state("facts", Value::Vector(PVector::from_iter(facts)))
417}
418
419fn native_test_current_namespace() -> Result<String, String> {
420    Ok(namespace_registry()?.current().name().as_str().to_owned())
421}
422
423fn native_test_description(value: &Value, operation: &str) -> Result<Value, String> {
424    let desc = map_value(value, &Value::Keyword("desc".into())).cloned();
425    let name = map_value(value, &Value::Keyword("name".into())).cloned();
426    match (desc, name) {
427        (Some(Value::String(desc)), Some(Value::String(name)))
428            if !desc.is_empty() && desc == name =>
429        {
430            Ok(Value::String(desc))
431        }
432        (Some(Value::String(_)), Some(Value::String(_))) => Err(format!(
433            "std.native.Test/{operation} :desc and legacy :name must agree"
434        )),
435        (Some(Value::String(desc)), None) | (None, Some(Value::String(desc)))
436            if !desc.is_empty() =>
437        {
438            Ok(Value::String(desc))
439        }
440        (Some(_), _) | (_, Some(_)) => Err(format!(
441            "std.native.Test/{operation} :desc must be a non-empty string"
442        )),
443        (None, None) => Err(format!("std.native.Test/{operation} requires :desc")),
444    }
445}
446
447fn native_test_truthy(value: Option<&Value>) -> bool {
448    !matches!(value, None | Some(Value::Nil) | Some(Value::Bool(false)))
449}
450
451fn native_test_metadata(value: &Value, namespace: &str, order: i64) -> Result<Value, String> {
452    let metadata = map_value(value, &Value::Keyword("meta".into()))
453        .cloned()
454        .unwrap_or_else(|| Value::Map(PMap::new()));
455    let Some(entries) = map_entries(&metadata) else {
456        return Err("std.native.Test/register :meta must be a map".into());
457    };
458    let metadata = PMap::from_iter(entries)
459        .assoc_value(
460            Value::Keyword("test/namespace".into()),
461            Value::String(namespace.into()),
462        )
463        .assoc_value(Value::Keyword("test/order".into()), Value::Number(order));
464    Ok(Value::Map(metadata))
465}
466
467fn native_test_next_order() -> Result<i64, String> {
468    let current = match native_test_state_value("order")? {
469        Value::Number(value) if value >= 0 => value,
470        Value::Nil => 0,
471        _ => return Err("std.native.Test state order must be a non-negative number".into()),
472    };
473    let next = current + 1;
474    native_test_set_state("order", Value::Number(next))?;
475    Ok(next)
476}
477
478fn native_test_fact_value(
479    namespace: String,
480    desc: Value,
481    metadata: Value,
482    descriptor: &Value,
483) -> Result<Value, String> {
484    let function = map_value(descriptor, &Value::Keyword("function".into())).cloned();
485    let test = map_value(descriptor, &Value::Keyword("test".into())).cloned();
486    let expected = map_value(descriptor, &Value::Keyword("expected".into())).cloned();
487    if function.is_some() && (test.is_some() || expected.is_some()) {
488        return Err(
489            "std.native.Test/register accepts either :function or :test with :expected".into(),
490        );
491    }
492    if function.is_none() && (test.is_none() || expected.is_none()) {
493        return Err(
494            "std.native.Test/register requires :function or both :test and :expected".into(),
495        );
496    }
497    let mut fields = vec![
498        (Value::Keyword("namespace".into()), Value::String(namespace)),
499        (Value::Keyword("desc".into()), desc.clone()),
500        (Value::Keyword("name".into()), desc),
501        (Value::Keyword("meta".into()), metadata),
502    ];
503    if let Some(function) = function {
504        fields.push((Value::Keyword("function".into()), function));
505    } else {
506        fields.push((Value::Keyword("test".into()), test.expect("checked above")));
507        fields.push((
508            Value::Keyword("expected".into()),
509            expected.expect("checked above"),
510        ));
511    }
512    for key in ["before", "after"] {
513        if let Some(value) = map_value(descriptor, &Value::Keyword(key.into())).cloned() {
514            fields.push((Value::Keyword(key.into()), value));
515        }
516    }
517    Ok(Value::Map(PMap::from_iter(fields)))
518}
519
520fn native_test_register(descriptor: Value) -> Result<Value, String> {
521    if map_entries(&descriptor).is_none() {
522        return Err("std.native.Test/register expects a fact map".into());
523    }
524    let namespace = native_test_current_namespace()?;
525    let desc = native_test_description(&descriptor, "register")?;
526    let order = native_test_next_order()?;
527    let metadata = native_test_metadata(&descriptor, &namespace, order)?;
528    let fact = native_test_fact_value(namespace.clone(), desc.clone(), metadata, &descriptor)?;
529    let mut facts = native_test_facts()?;
530    facts.retain(|candidate| {
531        native_test_map_value(candidate, "namespace") != Some(Value::String(namespace.clone()))
532            || native_test_map_value(candidate, "desc") != Some(desc.clone())
533    });
534    facts.push(fact.clone());
535    native_test_set_facts(facts)?;
536    Ok(fact)
537}
538
539fn native_test_check(
540    cases: Value,
541    check_function: Option<Value>,
542    lifecycle: Option<Value>,
543) -> Result<Value, String> {
544    let cases = match cases {
545        Value::Vector(cases) => cases.iter().cloned().collect::<Vec<_>>(),
546        Value::Tuple(cases) => cases.iter().cloned().collect::<Vec<_>>(),
547        _ => return Err("std.native.Test/check expects a vector of test cases".into()),
548    };
549    let mut results = Vec::new();
550    let setup_ok = match &lifecycle {
551        Some(lifecycle) => match native_test_lifecycle(lifecycle, "setup") {
552            Ok(()) => true,
553            Err(error) => {
554                results.push(native_test_lifecycle_error("setup", error));
555                false
556            }
557        },
558        None => true,
559    };
560    if setup_ok {
561        for (index, case) in cases.iter().enumerate() {
562            let fallback_desc = Value::String(format!("invalid case {}", index + 1));
563            let Some(entries) = map_entries(case) else {
564                results.push(native_test_error(
565                    fallback_desc,
566                    Value::Nil,
567                    Value::Nil,
568                    "Test/check case must be a map".into(),
569                ));
570                continue;
571            };
572            let _ = entries;
573            let desc = native_test_description(case, "check").unwrap_or(fallback_desc);
574            let expected = map_value(case, &Value::Keyword("expected".into())).cloned();
575            let test = map_value(case, &Value::Keyword("test".into())).cloned();
576            let metadata = map_value(case, &Value::Keyword("meta".into())).cloned();
577            let result = match (test, expected) {
578                (Some(test), Some(expected)) => match &check_function {
579                    Some(check) => match call_value(check.clone(), vec![test, expected])
580                        .and_then(native_test_await)
581                    {
582                        Ok(checked) => native_test_checked_result(desc, metadata, checked),
583                        Err(error) => {
584                            let failed =
585                                native_test_error(desc.clone(), Value::Nil, Value::Nil, error);
586                            native_test_checked_result(desc, metadata, failed)
587                        }
588                    },
589                    None => match call_value(test, Vec::new()).and_then(native_test_await) {
590                        Ok(actual) => {
591                            let comparison = native_test_compare(actual.clone(), expected.clone())?;
592                            native_test_result(desc, actual, expected, comparison)?
593                        }
594                        Err(error) => native_test_error(desc, Value::Nil, expected, error),
595                    },
596                },
597                (None, expected) => native_test_error(
598                    desc,
599                    Value::Nil,
600                    expected.unwrap_or(Value::Nil),
601                    "Test/check case requires :test".into(),
602                ),
603                (Some(_), None) => native_test_error(
604                    desc,
605                    Value::Nil,
606                    Value::Nil,
607                    "Test/check case requires :expected".into(),
608                ),
609            };
610            results.push(result);
611        }
612    }
613    if let Some(lifecycle) = &lifecycle {
614        if let Err(error) = native_test_lifecycle(lifecycle, "teardown") {
615            results.push(native_test_lifecycle_error("teardown", error));
616        }
617    }
618    let output = Value::Vector(PVector::from_iter(results.clone()));
619    let mut history = match native_test_state_value("results")? {
620        Value::Vector(values) => values.iter().cloned().collect::<Vec<_>>(),
621        Value::Nil => Vec::new(),
622        _ => return Err("std.native.Test state results must be a vector".into()),
623    };
624    history.extend(results);
625    native_test_set_state("results", Value::Vector(PVector::from_iter(history)))?;
626    Ok(output)
627}
628
629fn native_test_result_passed(value: &Value) -> bool {
630    matches!(
631        value,
632        Value::Result(result) if result.is_success() && matches!(result.data, Value::Bool(true))
633    )
634}
635
636fn native_test_result_timed_out(value: &Value) -> bool {
637    let Value::Result(result) = value else {
638        return false;
639    };
640    result.is_timeout()
641        || result
642            .error
643            .as_ref()
644            .is_some_and(|error| error.message == "asynchronous test did not settle")
645}
646
647fn native_test_checks(value: Value, desc: Value) -> Vec<Value> {
648    let values = match value {
649        Value::Vector(values) => values.iter().cloned().collect(),
650        Value::Tuple(values) => values.iter().cloned().collect(),
651        Value::Result(_) => vec![value],
652        value => {
653            let context = native_test_context(
654                desc,
655                value,
656                Value::Keyword("returned".into()),
657                Value::Vector(PVector::new()),
658            );
659            return vec![Value::Result(Rc::new(
660                ResultValue::success(Value::Bool(true), context)
661                    .expect("native Test returned-value context is a map"),
662            ))];
663        }
664    };
665    values
666        .into_iter()
667        .map(|value| match value {
668            Value::Result(_) => value,
669            value => native_test_error(
670                desc.clone(),
671                value,
672                Value::Keyword("Result".into()),
673                "Test fact functions must return Results or a vector of Results".into(),
674            ),
675        })
676        .collect()
677}
678
679fn native_test_fact_identity(fact: &Value) -> Result<Vec<(Value, Value)>, String> {
680    let namespace = native_test_map_value(fact, "namespace")
681        .ok_or_else(|| "std.native.Test fact is missing :namespace".to_owned())?;
682    let desc = native_test_map_value(fact, "desc")
683        .ok_or_else(|| "std.native.Test fact is missing :desc".to_owned())?;
684    let metadata = native_test_map_value(fact, "meta")
685        .ok_or_else(|| "std.native.Test fact is missing :meta".to_owned())?;
686    Ok(vec![
687        (Value::Keyword("namespace".into()), namespace),
688        (Value::Keyword("desc".into()), desc.clone()),
689        (Value::Keyword("name".into()), desc),
690        (Value::Keyword("meta".into()), metadata),
691    ])
692}
693
694fn native_test_hook(value: Option<Value>) -> Result<(), String> {
695    let Some(function) = value else {
696        return Ok(());
697    };
698    call_value(function, Vec::new())
699        .and_then(native_test_await)
700        .map(|_| ())
701}
702
703fn native_test_cancelled(fact: &Value, options: &Value) -> Result<bool, String> {
704    let Some(value) = native_test_map_value(options, "cancelled") else {
705        return Ok(false);
706    };
707    match value {
708        Value::Bool(value) => Ok(value),
709        Value::Nil => Ok(false),
710        function => call_value(function, vec![fact.clone()])
711            .and_then(native_test_await)
712            .map(|value| native_test_truthy(Some(&value))),
713    }
714}
715
716fn native_test_fact_checks(fact: &Value, options: &Value) -> Result<Vec<Value>, String> {
717    let desc = native_test_map_value(fact, "desc")
718        .ok_or_else(|| "std.native.Test fact is missing :desc".to_owned())?;
719    if let Some(function) = native_test_map_value(fact, "function") {
720        return call_value(function, vec![options.clone()])
721            .and_then(native_test_await)
722            .map(|value| native_test_checks(value, desc));
723    }
724    let test = native_test_map_value(fact, "test")
725        .ok_or_else(|| "std.native.Test fact is missing :test".to_owned())?;
726    let expected = native_test_map_value(fact, "expected")
727        .ok_or_else(|| "std.native.Test fact is missing :expected".to_owned())?;
728    native_test_check(
729        Value::Vector(PVector::from_iter([Value::Map(PMap::from_iter([
730            (Value::Keyword("desc".into()), desc),
731            (Value::Keyword("test".into()), test),
732            (Value::Keyword("expected".into()), expected),
733        ]))])),
734        None,
735        None,
736    )
737    .and_then(|value| match value {
738        Value::Vector(values) => Ok(values.iter().cloned().collect()),
739        _ => unreachable!(),
740    })
741}
742
743fn native_test_status(checks: &[Value]) -> &'static str {
744    if checks.iter().any(native_test_result_timed_out) {
745        "timeout"
746    } else if checks
747        .iter()
748        .any(|value| matches!(value, Value::Result(result) if result.is_error()))
749    {
750        "error"
751    } else if checks.iter().all(native_test_result_passed) {
752        "passed"
753    } else {
754        "failed"
755    }
756}
757
758fn native_test_fact_result(
759    fact: &Value,
760    status: &str,
761    checks: Vec<Value>,
762    error: Option<String>,
763    elapsed: i64,
764) -> Result<Value, String> {
765    let mut fields = native_test_fact_identity(fact)?;
766    fields.push((
767        Value::Keyword("status".into()),
768        Value::Keyword(status.into()),
769    ));
770    fields.push((
771        Value::Keyword("checks".into()),
772        Value::Vector(PVector::from_iter(checks)),
773    ));
774    fields.push((
775        Value::Keyword("elapsed".into()),
776        Value::Number(elapsed.max(0)),
777    ));
778    if let Some(error) = error {
779        fields.push((Value::Keyword("error".into()), Value::String(error)));
780    }
781    Ok(Value::Map(PMap::from_iter(fields)))
782}
783
784fn native_test_run_fact(fact: Value, options: Value) -> Result<Value, String> {
785    if map_entries(&fact).is_none() {
786        return Err("std.native.Test/run-fact expects a fact map".into());
787    }
788    let started = crate::clock::time_ms();
789    let metadata = native_test_map_value(&fact, "meta")
790        .ok_or_else(|| "std.native.Test fact is missing :meta".to_owned())?;
791    if native_test_truthy(native_test_map_value(&metadata, "skip").as_ref()) {
792        return native_test_fact_result(&fact, "skipped", Vec::new(), None, 0);
793    }
794    if native_test_cancelled(&fact, &options)? {
795        return native_test_fact_result(&fact, "cancelled", Vec::new(), None, 0);
796    }
797
798    let mut checks = Vec::new();
799    let mut failure = None;
800    for hook in [
801        native_test_map_value(&options, "before-each"),
802        native_test_map_value(&fact, "before"),
803    ] {
804        if let Err(error) = native_test_hook(hook) {
805            failure = Some(error);
806            break;
807        }
808    }
809    if failure.is_none() {
810        match native_test_fact_checks(&fact, &options) {
811            Ok(output) => checks = output,
812            Err(error) => failure = Some(error),
813        }
814    }
815    for hook in [
816        native_test_map_value(&fact, "after"),
817        native_test_map_value(&options, "after-each"),
818    ] {
819        if let Err(error) = native_test_hook(hook) {
820            failure.get_or_insert(error);
821        }
822    }
823    let elapsed = crate::clock::time_ms() - started;
824    match failure {
825        Some(error) => native_test_fact_result(&fact, "error", checks, Some(error), elapsed),
826        None => native_test_fact_result(&fact, native_test_status(&checks), checks, None, elapsed),
827    }
828}
829
830fn native_test_summary(results: Vec<Value>) -> Result<Value, String> {
831    let mut counts = [0_i64; 6];
832    let mut check_total = 0_i64;
833    let mut check_passed = 0_i64;
834    let mut namespaces = HashSet::new();
835    for result in &results {
836        let status = native_test_map_value(result, "status")
837            .ok_or_else(|| "std.native.Test summary result is missing :status".to_owned())?;
838        let index = match status {
839            Value::Keyword(status) => match status.as_str() {
840                "passed" => 0,
841                "failed" => 1,
842                "error" => 2,
843                "timeout" => 3,
844                "skipped" => 4,
845                "cancelled" => 5,
846                value => {
847                    return Err(format!(
848                        "std.native.Test summary has unknown status :{value}"
849                    ))
850                }
851            },
852            _ => return Err("std.native.Test summary status must be a keyword".into()),
853        };
854        counts[index] += 1;
855        if let Some(Value::String(namespace)) = native_test_map_value(result, "namespace") {
856            namespaces.insert(namespace);
857        }
858        if let Some(Value::Vector(checks)) = native_test_map_value(result, "checks") {
859            for check in checks.iter() {
860                check_total += 1;
861                if native_test_result_passed(check) {
862                    check_passed += 1;
863                }
864            }
865        }
866    }
867    let check_failed = check_total - check_passed;
868    let status = if counts[1] + counts[2] + counts[3] == 0 {
869        "passed"
870    } else {
871        "failed"
872    };
873    Ok(Value::Map(PMap::from_iter([
874        (
875            Value::Keyword("status".into()),
876            Value::Keyword(status.into()),
877        ),
878        (
879            Value::Keyword("counts".into()),
880            Value::Map(PMap::from_iter([
881                (Value::Keyword("passed".into()), Value::Number(counts[0])),
882                (Value::Keyword("failed".into()), Value::Number(counts[1])),
883                (Value::Keyword("error".into()), Value::Number(counts[2])),
884                (Value::Keyword("timeout".into()), Value::Number(counts[3])),
885                (Value::Keyword("skipped".into()), Value::Number(counts[4])),
886                (Value::Keyword("cancelled".into()), Value::Number(counts[5])),
887            ])),
888        ),
889        (
890            Value::Keyword("check-counts".into()),
891            Value::Map(PMap::from_iter([
892                (Value::Keyword("total".into()), Value::Number(check_total)),
893                (Value::Keyword("passed".into()), Value::Number(check_passed)),
894                (Value::Keyword("failed".into()), Value::Number(check_failed)),
895            ])),
896        ),
897        (
898            Value::Keyword("files".into()),
899            Value::Number(namespaces.len() as i64),
900        ),
901        (
902            Value::Keyword("facts".into()),
903            Value::Number(results.len() as i64),
904        ),
905        (Value::Keyword("checks".into()), Value::Number(check_total)),
906        (Value::Keyword("passed".into()), Value::Number(check_passed)),
907        (Value::Keyword("failed".into()), Value::Number(check_failed)),
908        (Value::Keyword("throw".into()), Value::Number(counts[2])),
909        (Value::Keyword("timeout".into()), Value::Number(counts[3])),
910        (
911            Value::Keyword("results".into()),
912            Value::Vector(PVector::from_iter(results)),
913        ),
914    ])))
915}
916
917fn native_test_namespace(value: Value, operation: &str) -> Result<String, String> {
918    match value {
919        Value::String(value) => Ok(value),
920        Value::Symbol(value) => Ok(value.as_str().to_owned()),
921        _ => Err(format!(
922            "std.native.Test/{operation} namespace must be a string or symbol"
923        )),
924    }
925}
926
927fn native_test_run(options: Value) -> Result<Value, String> {
928    if map_entries(&options).is_none() {
929        return Err(
930            "std.native.Test/run expects an optional options map; use Test/check for cases".into(),
931        );
932    }
933    let namespace = match native_test_map_value(&options, "namespace") {
934        Some(value) => native_test_namespace(value, "run")?,
935        None => native_test_current_namespace()?,
936    };
937    let facts = native_test_facts()?
938        .into_iter()
939        .filter(|fact| {
940            native_test_map_value(fact, "namespace") == Some(Value::String(namespace.clone()))
941        })
942        .collect::<Vec<_>>();
943    let checks = match native_test_state_value("results")? {
944        Value::Vector(values) => values.iter().cloned().collect::<Vec<_>>(),
945        Value::Nil => Vec::new(),
946        _ => return Err("std.native.Test state results must be a vector".into()),
947    };
948    let mut results = checks
949        .into_iter()
950        .enumerate()
951        .map(|(index, check)| {
952            let desc = match &check {
953                Value::Result(result) => map_value(&result.context, &Value::Keyword("desc".into()))
954                    .cloned()
955                    .unwrap_or_else(|| Value::String(format!("test check {}", index + 1))),
956                _ => Value::String(format!("test check {}", index + 1)),
957            };
958            let fact = Value::Map(PMap::from_iter([
959                (
960                    Value::Keyword("namespace".into()),
961                    Value::String(namespace.clone()),
962                ),
963                (Value::Keyword("desc".into()), desc.clone()),
964                (Value::Keyword("name".into()), desc),
965                (Value::Keyword("meta".into()), Value::Map(PMap::new())),
966            ]));
967            native_test_fact_result(&fact, native_test_status(&[check.clone()]), vec![check], None, 0)
968        })
969        .collect::<Result<Vec<_>, _>>()?;
970    let mut suite_error = None;
971    if let Err(error) = native_test_hook(native_test_map_value(&options, "before-all")) {
972        suite_error = Some(error);
973    }
974    if suite_error.is_none() {
975        let mut fail_fast = false;
976        for fact in facts {
977            if fail_fast {
978                results.push(native_test_fact_result(
979                    &fact,
980                    "cancelled",
981                    Vec::new(),
982                    None,
983                    0,
984                )?);
985                continue;
986            }
987            let result = native_test_run_fact(fact, options.clone())?;
988            fail_fast = native_test_truthy(native_test_map_value(&options, "fail-fast").as_ref())
989                && matches!(
990                    native_test_map_value(&result, "status"),
991                    Some(Value::Keyword(status)) if matches!(status.as_str(), "failed" | "error" | "timeout")
992                );
993            results.push(result);
994        }
995    } else {
996        let synthetic = Value::Map(PMap::from_iter([
997            (
998                Value::Keyword("namespace".into()),
999                Value::String(namespace.clone()),
1000            ),
1001            (
1002                Value::Keyword("desc".into()),
1003                Value::String("test before-all".into()),
1004            ),
1005            (
1006                Value::Keyword("name".into()),
1007                Value::String("test before-all".into()),
1008            ),
1009            (Value::Keyword("meta".into()), Value::Map(PMap::new())),
1010        ]));
1011        results.push(native_test_fact_result(
1012            &synthetic,
1013            "error",
1014            Vec::new(),
1015            suite_error,
1016            0,
1017        )?);
1018    }
1019    if let Err(error) = native_test_hook(native_test_map_value(&options, "after-all")) {
1020        let synthetic = Value::Map(PMap::from_iter([
1021            (Value::Keyword("namespace".into()), Value::String(namespace)),
1022            (
1023                Value::Keyword("desc".into()),
1024                Value::String("test after-all".into()),
1025            ),
1026            (
1027                Value::Keyword("name".into()),
1028                Value::String("test after-all".into()),
1029            ),
1030            (Value::Keyword("meta".into()), Value::Map(PMap::new())),
1031        ]));
1032        results.push(native_test_fact_result(
1033            &synthetic,
1034            "error",
1035            Vec::new(),
1036            Some(error),
1037            0,
1038        )?);
1039    }
1040    let summary = native_test_summary(results)?;
1041    native_test_set_state("last-run", summary.clone())?;
1042    Ok(summary)
1043}
1044
1045fn native_test_lookup(namespace: &str, desc: &Value) -> Result<Value, String> {
1046    Ok(native_test_facts()?
1047        .into_iter()
1048        .find(|fact| {
1049            native_test_map_value(fact, "namespace") == Some(Value::String(namespace.into()))
1050                && native_test_map_value(fact, "desc") == Some(desc.clone())
1051        })
1052        .unwrap_or(Value::Nil))
1053}
1054
1055fn native_test_desc_argument(value: Value, operation: &str) -> Result<Value, String> {
1056    match value {
1057        Value::String(value) if !value.is_empty() => Ok(Value::String(value)),
1058        _ => Err(format!(
1059            "std.native.Test/{operation} description must be a non-empty string"
1060        )),
1061    }
1062}
1063
1064fn native_test_reset() -> Result<Value, String> {
1065    native_test_set_state("facts", Value::Vector(PVector::new()))?;
1066    native_test_set_state("results", Value::Vector(PVector::new()))?;
1067    native_test_set_state("order", Value::Number(0))?;
1068    native_test_set_state("last-run", Value::Nil)?;
1069    Ok(Value::Nil)
1070}
1071
1072fn native_test_await(value: Value) -> Result<Value, String> {
1073    match value {
1074        Value::Promise(promise) => match promise.wait_state() {
1075            PromiseState::Fulfilled(value) => Ok(value),
1076            PromiseState::Rejected(error) => Err(promise_rejection_error(error)),
1077            PromiseState::Pending => Err("asynchronous test did not settle".into()),
1078        },
1079        value => Ok(value),
1080    }
1081}
1082
1083fn native_test_require_result(value: Value, operation: &str) -> Result<Rc<ResultValue>, String> {
1084    match value {
1085        Value::Result(result) => Ok(result),
1086        _ => Err(format!("std.native.Test/{operation} expects a Result")),
1087    }
1088}
1089
1090fn native_test_context_value(result: &ResultValue, key: &str) -> Value {
1091    native_test_map_value(&result.context, key).unwrap_or(Value::Nil)
1092}
1093
1094fn native_test_map_value(value: &Value, key: &str) -> Option<Value> {
1095    map_entries(value)?
1096        .into_iter()
1097        .find_map(|(candidate, value)| {
1098            matches!(candidate, Value::Keyword(keyword) if keyword.as_str() == key).then_some(value)
1099        })
1100}
1101
1102fn native_test_detail(result: &ResultValue, key: &str) -> Value {
1103    let test = native_test_context_value(result, "test");
1104    native_test_map_value(&test, key).unwrap_or(Value::Nil)
1105}
1106
1107fn native_test_failure_shape(value: &Value) -> bool {
1108    let Some(_) = map_entries(value) else {
1109        return false;
1110    };
1111    let keyword = |key: &str| matches!(native_test_map_value(value, key), Some(Value::Keyword(_)));
1112    let vector = |key: &str| {
1113        matches!(
1114            native_test_map_value(value, key),
1115            Some(Value::Vector(_) | Value::Tuple(_))
1116        )
1117    };
1118    let string = |key: &str| matches!(native_test_map_value(value, key), Some(Value::String(_)));
1119    let map = |key: &str| {
1120        native_test_map_value(value, key).is_some_and(|value| map_entries(&value).is_some())
1121    };
1122    let children_valid = match native_test_map_value(value, "failure/children") {
1123        Some(Value::Vector(children)) => children.iter().all(native_test_failure_shape),
1124        Some(Value::Tuple(children)) => children.iter().all(native_test_failure_shape),
1125        _ => false,
1126    };
1127    keyword("failure/code")
1128        && vector("failure/path")
1129        && vector("failure/in")
1130        && native_test_map_value(value, "failure/actual").is_some()
1131        && native_test_map_value(value, "failure/expected").is_some()
1132        && string("failure/message")
1133        && map("failure/context")
1134        && vector("failure/children")
1135        && children_valid
1136}
1137
1138fn native_test_failure_leaves(value: &Value, leaves: &mut Vec<Value>) {
1139    if !native_test_failure_shape(value) {
1140        return;
1141    }
1142    match native_test_map_value(value, "failure/children") {
1143        Some(Value::Vector(children)) => {
1144            if children.is_empty() {
1145                leaves.push(value.clone());
1146            } else {
1147                for child in children.iter() {
1148                    native_test_failure_leaves(child, leaves);
1149                }
1150            }
1151        }
1152        Some(Value::Tuple(children)) => {
1153            if children.is_empty() {
1154                leaves.push(value.clone());
1155            } else {
1156                for child in children.iter() {
1157                    native_test_failure_leaves(child, leaves);
1158                }
1159            }
1160        }
1161        _ => {}
1162    }
1163}
1164
1165fn native_test_failures(result: &ResultValue) -> Value {
1166    match native_test_context_value(result, "failures") {
1167        Value::Vector(failures) => Value::Vector(failures),
1168        Value::Tuple(failures) => Value::Vector(PVector::from_iter(failures.iter().cloned())),
1169        _ => Value::Vector(PVector::new()),
1170    }
1171}
1172
1173fn native_test_failure_seq(result: &ResultValue) -> Value {
1174    let mut leaves = Vec::new();
1175    if let Value::Vector(failures) = native_test_failures(result) {
1176        for failure in failures.iter() {
1177            native_test_failure_leaves(failure, &mut leaves);
1178        }
1179    }
1180    Value::Vector(PVector::from_iter(leaves))
1181}
1182
1183fn native_test_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
1184    let operation = operation
1185        .strip_prefix("std.native.Test/")
1186        .unwrap_or(operation);
1187    match operation {
1188        "events" => {
1189            if !values.is_empty() {
1190                return Err("std.native.Test/events expects no arguments".into());
1191            }
1192            Ok(native_test_events())
1193        }
1194        "catalog" => {
1195            if !values.is_empty() {
1196                return Err("std.native.Test/catalog expects no arguments".into());
1197            }
1198            Ok(Value::Map(PMap::from_iter([
1199                (
1200                    Value::Keyword("runners".into()),
1201                    Value::Vector(PVector::from_iter([
1202                        Value::Keyword("code.test".into()),
1203                        Value::Keyword("native".into()),
1204                    ])),
1205                ),
1206                (
1207                    Value::Keyword("default".into()),
1208                    Value::Keyword("code.test".into()),
1209                ),
1210                (
1211                    Value::Keyword("runner".into()),
1212                    native_test_active_runner()?,
1213                ),
1214                (
1215                    Value::Keyword("context".into()),
1216                    Value::Keyword("test".into()),
1217                ),
1218                (Value::Keyword("events".into()), native_test_events()),
1219            ])))
1220        }
1221        "config" => {
1222            if values.len() > 1 {
1223                return Err("std.native.Test/config expects optional options".into());
1224            }
1225            let options = if values.is_empty() {
1226                Value::Map(PMap::new())
1227            } else {
1228                values[0].clone()
1229            };
1230            native_test_config(native_test_active_runner()?, options)
1231        }
1232        "context" => {
1233            if values.len() > 1 {
1234                return Err("std.native.Test/context expects an optional config".into());
1235            }
1236            let config = if values.is_empty() {
1237                native_test_config(native_test_active_runner()?, Value::Map(PMap::new()))?
1238            } else {
1239                let value = values[0].clone();
1240                let Some(runner) = map_value(&value, &Value::Keyword("runner".into())).cloned()
1241                else {
1242                    return Err("std.native.Test/context expects a Test/config map".into());
1243                };
1244                let runner = native_test_runner(runner)?;
1245                if runner != native_test_active_runner()? {
1246                    return Err(
1247                        "std.native.Test/context config runner does not match the runtime".into(),
1248                    );
1249                }
1250                value
1251            };
1252            Ok(Value::Pointer(PPointer::new(
1253                "test".into(),
1254                PMap::from_iter([
1255                    (Value::Keyword("id".into()), Value::Keyword("test".into())),
1256                    (Value::Keyword("config".into()), config),
1257                ]),
1258            )))
1259        }
1260        "compare" => {
1261            if values.len() != 2 {
1262                return Err("std.native.Test/compare expects actual and expected".into());
1263            }
1264            native_test_compare(values[0].clone(), values[1].clone())
1265        }
1266        "result" => {
1267            if values.len() != 4 {
1268                return Err(
1269                    "std.native.Test/result expects name, actual, expected, and comparison Result"
1270                        .into(),
1271                );
1272            }
1273            let name = values[0].clone();
1274            let actual = values[1].clone();
1275            let expected = values[2].clone();
1276            let comparison = values[3].clone();
1277            native_test_result(name, actual, expected, comparison)
1278        }
1279        "check" => {
1280            if values.is_empty() || values.len() > 3 {
1281                return Err(
1282                    "std.native.Test/check expects cases, an optional check function, and an optional lifecycle map".into(),
1283                );
1284            }
1285            let cases = values[0].clone();
1286            let second = if values.len() >= 2 {
1287                Some(values[1].clone())
1288            } else {
1289                None
1290            };
1291            let third = if values.len() == 3 {
1292                Some(values[2].clone())
1293            } else {
1294                None
1295            };
1296            let (check_function, lifecycle) = match (second, third) {
1297                (Some(check), Some(lifecycle)) => (Some(check), Some(lifecycle)),
1298                (Some(value), None) if map_entries(&value).is_some() => (None, Some(value)),
1299                (check, None) => (check, None),
1300                (None, Some(_)) => unreachable!(),
1301            };
1302            if lifecycle
1303                .as_ref()
1304                .is_some_and(|value| map_entries(value).is_none())
1305            {
1306                return Err("std.native.Test/check lifecycle must be a map".into());
1307            }
1308            native_test_check(cases, check_function, lifecycle)
1309        }
1310        "register" => match values.as_slice() {
1311            [fact] => native_test_register(fact.clone()),
1312            _ => Err("std.native.Test/register expects one fact map".into()),
1313        },
1314        "facts" => {
1315            if values.len() > 1 {
1316                return Err("std.native.Test/facts expects an optional namespace".into());
1317            }
1318            let namespace = match values.as_slice() {
1319                [] => native_test_current_namespace()?,
1320                [namespace] => native_test_namespace(namespace.clone(), "facts")?,
1321                _ => unreachable!(),
1322            };
1323            Ok(Value::Vector(PVector::from_iter(
1324                native_test_facts()?.into_iter().filter(|fact| {
1325                    native_test_map_value(fact, "namespace")
1326                        == Some(Value::String(namespace.clone()))
1327                }),
1328            )))
1329        }
1330        "get" => {
1331            let (namespace, desc) = match values.as_slice() {
1332                [desc] => (
1333                    native_test_current_namespace()?,
1334                    native_test_desc_argument(desc.clone(), "get")?,
1335                ),
1336                [namespace, desc] => (
1337                    native_test_namespace(namespace.clone(), "get")?,
1338                    native_test_desc_argument(desc.clone(), "get")?,
1339                ),
1340                _ => {
1341                    return Err(
1342                        "std.native.Test/get expects a description and optional namespace".into(),
1343                    )
1344                }
1345            };
1346            native_test_lookup(&namespace, &desc)
1347        }
1348        "remove" => {
1349            let (namespace, desc) = match values.as_slice() {
1350                [desc] => (
1351                    native_test_current_namespace()?,
1352                    native_test_desc_argument(desc.clone(), "remove")?,
1353                ),
1354                [namespace, desc] => (
1355                    native_test_namespace(namespace.clone(), "remove")?,
1356                    native_test_desc_argument(desc.clone(), "remove")?,
1357                ),
1358                _ => {
1359                    return Err(
1360                        "std.native.Test/remove expects a description and optional namespace"
1361                            .into(),
1362                    )
1363                }
1364            };
1365            let removed = native_test_lookup(&namespace, &desc)?;
1366            let mut facts = native_test_facts()?;
1367            facts.retain(|fact| {
1368                native_test_map_value(fact, "namespace") != Some(Value::String(namespace.clone()))
1369                    || native_test_map_value(fact, "desc") != Some(desc.clone())
1370            });
1371            native_test_set_facts(facts)?;
1372            Ok(removed)
1373        }
1374        "purge" => {
1375            if values.len() > 1 {
1376                return Err("std.native.Test/purge expects an optional namespace".into());
1377            }
1378            let namespace = match values.as_slice() {
1379                [] => native_test_current_namespace()?,
1380                [namespace] => native_test_namespace(namespace.clone(), "purge")?,
1381                _ => unreachable!(),
1382            };
1383            let facts = native_test_facts()?;
1384            let removed = facts
1385                .iter()
1386                .filter(|fact| {
1387                    native_test_map_value(fact, "namespace")
1388                        == Some(Value::String(namespace.clone()))
1389                })
1390                .cloned()
1391                .collect::<Vec<_>>();
1392            native_test_set_facts(
1393                facts
1394                    .into_iter()
1395                    .filter(|fact| {
1396                        native_test_map_value(fact, "namespace")
1397                            != Some(Value::String(namespace.clone()))
1398                    })
1399                    .collect(),
1400            )?;
1401            Ok(Value::Vector(PVector::from_iter(removed)))
1402        }
1403        "reset" => {
1404            if !values.is_empty() {
1405                return Err("std.native.Test/reset expects no arguments".into());
1406            }
1407            native_test_reset()
1408        }
1409        "run-fact" => {
1410            let (fact, options) = match values.as_slice() {
1411                [fact] => (fact.clone(), Value::Map(PMap::new())),
1412                [fact, options] if map_entries(options).is_some() => {
1413                    (fact.clone(), options.clone())
1414                }
1415                [_, _] => return Err("std.native.Test/run-fact options must be a map".into()),
1416                _ => return Err(
1417                    "std.native.Test/run-fact expects a fact or description and optional options"
1418                        .into(),
1419                ),
1420            };
1421            let fact = if map_entries(&fact).is_some() {
1422                fact
1423            } else {
1424                let desc = native_test_desc_argument(fact, "run-fact")?;
1425                let namespace = native_test_current_namespace()?;
1426                let fact = native_test_lookup(&namespace, &desc)?;
1427                if matches!(fact, Value::Nil) {
1428                    return Err(format!(
1429                        "std.native.Test/run-fact fact not found: {}",
1430                        desc.display()
1431                    ));
1432                }
1433                fact
1434            };
1435            native_test_run_fact(fact, options)
1436        }
1437        "run" => {
1438            if values.len() > 1 {
1439                return Err(
1440                    "std.native.Test/run expects an optional options map; use Test/check for cases"
1441                        .into(),
1442                );
1443            }
1444            let options = values
1445                .into_iter()
1446                .next()
1447                .unwrap_or_else(|| Value::Map(PMap::new()));
1448            native_test_run(options)
1449        }
1450        "summary" => match values.as_slice() {
1451            [Value::Vector(results)] => native_test_summary(results.iter().cloned().collect()),
1452            [Value::Tuple(results)] => native_test_summary(results.iter().cloned().collect()),
1453            [_] => Err("std.native.Test/summary expects a vector of fact results".into()),
1454            _ => Err("std.native.Test/summary expects one vector of fact results".into()),
1455        },
1456        "passed?" => {
1457            if values.len() != 1 {
1458                return Err("std.native.Test/passed? expects one result".into());
1459            }
1460            let result = native_test_require_result(values[0].clone(), "passed?")?;
1461            Ok(Value::Bool(
1462                result.is_success() && matches!(result.data, Value::Bool(true)),
1463            ))
1464        }
1465        "actual" | "expected" | "failures" | "failure-seq" | "failure-count" => {
1466            if values.len() != 1 {
1467                return Err(format!("std.native.Test/{operation} expects one Result"));
1468            }
1469            let result = native_test_require_result(values[0].clone(), operation)?;
1470            Ok(match operation {
1471                "actual" => native_test_detail(&result, "actual"),
1472                "expected" => native_test_detail(&result, "expected"),
1473                "failures" => native_test_failures(&result),
1474                "failure-seq" => native_test_failure_seq(&result),
1475                "failure-count" => match native_test_failure_seq(&result) {
1476                    Value::Vector(values) => Value::Number(values.len() as i64),
1477                    _ => unreachable!(),
1478                },
1479                _ => unreachable!(),
1480            })
1481        }
1482        "failure" => {
1483            if values.len() != 2 {
1484                return Err("std.native.Test/failure expects a Result and index".into());
1485            }
1486            let result = native_test_require_result(values[0].clone(), "failure")?;
1487            let index = match &values[1] {
1488                Value::Number(index) if *index >= 0 => *index as usize,
1489                _ => {
1490                    return Err(
1491                        "std.native.Test/failure index must be a non-negative integer".into(),
1492                    )
1493                }
1494            };
1495            match native_test_failure_seq(&result) {
1496                Value::Vector(values) => Ok(values.get(index).cloned().unwrap_or(Value::Nil)),
1497                _ => unreachable!(),
1498            }
1499        }
1500        "failure?" => {
1501            if values.len() != 1 {
1502                return Err("std.native.Test/failure? expects one value".into());
1503            }
1504            Ok(Value::Bool(native_test_failure_shape(&values[0])))
1505        }
1506        _ => Err(format!("unknown std.native.Test operation: {operation}")),
1507    }
1508}
1509
1510struct NativeCommandRequest {
1511    app: u64,
1512    request: crate::command::Request,
1513    context: Value,
1514}
1515
1516struct NativeCommandSnapshot {
1517    app: u64,
1518    snapshot: crate::command::Snapshot<Value>,
1519}
1520
1521struct NativeCommandState {
1522    next_app: u64,
1523    next_request: u64,
1524    next_snapshot: u64,
1525    apps: HashMap<u64, crate::command::App<Value>>,
1526    requests: HashMap<u64, NativeCommandRequest>,
1527    snapshots: HashMap<u64, NativeCommandSnapshot>,
1528}
1529
1530impl Default for NativeCommandState {
1531    fn default() -> Self {
1532        Self {
1533            next_app: 1,
1534            next_request: 1,
1535            next_snapshot: 1,
1536            apps: HashMap::new(),
1537            requests: HashMap::new(),
1538            snapshots: HashMap::new(),
1539        }
1540    }
1541}
1542
1543thread_local! {
1544    static NATIVE_COMMAND_STATE: RefCell<NativeCommandState> = RefCell::new(NativeCommandState::default());
1545}
1546
1547fn native_command_keyword(name: &str) -> Value {
1548    Value::Keyword(Keyword::from(name))
1549}
1550
1551fn native_command_map(entries: impl IntoIterator<Item = (Value, Value)>) -> Value {
1552    Value::Map(PMap::from_iter(entries))
1553}
1554
1555fn native_command_pointer(context: &str, entries: impl IntoIterator<Item = (Value, Value)>) -> Value {
1556    Value::Pointer(PPointer::new(
1557        Keyword::from(context),
1558        PMap::from_iter(entries),
1559    ))
1560}
1561
1562fn native_command_handle(value: &Value, context: &str, operation: &str) -> Result<u64, String> {
1563    let Value::Pointer(pointer) = value else {
1564        return Err(format!("std.native.Command/{operation} expects a {context} handle"));
1565    };
1566    if pointer.context().as_str() != context {
1567        return Err(format!("std.native.Command/{operation} expects a {context} handle"));
1568    }
1569    match pointer.get(&native_command_keyword("id")) {
1570        Some(Value::Number(id)) if *id > 0 => Ok(*id as u64),
1571        _ => Err(format!("std.native.Command/{operation} received an invalid {context} handle")),
1572    }
1573}
1574
1575fn native_command_app(value: &Value, operation: &str) -> Result<u64, String> {
1576    native_command_handle(value, "command/app", operation)
1577}
1578
1579fn native_command_route(value: &Value, app: u64, operation: &str) -> Result<crate::command::RouteHandle, String> {
1580    let route = native_command_handle(value, "command/route", operation)?;
1581    let Value::Pointer(pointer) = value else {
1582        unreachable!()
1583    };
1584    match pointer.get(&native_command_keyword("app")) {
1585        Some(Value::Number(candidate)) if *candidate == app as i64 => {
1586            Ok(crate::command::RouteHandle::from_id(route))
1587        }
1588        _ => Err(format!("std.native.Command/{operation} route belongs to a different application")),
1589    }
1590}
1591
1592fn native_command_config_argument(value: &Value, operation: &str) -> Result<crate::command::AppConfig, String> {
1593    let Some(entries) = map_entries(value) else {
1594        return Err(format!("std.native.Command/{operation} expects a config map"));
1595    };
1596    let config = Value::Map(PMap::from_iter(entries));
1597    let id = match map_value(&config, &native_command_keyword("id")) {
1598        Some(Value::Symbol(id)) if !id.as_str().is_empty() => id.as_str().to_owned(),
1599        _ => return Err(format!("std.native.Command/{operation} config :id must be a symbol")),
1600    };
1601    let desc = match map_value(&config, &native_command_keyword("desc")) {
1602        Some(Value::String(desc)) if !desc.trim().is_empty() => desc.clone(),
1603        _ => return Err(format!("std.native.Command/{operation} config :desc must be a non-empty string")),
1604    };
1605    Ok(crate::command::AppConfig { id, desc })
1606}
1607
1608fn native_command_vector(value: &Value, operation: &str, field: &str) -> Result<Vec<Value>, String> {
1609    match value {
1610        Value::Vector(values) => Ok(values.iter().cloned().collect()),
1611        Value::Tuple(values) => Ok(values.iter().cloned().collect()),
1612        _ => Err(format!("std.native.Command/{operation} {field} must be a vector")),
1613    }
1614}
1615
1616fn native_command_string(value: &Value, operation: &str, field: &str) -> Result<String, String> {
1617    match value {
1618        Value::String(value) => Ok(value.clone()),
1619        _ => Err(format!("std.native.Command/{operation} {field} must be a string")),
1620    }
1621}
1622
1623fn native_command_strings(value: &Value, operation: &str, field: &str) -> Result<Vec<String>, String> {
1624    native_command_vector(value, operation, field)?
1625        .into_iter()
1626        .map(|value| native_command_string(&value, operation, field))
1627        .collect()
1628}
1629
1630fn native_command_keyword_argument(value: &Value, operation: &str, field: &str) -> Result<String, String> {
1631    match value {
1632        Value::Keyword(value) if !value.as_str().is_empty() => Ok(value.as_str().to_owned()),
1633        _ => Err(format!("std.native.Command/{operation} {field} must be a keyword")),
1634    }
1635}
1636
1637fn native_command_boolean(value: Option<&Value>, operation: &str, field: &str, fallback: bool) -> Result<bool, String> {
1638    match value {
1639        None | Some(Value::Nil) => Ok(fallback),
1640        Some(Value::Bool(value)) => Ok(*value),
1641        _ => Err(format!("std.native.Command/{operation} {field} must be a boolean")),
1642    }
1643}
1644
1645fn native_command_option(value: Value, operation: &str) -> Result<crate::command::OptionSpec, String> {
1646    let Some(entries) = map_entries(&value) else {
1647        return Err(format!("std.native.Command/{operation} :options entries must be maps"));
1648    };
1649    let value = Value::Map(PMap::from_iter(entries));
1650    let id = native_command_keyword_argument(
1651        map_value(&value, &native_command_keyword("id"))
1652            .ok_or_else(|| format!("std.native.Command/{operation} option :id is required"))?,
1653        operation,
1654        "option :id",
1655    )?;
1656    let long = match map_value(&value, &native_command_keyword("long")) {
1657        None | Some(Value::Nil) => None,
1658        Some(value) => Some(native_command_string(value, operation, "option :long")?),
1659    };
1660    let short = match map_value(&value, &native_command_keyword("short")) {
1661        None | Some(Value::Nil) => None,
1662        Some(Value::String(value)) if value.chars().count() == 1 => value.chars().next(),
1663        _ => return Err(format!("std.native.Command/{operation} option :short must be one character")),
1664    };
1665    let kind = match map_value(&value, &native_command_keyword("type")) {
1666        Some(Value::Keyword(kind)) if kind.as_str() == "boolean" => crate::command::OptionKind::Boolean,
1667        Some(Value::Keyword(kind)) if kind.as_str() == "string" => crate::command::OptionKind::String,
1668        _ => return Err(format!("std.native.Command/{operation} option :type must be :boolean or :string")),
1669    };
1670    let many = native_command_boolean(
1671        map_value(&value, &native_command_keyword("many?")),
1672        operation,
1673        "option :many?",
1674        false,
1675    )?;
1676    let default = match map_value(&value, &native_command_keyword("default")) {
1677        None | Some(Value::Nil) => None,
1678        Some(Value::Bool(value)) => Some(crate::command::ParsedValue::Boolean(*value)),
1679        Some(Value::String(value)) => Some(crate::command::ParsedValue::String(value.clone())),
1680        Some(value) => Some(crate::command::ParsedValue::Strings(native_command_strings(
1681            value,
1682            operation,
1683            "option :default",
1684        )?)),
1685    };
1686    Ok(crate::command::OptionSpec {
1687        id,
1688        long,
1689        short,
1690        kind,
1691        many,
1692        default,
1693    })
1694}
1695
1696fn native_command_argument(value: Value, operation: &str) -> Result<crate::command::ArgumentSpec, String> {
1697    let Some(entries) = map_entries(&value) else {
1698        return Err(format!("std.native.Command/{operation} :arguments entries must be maps"));
1699    };
1700    let value = Value::Map(PMap::from_iter(entries));
1701    Ok(crate::command::ArgumentSpec {
1702        id: native_command_keyword_argument(
1703            map_value(&value, &native_command_keyword("id"))
1704                .ok_or_else(|| format!("std.native.Command/{operation} argument :id is required"))?,
1705            operation,
1706            "argument :id",
1707        )?,
1708        required: native_command_boolean(
1709            map_value(&value, &native_command_keyword("required?")),
1710            operation,
1711            "argument :required?",
1712            true,
1713        )?,
1714        many: native_command_boolean(
1715            map_value(&value, &native_command_keyword("many?")),
1716            operation,
1717            "argument :many?",
1718            false,
1719        )?,
1720    })
1721}
1722
1723fn native_command_route_argument(value: Value, operation: &str) -> Result<crate::command::Route<Value>, String> {
1724    let Some(entries) = map_entries(&value) else {
1725        return Err(format!("std.native.Command/{operation} expects a route map"));
1726    };
1727    let value = Value::Map(PMap::from_iter(entries));
1728    let path = native_command_strings(
1729        map_value(&value, &native_command_keyword("path"))
1730            .ok_or_else(|| format!("std.native.Command/{operation} route :path is required"))?,
1731        operation,
1732        "route :path",
1733    )?;
1734    let aliases = match map_value(&value, &native_command_keyword("aliases")) {
1735        None | Some(Value::Nil) => Vec::new(),
1736        Some(value) => native_command_vector(value, operation, "route :aliases")?
1737            .into_iter()
1738            .map(|alias| native_command_strings(&alias, operation, "route :aliases"))
1739            .collect::<Result<Vec<_>, _>>()?,
1740    };
1741    let options = match map_value(&value, &native_command_keyword("options")) {
1742        None | Some(Value::Nil) => Vec::new(),
1743        Some(value) => native_command_vector(value, operation, "route :options")?
1744            .into_iter()
1745            .map(|value| native_command_option(value, operation))
1746            .collect::<Result<Vec<_>, _>>()?,
1747    };
1748    let arguments = match map_value(&value, &native_command_keyword("arguments")) {
1749        None | Some(Value::Nil) => Vec::new(),
1750        Some(value) => native_command_vector(value, operation, "route :arguments")?
1751            .into_iter()
1752            .map(|value| native_command_argument(value, operation))
1753            .collect::<Result<Vec<_>, _>>()?,
1754    };
1755    let handler = match map_value(&value, &native_command_keyword("handler")) {
1756        Some(Value::Function(function)) => Value::Function(function.clone()),
1757        _ => return Err(format!("std.native.Command/{operation} route :handler must be a function")),
1758    };
1759    Ok(crate::command::Route {
1760        spec: crate::command::RouteSpec {
1761            id: native_command_keyword_argument(
1762                map_value(&value, &native_command_keyword("id"))
1763                    .ok_or_else(|| format!("std.native.Command/{operation} route :id is required"))?,
1764                operation,
1765                "route :id",
1766            )?,
1767            path,
1768            aliases,
1769            desc: native_command_string(
1770                map_value(&value, &native_command_keyword("desc"))
1771                    .ok_or_else(|| format!("std.native.Command/{operation} route :desc is required"))?,
1772                operation,
1773                "route :desc",
1774            )?,
1775            options,
1776            arguments,
1777            passthrough: native_command_boolean(
1778                map_value(&value, &native_command_keyword("passthrough?")),
1779                operation,
1780                "route :passthrough?",
1781                false,
1782            )?,
1783        },
1784        handler,
1785    })
1786}
1787
1788fn native_command_parsed_value(value: &crate::command::ParsedValue) -> Value {
1789    match value {
1790        crate::command::ParsedValue::Boolean(value) => Value::Bool(*value),
1791        crate::command::ParsedValue::String(value) => Value::String(value.clone()),
1792        crate::command::ParsedValue::Strings(values) => {
1793            Value::Vector(PVector::from_iter(values.iter().cloned().map(Value::String)))
1794        }
1795    }
1796}
1797
1798fn native_command_spec_value(spec: &crate::command::RouteSpec) -> Value {
1799    native_command_map([
1800        (native_command_keyword("id"), native_command_keyword(&spec.id)),
1801        (
1802            native_command_keyword("path"),
1803            Value::Vector(PVector::from_iter(spec.path.iter().cloned().map(Value::String))),
1804        ),
1805        (
1806            native_command_keyword("aliases"),
1807            Value::Vector(PVector::from_iter(spec.aliases.iter().map(|alias| {
1808                Value::Vector(PVector::from_iter(alias.iter().cloned().map(Value::String)))
1809            }))),
1810        ),
1811        (native_command_keyword("desc"), Value::String(spec.desc.clone())),
1812        (
1813            native_command_keyword("passthrough?"),
1814            Value::Bool(spec.passthrough),
1815        ),
1816        (
1817            native_command_keyword("options"),
1818            Value::Vector(PVector::from_iter(spec.options.iter().map(|option| {
1819                let mut entries = vec![
1820                    (native_command_keyword("id"), native_command_keyword(&option.id)),
1821                    (native_command_keyword("long"), Value::String(option.long_name())),
1822                    (
1823                        native_command_keyword("short"),
1824                        option
1825                            .short
1826                            .map(|short| Value::String(short.into()))
1827                            .unwrap_or(Value::Nil),
1828                    ),
1829                    (
1830                        native_command_keyword("type"),
1831                        native_command_keyword(match option.kind {
1832                            crate::command::OptionKind::Boolean => "boolean",
1833                            crate::command::OptionKind::String => "string",
1834                        }),
1835                    ),
1836                    (native_command_keyword("many?"), Value::Bool(option.many)),
1837                ];
1838                if let Some(default) = &option.default {
1839                    entries.push((native_command_keyword("default"), native_command_parsed_value(default)));
1840                }
1841                native_command_map(entries)
1842            }))),
1843        ),
1844        (
1845            native_command_keyword("arguments"),
1846            Value::Vector(PVector::from_iter(spec.arguments.iter().map(|argument| {
1847                native_command_map([
1848                    (native_command_keyword("id"), native_command_keyword(&argument.id)),
1849                    (native_command_keyword("required?"), Value::Bool(argument.required)),
1850                    (native_command_keyword("many?"), Value::Bool(argument.many)),
1851                ])
1852            }))),
1853        ),
1854    ])
1855}
1856
1857fn native_command_invocation(value: Value, operation: &str) -> Result<(Vec<String>, Value), String> {
1858    let Some(entries) = map_entries(&value) else {
1859        return Err(format!("std.native.Command/{operation} expects an invocation map"));
1860    };
1861    let value = Value::Map(PMap::from_iter(entries));
1862    let argv = native_command_strings(
1863        map_value(&value, &native_command_keyword("argv"))
1864            .ok_or_else(|| format!("std.native.Command/{operation} invocation :argv is required"))?,
1865        operation,
1866        "invocation :argv",
1867    )?;
1868    let context = map_value(&value, &native_command_keyword("context"))
1869        .cloned()
1870        .unwrap_or_else(|| Value::Map(PMap::new()));
1871    if map_entries(&context).is_none() {
1872        return Err(format!("std.native.Command/{operation} invocation :context must be a map"));
1873    }
1874    Ok((argv, context))
1875}
1876
1877fn native_command_request_value(request_id: u64, request: &crate::command::Request, context: Value) -> Value {
1878    native_command_map([
1879        (
1880            native_command_keyword("app/id"),
1881            Value::Symbol(Symbol::parse(&request.app_id)),
1882        ),
1883        (native_command_keyword("route/id"), native_command_keyword(&request.route_id)),
1884        (
1885            native_command_keyword("route/path"),
1886            Value::Vector(PVector::from_iter(request.route_path.iter().cloned().map(Value::String))),
1887        ),
1888        (
1889            native_command_keyword("argv"),
1890            Value::Vector(PVector::from_iter(request.argv.iter().cloned().map(Value::String))),
1891        ),
1892        (
1893            native_command_keyword("arguments"),
1894            native_command_map(request.arguments.iter().map(|(key, value)| {
1895                (native_command_keyword(key), native_command_parsed_value(value))
1896            })),
1897        ),
1898        (
1899            native_command_keyword("options"),
1900            native_command_map(request.options.iter().map(|(key, value)| {
1901                (native_command_keyword(key), native_command_parsed_value(value))
1902            })),
1903        ),
1904        (native_command_keyword("context"), context),
1905        (
1906            native_command_keyword("command/request"),
1907            native_command_pointer(
1908                "command/request",
1909                [(native_command_keyword("id"), Value::Number(request_id as i64))],
1910            ),
1911        ),
1912    ])
1913}
1914
1915fn native_command_response_value(response: crate::command::Response) -> Value {
1916    native_command_map([
1917        (native_command_keyword("stdout"), Value::String(response.stdout)),
1918        (native_command_keyword("stderr"), Value::String(response.stderr)),
1919        (native_command_keyword("exit"), Value::Number(response.exit)),
1920    ])
1921}
1922
1923fn native_command_response_argument(value: Value, operation: &str) -> Result<crate::command::Response, String> {
1924    let Some(entries) = map_entries(&value) else {
1925        return Err(format!("std.native.Command/{operation} handler must return a response map"));
1926    };
1927    if entries.len() != 3 {
1928        return Err(format!("std.native.Command/{operation} response must contain only :stdout, :stderr, and :exit"));
1929    }
1930    let value = Value::Map(PMap::from_iter(entries));
1931    let stdout = native_command_string(
1932        map_value(&value, &native_command_keyword("stdout"))
1933            .ok_or_else(|| format!("std.native.Command/{operation} response :stdout is required"))?,
1934        operation,
1935        "response :stdout",
1936    )?;
1937    let stderr = native_command_string(
1938        map_value(&value, &native_command_keyword("stderr"))
1939            .ok_or_else(|| format!("std.native.Command/{operation} response :stderr is required"))?,
1940        operation,
1941        "response :stderr",
1942    )?;
1943    let exit = match map_value(&value, &native_command_keyword("exit")) {
1944        Some(Value::Number(value)) => *value,
1945        _ => return Err(format!("std.native.Command/{operation} response :exit must be an integer")),
1946    };
1947    crate::command::Response { stdout, stderr, exit }
1948        .checked()
1949        .map_err(|error| error.to_string())
1950}
1951
1952fn native_command_parse(app: u64, invocation: Value, operation: &str) -> Result<Value, String> {
1953    let (argv, context) = native_command_invocation(invocation, operation)?;
1954    NATIVE_COMMAND_STATE.with(|state| {
1955        let mut state = state.borrow_mut();
1956        let request = state
1957            .apps
1958            .get(&app)
1959            .ok_or_else(|| format!("std.native.Command/{operation} application was not found"))?
1960            .parse(argv)
1961            .map_err(|error| error.to_string())?;
1962        let request_id = state.next_request;
1963        state.next_request += 1;
1964        state.requests.insert(
1965            request_id,
1966            NativeCommandRequest {
1967                app,
1968                request: request.clone(),
1969                context: context.clone(),
1970            },
1971        );
1972        Ok(native_command_request_value(request_id, &request, context))
1973    })
1974}
1975
1976fn native_command_request_id(value: &Value, operation: &str) -> Result<u64, String> {
1977    let Some(request) = map_value(value, &native_command_keyword("command/request")) else {
1978        return Err(format!("std.native.Command/{operation} expects a Command/parse request"));
1979    };
1980    native_command_handle(request, "command/request", operation)
1981}
1982
1983fn native_command_dispatch(app: u64, request_value: Value, operation: &str) -> Result<Value, String> {
1984    let request_id = native_command_request_id(&request_value, operation)?;
1985    let (handler, request, context) = NATIVE_COMMAND_STATE.with(|state| {
1986        let state = state.borrow();
1987        let stored = state
1988            .requests
1989            .get(&request_id)
1990            .ok_or_else(|| format!("std.native.Command/{operation} request was not found"))?;
1991        if stored.app != app {
1992            return Err(format!("std.native.Command/{operation} request belongs to a different application"));
1993        }
1994        let application = state
1995            .apps
1996            .get(&app)
1997            .ok_or_else(|| format!("std.native.Command/{operation} application was not found"))?;
1998        let handler = application
1999            .handler(&stored.request)
2000            .map_err(|error| error.to_string())?
2001            .clone();
2002        Ok::<_, String>((handler, stored.request.clone(), stored.context.clone()))
2003    })?;
2004    let output = call_value(handler, vec![native_command_request_value(request_id, &request, context)])?;
2005    native_command_response_argument(output, operation).map(native_command_response_value)
2006}
2007
2008fn native_command_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
2009    let operation = operation
2010        .strip_prefix("std.native.Command/")
2011        .unwrap_or(operation);
2012    match operation {
2013        "create" => match values.as_slice() {
2014            [config] => {
2015                let config = native_command_config_argument(config, operation)?;
2016                NATIVE_COMMAND_STATE.with(|state| {
2017                    let mut state = state.borrow_mut();
2018                    let id = state.next_app;
2019                    state.next_app += 1;
2020                    state
2021                        .apps
2022                        .insert(id, crate::command::App::create(config).map_err(|error| error.to_string())?);
2023                    Ok(native_command_pointer(
2024                        "command/app",
2025                        [(native_command_keyword("id"), Value::Number(id as i64))],
2026                    ))
2027                })
2028            }
2029            _ => Err("std.native.Command/create expects one config map".into()),
2030        },
2031        "config" => match values.as_slice() {
2032            [app] => {
2033                let app = native_command_app(app, operation)?;
2034                NATIVE_COMMAND_STATE.with(|state| {
2035                    let state = state.borrow();
2036                    let config = state
2037                        .apps
2038                        .get(&app)
2039                        .ok_or_else(|| "std.native.Command/config application was not found".to_owned())?
2040                        .config();
2041                    Ok(native_command_map([
2042                        (native_command_keyword("id"), Value::Symbol(Symbol::parse(&config.id))),
2043                        (native_command_keyword("desc"), Value::String(config.desc)),
2044                    ]))
2045                })
2046            }
2047            _ => Err("std.native.Command/config expects one application".into()),
2048        },
2049        "install" => match values.as_slice() {
2050            [app, route] => {
2051                let app = native_command_app(app, operation)?;
2052                let route = native_command_route_argument(route.clone(), operation)?;
2053                NATIVE_COMMAND_STATE.with(|state| {
2054                    let mut state = state.borrow_mut();
2055                    let handle = state
2056                        .apps
2057                        .get_mut(&app)
2058                        .ok_or_else(|| "std.native.Command/install application was not found".to_owned())?
2059                        .install(route)
2060                        .map_err(|error| error.to_string())?;
2061                    Ok(native_command_pointer(
2062                        "command/route",
2063                        [
2064                            (native_command_keyword("id"), Value::Number(handle.id() as i64)),
2065                            (native_command_keyword("app"), Value::Number(app as i64)),
2066                        ],
2067                    ))
2068                })
2069            }
2070            _ => Err("std.native.Command/install expects an application and route map".into()),
2071        },
2072        "uninstall" => match values.as_slice() {
2073            [app, route] => {
2074                let app = native_command_app(app, operation)?;
2075                let route = native_command_route(route, app, operation)?;
2076                NATIVE_COMMAND_STATE.with(|state| {
2077                    state
2078                        .borrow_mut()
2079                        .apps
2080                        .get_mut(&app)
2081                        .ok_or_else(|| "std.native.Command/uninstall application was not found".to_owned())?
2082                        .uninstall(route)
2083                        .map(Value::Bool)
2084                        .map_err(|error| error.to_string())
2085                })
2086            }
2087            _ => Err("std.native.Command/uninstall expects an application and route handle".into()),
2088        },
2089        "routes" => match values.as_slice() {
2090            [app] => {
2091                let app = native_command_app(app, operation)?;
2092                NATIVE_COMMAND_STATE.with(|state| {
2093                    state
2094                        .borrow()
2095                        .apps
2096                        .get(&app)
2097                        .ok_or_else(|| "std.native.Command/routes application was not found".to_owned())?
2098                        .routes()
2099                        .map(|routes| Value::Vector(PVector::from_iter(routes.iter().map(native_command_spec_value))))
2100                        .map_err(|error| error.to_string())
2101                })
2102            }
2103            _ => Err("std.native.Command/routes expects one application".into()),
2104        },
2105        "snapshot" => match values.as_slice() {
2106            [app] => {
2107                let app = native_command_app(app, operation)?;
2108                NATIVE_COMMAND_STATE.with(|state| {
2109                    let mut state = state.borrow_mut();
2110                    let snapshot = state
2111                        .apps
2112                        .get(&app)
2113                        .ok_or_else(|| "std.native.Command/snapshot application was not found".to_owned())?
2114                        .snapshot()
2115                        .map_err(|error| error.to_string())?;
2116                    let id = state.next_snapshot;
2117                    state.next_snapshot += 1;
2118                    state.snapshots.insert(id, NativeCommandSnapshot { app, snapshot });
2119                    Ok(native_command_pointer(
2120                        "command/snapshot",
2121                        [
2122                            (native_command_keyword("id"), Value::Number(id as i64)),
2123                            (native_command_keyword("app"), Value::Number(app as i64)),
2124                        ],
2125                    ))
2126                })
2127            }
2128            _ => Err("std.native.Command/snapshot expects one application".into()),
2129        },
2130        "restore" => match values.as_slice() {
2131            [app, snapshot] => {
2132                let app = native_command_app(app, operation)?;
2133                let snapshot_id = native_command_handle(snapshot, "command/snapshot", operation)?;
2134                NATIVE_COMMAND_STATE.with(|state| {
2135                    let mut state = state.borrow_mut();
2136                    let snapshot = state
2137                        .snapshots
2138                        .get(&snapshot_id)
2139                        .ok_or_else(|| "std.native.Command/restore snapshot was not found".to_owned())?;
2140                    if snapshot.app != app {
2141                        return Err("std.native.Command/restore snapshot belongs to a different application".into());
2142                    }
2143                    let snapshot = snapshot.snapshot.clone();
2144                    state
2145                        .apps
2146                        .get_mut(&app)
2147                        .ok_or_else(|| "std.native.Command/restore application was not found".to_owned())?
2148                        .restore(snapshot)
2149                        .map_err(|error| error.to_string())?;
2150                    Ok(values[0].clone())
2151                })
2152            }
2153            _ => Err("std.native.Command/restore expects an application and snapshot".into()),
2154        },
2155        "reset" => match values.as_slice() {
2156            [app] => {
2157                let app = native_command_app(app, operation)?;
2158                NATIVE_COMMAND_STATE.with(|state| {
2159                    let mut state = state.borrow_mut();
2160                    state
2161                        .apps
2162                        .get_mut(&app)
2163                        .ok_or_else(|| "std.native.Command/reset application was not found".to_owned())?
2164                        .reset()
2165                        .map_err(|error| error.to_string())?;
2166                    state.requests.retain(|_, request| request.app != app);
2167                    Ok(values[0].clone())
2168                })
2169            }
2170            _ => Err("std.native.Command/reset expects one application".into()),
2171        },
2172        "closed?" => match values.as_slice() {
2173            [app] => {
2174                let app = native_command_app(app, operation)?;
2175                NATIVE_COMMAND_STATE.with(|state| {
2176                    state
2177                        .borrow()
2178                        .apps
2179                        .get(&app)
2180                        .map(|app| Value::Bool(app.closed()))
2181                        .ok_or_else(|| "std.native.Command/closed? application was not found".to_owned())
2182                })
2183            }
2184            _ => Err("std.native.Command/closed? expects one application".into()),
2185        },
2186        "close" => match values.as_slice() {
2187            [app] => {
2188                let app = native_command_app(app, operation)?;
2189                NATIVE_COMMAND_STATE.with(|state| {
2190                    let mut state = state.borrow_mut();
2191                    state
2192                        .apps
2193                        .get_mut(&app)
2194                        .ok_or_else(|| "std.native.Command/close application was not found".to_owned())?
2195                        .close();
2196                    state.requests.retain(|_, request| request.app != app);
2197                    Ok(Value::Nil)
2198                })
2199            }
2200            _ => Err("std.native.Command/close expects one application".into()),
2201        },
2202        "parse" => match values.as_slice() {
2203            [app, invocation] => native_command_parse(native_command_app(app, operation)?, invocation.clone(), operation),
2204            _ => Err("std.native.Command/parse expects an application and invocation map".into()),
2205        },
2206        "dispatch" => match values.as_slice() {
2207            [app, request] => native_command_dispatch(native_command_app(app, operation)?, request.clone(), operation),
2208            _ => Err("std.native.Command/dispatch expects an application and parsed request".into()),
2209        },
2210        "run" => match values.as_slice() {
2211            [app, invocation] => {
2212                let app = native_command_app(app, operation)?;
2213                match native_command_parse(app, invocation.clone(), operation) {
2214                    Ok(request) => match native_command_dispatch(app, request, operation) {
2215                        Ok(response) => Ok(response),
2216                        Err(error) => Ok(native_command_response_value(crate::command::Response::failure(1, error))),
2217                    },
2218                    Err(error) => Ok(native_command_response_value(crate::command::Response::failure(2, error))),
2219                }
2220            }
2221            _ => Err("std.native.Command/run expects an application and invocation map".into()),
2222        },
2223        _ => Err(format!("unknown std.native.Command operation: {operation}")),
2224    }
2225}
2226
2227fn native_regex_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
2228    let operation = operation
2229        .strip_prefix("std.native.RegExp/")
2230        .unwrap_or(operation);
2231    match operation {
2232        "compile" => {
2233            if values.len() != 1 {
2234                return Err("std.native.RegExp/compile expects one string".into());
2235            }
2236            let pattern = match &values[0] {
2237                Value::String(pattern) => pattern.clone(),
2238                _ => return Err("std.native.RegExp/compile expects one string".into()),
2239            };
2240            regex::Regex::new(&pattern).map_err(|error| format!("invalid regexp: {error}"))?;
2241            Ok(Value::Regex(pattern))
2242        }
2243        "pattern" => {
2244            if values.len() != 1 {
2245                return Err("std.native.RegExp/pattern expects one regexp".into());
2246            }
2247            match &values[0] {
2248                Value::Regex(pattern) => Ok(Value::String(pattern.clone())),
2249                _ => Err("std.native.RegExp/pattern expects one regexp".into()),
2250            }
2251        }
2252        "find?" => {
2253            if values.len() != 2 {
2254                return Err("std.native.RegExp/find? expects a regexp and string".into());
2255            }
2256            let pattern = match &values[0] {
2257                Value::Regex(pattern) => pattern.clone(),
2258                _ => return Err("std.native.RegExp/find? expects a regexp and string".into()),
2259            };
2260            let input = match &values[1] {
2261                Value::String(input) => input.clone(),
2262                _ => return Err("std.native.RegExp/find? expects a regexp and string".into()),
2263            };
2264            let regexp =
2265                regex::Regex::new(&pattern).map_err(|error| format!("invalid regexp: {error}"))?;
2266            Ok(Value::Bool(regexp.is_match(&input)))
2267        }
2268        "find" => {
2269            if values.len() != 2 {
2270                return Err("std.native.RegExp/find expects a regexp and string".into());
2271            }
2272            let pattern = match &values[0] {
2273                Value::Regex(pattern) => pattern.clone(),
2274                _ => return Err("std.native.RegExp/find expects a regexp and string".into()),
2275            };
2276            let input = match &values[1] {
2277                Value::String(input) => input.clone(),
2278                _ => return Err("std.native.RegExp/find expects a regexp and string".into()),
2279            };
2280            let regexp =
2281                regex::Regex::new(&pattern).map_err(|error| format!("invalid regexp: {error}"))?;
2282            Ok(regexp
2283                .find(&input)
2284                .map(|matched| Value::String(matched.as_str().to_owned()))
2285                .unwrap_or(Value::Nil))
2286        }
2287        "matches" => {
2288            if values.len() != 2 {
2289                return Err("std.native.RegExp/matches expects a regexp and string".into());
2290            }
2291            let pattern = match &values[0] {
2292                Value::Regex(pattern) => pattern.clone(),
2293                _ => return Err("std.native.RegExp/matches expects a regexp and string".into()),
2294            };
2295            let input = match &values[1] {
2296                Value::String(input) => input.clone(),
2297                _ => return Err("std.native.RegExp/matches expects a regexp and string".into()),
2298            };
2299            let anchored = format!(r"\A(?:{pattern})\z");
2300            let regexp =
2301                regex::Regex::new(&anchored).map_err(|error| format!("invalid regexp: {error}"))?;
2302            Ok(Value::Bool(regexp.is_match(&input)))
2303        }
2304        "replace" => {
2305            if values.len() != 3 {
2306                return Err(
2307                    "std.native.RegExp/replace expects a regexp, string, and replacement".into(),
2308                );
2309            }
2310            let pattern = match &values[0] {
2311                Value::Regex(pattern) => pattern.clone(),
2312                _ => {
2313                    return Err(
2314                        "std.native.RegExp/replace expects a regexp, string, and replacement"
2315                            .into(),
2316                    )
2317                }
2318            };
2319            let input = match &values[1] {
2320                Value::String(input) => input.clone(),
2321                _ => {
2322                    return Err(
2323                        "std.native.RegExp/replace expects a regexp, string, and replacement"
2324                            .into(),
2325                    )
2326                }
2327            };
2328            let replacement = match &values[2] {
2329                Value::String(replacement) => replacement.clone(),
2330                _ => {
2331                    return Err(
2332                        "std.native.RegExp/replace expects a regexp, string, and replacement"
2333                            .into(),
2334                    )
2335                }
2336            };
2337            let regexp =
2338                regex::Regex::new(&pattern).map_err(|error| format!("invalid regexp: {error}"))?;
2339            Ok(Value::String(
2340                regexp
2341                    .replace_all(&input, replacement.as_str())
2342                    .into_owned(),
2343            ))
2344        }
2345        "split" => {
2346            if values.len() != 2 {
2347                return Err("std.native.RegExp/split expects a regexp and string".into());
2348            }
2349            let pattern = match &values[0] {
2350                Value::Regex(pattern) => pattern.clone(),
2351                _ => return Err("std.native.RegExp/split expects a regexp and string".into()),
2352            };
2353            let input = match &values[1] {
2354                Value::String(input) => input.clone(),
2355                _ => return Err("std.native.RegExp/split expects a regexp and string".into()),
2356            };
2357            if input.is_empty() {
2358                return Ok(Value::Nil);
2359            }
2360            if pattern.is_empty() {
2361                return Ok(Value::Vector(PVector::from_iter(
2362                    input
2363                        .chars()
2364                        .map(|character| Value::String(character.to_string())),
2365                )));
2366            }
2367            let regexp =
2368                regex::Regex::new(&pattern).map_err(|error| format!("invalid regexp: {error}"))?;
2369            Ok(Value::Vector(PVector::from_iter(
2370                regexp
2371                    .split(&input)
2372                    .map(|part| Value::String(part.to_owned())),
2373            )))
2374        }
2375        _ => Err(format!("unknown std.native.RegExp operation: {operation}")),
2376    }
2377}
2378
2379fn file_error(operation: &str, error: FileError) -> String {
2380    let method = operation
2381        .strip_prefix("std.native.File/")
2382        .unwrap_or(operation);
2383    format!("file/{method} failed: file/{}", error.code())
2384}
2385
2386fn socket_error(operation: &str, error: SocketError) -> String {
2387    format!("{operation} failed: socket/{}", error.code())
2388}
2389
2390fn active_file_provider() -> Option<Rc<dyn FileProvider>> {
2391    ACTIVE_FILE_PROVIDER.with(|active| active.borrow().clone())
2392}
2393
2394fn rejected_file_effect(
2395    operation: &str,
2396    path: &str,
2397    target: Option<&str>,
2398    error: FileError,
2399) -> Value {
2400    let promise = Promise::new();
2401    promise.reject_value(crate::file::file_error_value(
2402        operation, path, target, &error,
2403    ));
2404    Value::Promise(promise)
2405}
2406
2407fn file_effect(
2408    operation: &str,
2409    path: &str,
2410    target: Option<&str>,
2411    invoke: impl FnOnce(&dyn FileProvider) -> Result<Promise, FileError>,
2412) -> Value {
2413    let Some(provider) = active_file_provider() else {
2414        return rejected_file_effect(operation, path, target, FileError::Denied);
2415    };
2416    match invoke(provider.as_ref()) {
2417        Ok(promise) => Value::Promise(promise),
2418        Err(error) => rejected_file_effect(operation, path, target, error),
2419    }
2420}
2421
2422fn file_option(options: &Value, name: &str) -> Option<Value> {
2423    let key = Value::Keyword(name.into());
2424    map_entries(options)?
2425        .into_iter()
2426        .find_map(|(candidate, value)| (candidate == key).then_some(value))
2427}
2428
2429fn file_options_value(value: Value, operation: &str) -> Result<Value, String> {
2430    match value {
2431        Value::Nil => Ok(Value::Map(PMap::new())),
2432        value if map_entries(&value).is_some() => Ok(value),
2433        _ => Err(format!("{operation} options must be a map")),
2434    }
2435}
2436
2437fn file_bool_option(
2438    options: &Value,
2439    name: &str,
2440    default: bool,
2441    operation: &str,
2442) -> Result<bool, String> {
2443    match file_option(options, name) {
2444        None => Ok(default),
2445        Some(Value::Bool(value)) => Ok(value),
2446        Some(_) => Err(format!("{operation} :{name} must be boolean")),
2447    }
2448}
2449
2450fn file_string_option(
2451    options: &Value,
2452    name: &str,
2453    default: &str,
2454    operation: &str,
2455) -> Result<String, String> {
2456    match file_option(options, name) {
2457        None => Ok(default.into()),
2458        Some(Value::String(value)) => Ok(value),
2459        Some(_) => Err(format!("{operation} :{name} must be a string")),
2460    }
2461}
2462
2463fn file_write_options(options: &Value) -> Result<WriteOptions, String> {
2464    let mode = match file_option(options, "mode") {
2465        None => WriteMode::Create,
2466        Some(Value::Keyword(value)) if value.as_str() == "create" => WriteMode::Create,
2467        Some(Value::Keyword(value)) if value.as_str() == "replace" => WriteMode::Replace,
2468        Some(Value::Keyword(value)) if value.as_str() == "append" => WriteMode::Append,
2469        Some(_) => {
2470            return Err("std.native.File/write :mode must be :create, :replace, or :append".into())
2471        }
2472    };
2473    Ok(WriteOptions {
2474        mode,
2475        parents: file_bool_option(options, "parents?", false, "std.native.File/write")?,
2476    })
2477}
2478
2479fn file_mkdir_options(options: &Value) -> Result<MkdirOptions, String> {
2480    Ok(MkdirOptions {
2481        parents: file_bool_option(options, "parents?", true, "std.native.File/mkdir")?,
2482        exists_ok: file_bool_option(options, "exists-ok?", true, "std.native.File/mkdir")?,
2483    })
2484}
2485
2486fn file_delete_options(options: &Value) -> Result<DeleteOptions, String> {
2487    Ok(DeleteOptions {
2488        missing_ok: file_bool_option(options, "missing-ok?", false, "std.native.File/delete")?,
2489    })
2490}
2491
2492fn file_copy_options(options: &Value) -> Result<CopyOptions, String> {
2493    Ok(CopyOptions {
2494        replace: file_bool_option(options, "replace?", false, "std.native.File/copy")?,
2495        parents: file_bool_option(options, "parents?", false, "std.native.File/copy")?,
2496        preserve_modified: file_bool_option(
2497            options,
2498            "preserve-modified?",
2499            false,
2500            "std.native.File/copy",
2501        )?,
2502    })
2503}
2504
2505fn file_move_options(options: &Value) -> Result<MoveOptions, String> {
2506    Ok(MoveOptions {
2507        replace: file_bool_option(options, "replace?", false, "std.native.File/move")?,
2508        parents: file_bool_option(options, "parents?", false, "std.native.File/move")?,
2509        atomic: file_bool_option(options, "atomic?", false, "std.native.File/move")?,
2510    })
2511}
2512
2513fn file_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
2514    let effect_operation = operation
2515        .strip_prefix("std.native.File/")
2516        .map(|method| format!("file/{method}"))
2517        .unwrap_or_else(|| operation.to_owned());
2518    match operation {
2519        "std.native.File/join" | "std.native.File/resolve" => {
2520            if values.len() != 2 {
2521                return Err(format!("{operation} expects a base and path"));
2522            }
2523            let Value::String(base) = &values[0] else {
2524                return Err(format!("{operation} expects a base and path"));
2525            };
2526            let Value::String(path) = &values[1] else {
2527                return Err(format!("{operation} expects a base and path"));
2528            };
2529            let result = if operation == "std.native.File/join" {
2530                crate::file::logical_join(&base, &path)
2531            } else {
2532                crate::file::logical_resolve(&base, &path)
2533            };
2534            result
2535                .map(Value::String)
2536                .map_err(|error| file_error(operation, error))
2537        }
2538        "std.native.File/parent" => {
2539            if values.len() != 1 {
2540                return Err("std.native.File/parent expects a path".into());
2541            }
2542            let Value::String(path) = &values[0] else {
2543                return Err("std.native.File/parent expects a path".into());
2544            };
2545            crate::file::logical_parent(path)
2546                .map(|parent| parent.map(Value::String).unwrap_or(Value::Nil))
2547                .map_err(|error| file_error(operation, error))
2548        }
2549        "std.native.File/read"
2550        | "std.native.File/exists?"
2551        | "std.native.File/stat"
2552        | "std.native.File/entries"
2553        | "std.native.File/list"
2554        | "std.native.File/walk" => {
2555            if values.len() != 1 {
2556                return Err(format!("{operation} expects a path"));
2557            }
2558            let Value::String(path) = &values[0] else {
2559                return Err(format!("{operation} expects a path"));
2560            };
2561            Ok(file_effect(
2562                &effect_operation,
2563                &path,
2564                None,
2565                |provider| match operation {
2566                    "std.native.File/read" => provider.read(&path),
2567                    "std.native.File/exists?" => provider.exists(&path),
2568                    "std.native.File/stat" => provider.stat(&path),
2569                    "std.native.File/entries" => provider.entries(&path),
2570                    "std.native.File/list" => provider.list(&path),
2571                    "std.native.File/walk" => provider.walk(&path),
2572                    _ => unreachable!(),
2573                },
2574            ))
2575        }
2576        "std.native.File/write" => {
2577            if !(2..=3).contains(&values.len()) {
2578                return Err(
2579                    "std.native.File/write expects a path, bytes, and optional options".into(),
2580                );
2581            }
2582            let Value::String(path) = &values[0] else {
2583                return Err("std.native.File/write expects a path and bytes".into());
2584            };
2585            let bytes = match &values[1] {
2586                Value::Bytes(value) => value.clone(),
2587                Value::ByteBuffer(value) => value.borrow().clone(),
2588                _ => return Err("std.native.File/write expects a path and bytes".into()),
2589            };
2590            let options = if values.len() == 3 {
2591                file_options_value(values[2].clone(), operation)?
2592            } else {
2593                Value::Map(PMap::new())
2594            };
2595            let options = file_write_options(&options)?;
2596            Ok(file_effect(&effect_operation, &path, None, |provider| {
2597                provider.write_with_options(&path, bytes, options)
2598            }))
2599        }
2600        "std.native.File/mkdir" => {
2601            if !(1..=2).contains(&values.len()) {
2602                return Err("std.native.File/mkdir expects a path and optional options".into());
2603            }
2604            let Value::String(path) = &values[0] else {
2605                return Err("std.native.File/mkdir expects a path".into());
2606            };
2607            let options = if values.len() == 2 {
2608                file_options_value(values[1].clone(), operation)?
2609            } else {
2610                Value::Map(PMap::new())
2611            };
2612            let options = file_mkdir_options(&options)?;
2613            Ok(file_effect(&effect_operation, &path, None, |provider| {
2614                provider.mkdir_with_options(&path, options)
2615            }))
2616        }
2617        "std.native.File/delete" => {
2618            if !(1..=2).contains(&values.len()) {
2619                return Err("std.native.File/delete expects a path and optional options".into());
2620            }
2621            let Value::String(path) = &values[0] else {
2622                return Err("std.native.File/delete expects a path".into());
2623            };
2624            let options = if values.len() == 2 {
2625                file_options_value(values[1].clone(), operation)?
2626            } else {
2627                Value::Map(PMap::new())
2628            };
2629            let options = file_delete_options(&options)?;
2630            Ok(file_effect(&effect_operation, &path, None, |provider| {
2631                provider.delete_with_options(&path, options)
2632            }))
2633        }
2634        "std.native.File/copy" | "std.native.File/move" => {
2635            if !(2..=3).contains(&values.len()) {
2636                return Err(format!(
2637                    "{operation} expects source, target, and optional options"
2638                ));
2639            }
2640            let Value::String(source) = &values[0] else {
2641                return Err(format!("{operation} expects source and target paths"));
2642            };
2643            let Value::String(target) = &values[1] else {
2644                return Err(format!("{operation} expects source and target paths"));
2645            };
2646            let options = if values.len() == 3 {
2647                file_options_value(values[2].clone(), operation)?
2648            } else {
2649                Value::Map(PMap::new())
2650            };
2651            Ok(if operation == "std.native.File/copy" {
2652                let options = file_copy_options(&options)?;
2653                file_effect(&effect_operation, &source, Some(&target), |provider| {
2654                    provider.copy(&source, &target, options)
2655                })
2656            } else {
2657                let options = file_move_options(&options)?;
2658                file_effect(&effect_operation, &source, Some(&target), |provider| {
2659                    provider.move_entry(&source, &target, options)
2660                })
2661            })
2662        }
2663        "std.native.File/temp-file" | "std.native.File/temp-directory" => {
2664            if !(1..=2).contains(&values.len()) {
2665                return Err(format!("{operation} expects a parent and optional options"));
2666            }
2667            let Value::String(parent) = &values[0] else {
2668                return Err(format!("{operation} expects a parent path"));
2669            };
2670            let options = if values.len() == 2 {
2671                file_options_value(values[1].clone(), operation)?
2672            } else {
2673                Value::Map(PMap::new())
2674            };
2675            Ok(if operation == "std.native.File/temp-file" {
2676                let options = TempFileOptions {
2677                    prefix: file_string_option(&options, "prefix", "tmp", operation)?,
2678                    suffix: file_string_option(&options, "suffix", "", operation)?,
2679                };
2680                file_effect(&effect_operation, &parent, None, |provider| {
2681                    provider.temp_file(&parent, options)
2682                })
2683            } else {
2684                let options = TempDirectoryOptions {
2685                    prefix: file_string_option(&options, "prefix", "tmp", operation)?,
2686                };
2687                file_effect(&effect_operation, &parent, None, |provider| {
2688                    provider.temp_directory(&parent, options)
2689                })
2690            })
2691        }
2692        _ => Err(format!("unknown std.native.File operation: {operation}")),
2693    }
2694}
2695
2696fn socket_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
2697    let operation = operation
2698        .strip_prefix("std.native.Socket/")
2699        .unwrap_or(operation);
2700    match operation {
2701        "receive-stream" | "socket/receive-stream" => {
2702            if values.len() != 1 {
2703                return Err(format!("Socket/{operation} expects a socket connection"));
2704            }
2705            let socket = socket_handle(&values[0], &format!("Socket/{operation}"))?;
2706            let events = socket_provider(operation)?
2707                .events(socket)
2708                .map_err(|e| socket_error(operation, e))?;
2709            Ok(host_stream(
2710                Rc::new(move || socket_receive_promise(events)),
2711                Rc::new(|| Ok(())),
2712            ))
2713        }
2714        "socket/connect" => {
2715            if values.len() != 4 {
2716                return Err("socket/connect expects a host, port, options, and callback".into());
2717            }
2718            let host = match &values[0] {
2719                Value::String(value) => value.clone(),
2720                _ => {
2721                    return Err("socket/connect expects a host, port, options, and callback".into())
2722                }
2723            };
2724            let port = value_u16_integer(&values[1], "socket/connect", false)?;
2725            let _options = &values[2];
2726            let callback = match &values[3] {
2727                Value::Function(value) => value.clone(),
2728                _ => return Err("socket/connect expects a callback".into()),
2729            };
2730            let callback = Rc::new(move |event| {
2731                let arguments = match event {
2732                    SocketEvent::Connected(handle) => {
2733                        vec![Value::Nil, Value::Number(handle as i64)]
2734                    }
2735                    SocketEvent::Failed(_, error) => vec![Value::String(error), Value::Nil],
2736                    SocketEvent::Data(_, _) | SocketEvent::Closed(_) => return,
2737                };
2738                let _ = call_function(&callback, arguments);
2739            });
2740            socket_provider(operation)?
2741                .connect(&host, port, callback)
2742                .map(|handle| Value::Number(handle as i64))
2743                .map_err(|error| socket_error(operation, error))
2744        }
2745        "socket/listen" => {
2746            if values.len() != 4 {
2747                return Err("socket/listen expects a host, port, options, and callback".into());
2748            }
2749            let host = match &values[0] {
2750                Value::String(value) => value.clone(),
2751                _ => return Err("socket/listen expects a host string".into()),
2752            };
2753            let port = value_u16_integer(&values[1], "socket/listen", true)?;
2754            let _options = &values[2];
2755            let callback = match &values[3] {
2756                Value::Function(value) => value.clone(),
2757                _ => return Err("socket/listen expects a callback".into()),
2758            };
2759            let callback = Rc::new(move |event| {
2760                let _ = call_function(&callback, vec![socket_server_event_value(event)]);
2761            });
2762            socket_provider(operation)?
2763                .listen(&host, port, callback)
2764                .map(|handle| Value::Number(handle as i64))
2765                .map_err(|error| socket_error(operation, error))
2766        }
2767        "socket/endpoint" => {
2768            if values.len() != 1 {
2769                return Err("socket/endpoint expects a server".into());
2770            }
2771            let server = socket_handle(&values[0], "socket/endpoint")?;
2772            socket_provider(operation)?
2773                .endpoint(server)
2774                .map(|(host, port)| {
2775                    Value::Map(PMap::from_iter([
2776                        (Value::Keyword("host".into()), Value::String(host)),
2777                        (Value::Keyword("port".into()), Value::Number(port as i64)),
2778                    ]))
2779                })
2780                .map_err(|error| socket_error(operation, error))
2781        }
2782        "socket/events" => {
2783            if values.len() != 2 {
2784                return Err("socket/events expects a socket handle and options".into());
2785            }
2786            let handle = socket_handle(&values[0], "socket/events")?;
2787            let _options = &values[1];
2788            socket_provider(operation)?
2789                .events(handle)
2790                .map(|stream| Value::Number(stream as i64))
2791                .map_err(|error| socket_error(operation, error))
2792        }
2793        "socket/next" => {
2794            if values.len() != 1 {
2795                return Err("socket/next expects a socket stream".into());
2796            }
2797            let stream = socket_handle(&values[0], "socket/next")?;
2798            socket_provider(operation)?
2799                .next(stream)
2800                .map(Value::Promise)
2801                .map_err(|error| socket_error(operation, error))
2802        }
2803        "socket/send" => {
2804            if values.len() != 2 {
2805                return Err("socket/send expects a socket connection and bytes".into());
2806            }
2807            let socket = socket_handle(&values[0], "socket/send")?;
2808            let bytes = match &values[1] {
2809                Value::Bytes(value) => value.clone(),
2810                Value::ByteBuffer(value) => value.borrow().clone(),
2811                _ => return Err("socket/send expects a socket connection and bytes".into()),
2812            };
2813            socket_provider(operation)?
2814                .send(socket, &bytes)
2815                .map(|count| Value::Number(count as i64))
2816                .map_err(|error| socket_error(operation, error))
2817        }
2818        "socket/close" => {
2819            if values.len() != 1 {
2820                return Err("socket/close expects a socket connection".into());
2821            }
2822            let socket = socket_handle(&values[0], "socket/close")?;
2823            socket_provider(operation)?
2824                .close(socket)
2825                .map(|()| Value::Nil)
2826                .map_err(|error| socket_error(operation, error))
2827        }
2828        _ => Err(format!("unknown std.native.Socket operation: {operation}")),
2829    }
2830}
2831
2832fn socket_receive_promise(stream: SocketHandle) -> Result<Promise, String> {
2833    let source = socket_provider("Socket/receive-stream")?
2834        .next(stream)
2835        .map_err(|e| socket_error("Socket/receive-stream", e))?;
2836    let output = Promise::new();
2837    let settled = output.clone();
2838    source.on_settle(Rc::new(move |result| match result {
2839        PromiseState::Rejected(error) => {
2840            settled.reject_rejection(error);
2841        }
2842        PromiseState::Pending => {}
2843        PromiseState::Fulfilled(event) => {
2844            let entries = map_entries(&event).unwrap_or_default();
2845            let kind = entries.iter().find_map(|(k, v)| {
2846                if matches!(k, Value::Keyword(key) if key.as_str() == "type") {
2847                    Some(v.clone())
2848                } else {
2849                    None
2850                }
2851            });
2852            match kind {
2853                Some(Value::Keyword(kind)) if kind.as_str() == "data" => {
2854                    let bytes = entries
2855                        .into_iter()
2856                        .find_map(|(k, v)| {
2857                            if matches!(k, Value::Keyword(key) if key.as_str() == "bytes") {
2858                                Some(v)
2859                            } else {
2860                                None
2861                            }
2862                        })
2863                        .unwrap_or(Value::Nil);
2864                    settled.resolve(bytes);
2865                }
2866                Some(Value::Keyword(kind)) if kind.as_str() == "close" => {
2867                    settled.resolve(Value::Nil);
2868                }
2869                Some(Value::Keyword(kind)) if kind.as_str() == "error" => {
2870                    settled.reject("socket receive failed");
2871                }
2872                _ => {
2873                    settled.reject("Socket/receive-stream received an invalid event");
2874                }
2875            }
2876        }
2877    }));
2878    let poll = source.clone();
2879    output.set_poller(Rc::new(move || {
2880        poll.state();
2881    }));
2882    let wait = source.clone();
2883    output.set_waiter(Rc::new(move || {
2884        wait.wait_state();
2885    }));
2886    Ok(output)
2887}
2888
2889fn socket_handle(value: &Value, operation: &str) -> Result<SocketHandle, String> {
2890    value_u64_integer(value, operation)
2891        .map(|value| value as SocketHandle)
2892        .map_err(|_| format!("{operation} expects a socket handle"))
2893}
2894
2895fn native_host_values(operation: &str, values: Vec<Value>) -> Result<Value, String> {
2896    let method = operation
2897        .strip_prefix("std.native.Host/")
2898        .unwrap_or(operation);
2899    let (service, target, arguments) = match method {
2900        "call" => {
2901            if values.len() != 3 {
2902                return Err(
2903                    "std.native.Host/call expects service, method, and an argument vector".into(),
2904                );
2905            }
2906            let service = match &values[0] {
2907                Value::String(value) => value.clone(),
2908                _ => return Err("std.native.Host/call service must be a string".into()),
2909            };
2910            let target = match &values[1] {
2911                Value::String(value) => value.clone(),
2912                _ => return Err("std.native.Host/call method must be a string".into()),
2913            };
2914            let arguments = match &values[2] {
2915                Value::Vector(values) => values.iter().cloned().collect(),
2916                Value::Tuple(values) => values.iter().cloned().collect(),
2917                _ => return Err("std.native.Host/call arguments must be a vector".into()),
2918            };
2919            (service, target, arguments)
2920        }
2921        "describe" | "capabilities" => {
2922            if !values.is_empty() {
2923                return Err(format!("std.native.Host/{method} expects no arguments"));
2924            }
2925            ("host".into(), method.into(), Vec::new())
2926        }
2927        "capability?" => {
2928            if values.len() != 1 {
2929                return Err("std.native.Host/capability? expects one capability".into());
2930            }
2931            ("host".into(), "capability?".into(), vec![values[0].clone()])
2932        }
2933        _ => return Err(format!("unknown std.native.Host method: {method}")),
2934    };
2935    HOST_CALL_HANDLER.with(|active| {
2936        let Some(handler) = active.borrow().as_ref().cloned() else {
2937            let promise = Promise::new();
2938            promise.reject_value(host_error(
2939                "host/unavailable",
2940                "Host capability provider is unavailable",
2941            ));
2942            return Ok(Value::Promise(promise));
2943        };
2944        handler(service, target, arguments)
2945    })
2946}
2947
2948pub(crate) fn namespace_identifier(value: Value, operation: &str) -> Result<String, String> {
2949    match value {
2950        Value::Symbol(name) if name.get_namespace().is_none() => Ok(name.as_str().to_owned()),
2951        Value::String(name) => Ok(name),
2952        Value::Namespace(namespace) => Ok(namespace.name().as_str().to_owned()),
2953        _ => Err(format!(
2954            "{operation} expects an unqualified namespace symbol, string, or Namespace"
2955        )),
2956    }
2957}
2958
2959fn namespace_descriptor(registry: &NamespaceRegistry<Value>, name: &str) -> Value {
2960    let state = registry
2961        .load_state(name)
2962        .or_else(|| registry.find(name).map(|_| NamespaceLoadState::Loaded))
2963        .map(NamespaceLoadState::as_str)
2964        .unwrap_or("unknown");
2965    let package = package_catalog().coordinate_for_namespace(name);
2966    let origin = if name.starts_with("std.native") {
2967        "embedded"
2968    } else if package.is_some() {
2969        "package"
2970    } else if registry.find(name).is_some() {
2971        "runtime"
2972    } else {
2973        "registered"
2974    };
2975    let mut fields = vec![
2976        (
2977            Value::Keyword("namespace/name".into()),
2978            Value::Symbol(Symbol::parse(name)),
2979        ),
2980        (
2981            Value::Keyword("namespace/state".into()),
2982            Value::Keyword(state.into()),
2983        ),
2984        (
2985            Value::Keyword("namespace/role".into()),
2986            Value::Keyword(
2987                registry
2988                    .find(name)
2989                    .map(|namespace| namespace.role())
2990                    .unwrap_or_else(|| "standard".into())
2991                    .into(),
2992            ),
2993        ),
2994        (
2995            Value::Keyword("namespace/revision".into()),
2996            Value::Number(registry.module_revision(name) as i64),
2997        ),
2998        (
2999            Value::Keyword("namespace/origin".into()),
3000            Value::Keyword(origin.into()),
3001        ),
3002    ];
3003    if let Some(package) = package {
3004        fields.push((
3005            Value::Keyword("namespace/package".into()),
3006            Value::String(package),
3007        ));
3008    }
3009    Value::OrderedMap(Box::new(POrderedMap::from_iter(fields)))
3010}
3011
3012fn native_runtime_values(
3013    operation: &str,
3014    values: Vec<Value>,
3015    env: &mut HashMap<String, Value>,
3016) -> Result<Value, String> {
3017    let method = operation
3018        .strip_prefix("std.native.Runtime/")
3019        .unwrap_or(operation);
3020    let registry = namespace_registry()?;
3021    match method {
3022        "ns-publics" => {
3023            let namespace = match values.as_slice() {
3024                [Value::Symbol(name)] if name.get_namespace().is_none() => name.as_str().to_owned(),
3025                [Value::String(name)] => name.clone(),
3026                [Value::Namespace(namespace)] => namespace.name().as_str().to_owned(),
3027                _ => {
3028                    return Err(
3029                        "std.native.Runtime/ns-publics expects a namespace symbol or string".into(),
3030                    )
3031                }
3032            };
3033            let target = registry
3034                .find(&namespace)
3035                .ok_or_else(|| format!("No such namespace: {namespace}"))?;
3036            let mut mappings = target.mappings();
3037            mappings.retain(|(_, var)| var.symbol().get_namespace() == Some(namespace.as_str()));
3038            mappings.sort_by(|(left, _), (right, _)| left.as_str().cmp(right.as_str()));
3039            Ok(Value::OrderedMap(Box::new(POrderedMap::from_iter(
3040                mappings.into_iter().map(|(name, var)| {
3041                    (
3042                        Value::Symbol(Symbol::create(None, name.as_str())),
3043                        Value::Var(var),
3044                    )
3045                }),
3046            ))))
3047        }
3048        "ns-aliases" => {
3049            let name = match values.as_slice() {
3050                [value] => namespace_identifier(value.clone(), "std.native.Runtime/ns-aliases")?,
3051                _ => return Err("std.native.Runtime/ns-aliases expects one namespace".into()),
3052            };
3053            let target = registry
3054                .find(&name)
3055                .ok_or_else(|| format!("No such namespace: {name}"))?;
3056            let mut aliases = target.aliases();
3057            aliases.sort_by(|(left, _), (right, _)| left.as_str().cmp(right.as_str()));
3058            Ok(Value::OrderedMap(Box::new(POrderedMap::from_iter(
3059                aliases.into_iter().map(|(alias, namespace)| {
3060                    (Value::Symbol(alias), Value::Namespace(Rc::new(namespace)))
3061                }),
3062            ))))
3063        }
3064        "ns-find" => {
3065            if values.len() != 1 {
3066                return Err("std.native.Runtime/ns-find expects one namespace".into());
3067            }
3068            let name = namespace_identifier(values[0].clone(), "std.native.Runtime/ns-find")?;
3069            Ok(registry
3070                .find(&name)
3071                .map(|namespace| Value::Namespace(Rc::new(namespace)))
3072                .unwrap_or(Value::Nil))
3073        }
3074        "ns-create" => match values.as_slice() {
3075            [Value::Symbol(name)] if name.get_namespace().is_none() => {
3076                let namespace = registry.find_or_create(name.as_str());
3077                Ok(Value::Namespace(Rc::new(namespace)))
3078            }
3079            _ => Err("std.native.Runtime/ns-create expects an unqualified symbol".into()),
3080        },
3081        "ns-name" => match values.as_slice() {
3082            [Value::Namespace(namespace)] => Ok(Value::Symbol(namespace.name().clone())),
3083            [Value::Symbol(name)]
3084                if name.get_namespace().is_none()
3085                    && namespace_registry()?.find(name.as_str()).is_some() =>
3086            {
3087                Ok(Value::Symbol(name.clone()))
3088            }
3089            _ => Err("std.native.Runtime/ns-name expects a namespace".into()),
3090        },
3091        "current" => {
3092            if !values.is_empty() {
3093                return Err("std.native.Runtime/current expects no arguments".into());
3094            }
3095            Ok(Value::Symbol(registry.current().name().clone()))
3096        }
3097        "snapshot" => {
3098            if !values.is_empty() {
3099                return Err("std.native.Runtime/snapshot expects no arguments".into());
3100            }
3101            let namespaces = registry
3102                .known_names()
3103                .into_iter()
3104                .map(|name| namespace_descriptor(&registry, name.as_str()))
3105                .collect::<Vec<_>>();
3106            Ok(Value::OrderedMap(Box::new(POrderedMap::from_iter([
3107                (
3108                    Value::Keyword("env/current".into()),
3109                    Value::Symbol(registry.current().name().clone()),
3110                ),
3111                (
3112                    Value::Keyword("env/namespaces".into()),
3113                    Value::Vector(PVector::from(namespaces)),
3114                ),
3115            ]))))
3116        }
3117        "namespaces" => {
3118            if !values.is_empty() {
3119                return Err("std.native.Runtime/namespaces expects no arguments".into());
3120            }
3121            Ok(Value::Vector(PVector::from(
3122                registry
3123                    .known_names()
3124                    .into_iter()
3125                    .map(|name| namespace_descriptor(&registry, name.as_str()))
3126                    .collect::<Vec<_>>(),
3127            )))
3128        }
3129        "namespace" => {
3130            if values.len() != 1 {
3131                return Err("std.native.Runtime/namespace expects one namespace".into());
3132            }
3133            let name = namespace_identifier(values[0].clone(), operation)?;
3134            if registry.load_state(&name).is_none() && registry.find(&name).is_none() {
3135                Ok(Value::Nil)
3136            } else {
3137                Ok(namespace_descriptor(&registry, &name))
3138            }
3139        }
3140        "module" => {
3141            if values.len() != 1 {
3142                return Err("std.native.Runtime/module expects one module path".into());
3143            }
3144            let requested = match &values[0] {
3145                Value::String(path) => path.clone(),
3146                Value::Symbol(name) => name.as_str().to_owned(),
3147                _ => {
3148                    return Err(
3149                        "std.native.Runtime/module expects a path string or namespace symbol"
3150                            .into(),
3151                    )
3152                }
3153            };
3154            let source = requested.strip_prefix("classpath:").unwrap_or(&requested);
3155            let namespace = if source.ends_with(".hal") || source.ends_with(".hrl") {
3156                source
3157                    .trim_end_matches(".hal")
3158                    .trim_end_matches(".hrl")
3159                    .trim_start_matches("./")
3160                    .replace('/', ".")
3161            } else {
3162                source.to_owned()
3163            };
3164            let revision = registry.module_revision(&namespace);
3165            if revision == 0
3166                && registry.load_state(&namespace).is_none()
3167                && registry.find(&namespace).is_none()
3168            {
3169                return Ok(Value::Nil);
3170            }
3171            let dependencies = registry
3172                .module_dependencies(&namespace)
3173                .into_iter()
3174                .map(|dependency| {
3175                    Value::String(format!("{}.hal", dependency.as_str().replace('.', "/")))
3176                })
3177                .collect::<Vec<_>>();
3178            Ok(Value::OrderedMap(Box::new(POrderedMap::from_iter([
3179                (
3180                    Value::Keyword("module/path".into()),
3181                    Value::String(requested),
3182                ),
3183                (
3184                    Value::Keyword("module/namespace".into()),
3185                    Value::Symbol(Symbol::parse(&namespace)),
3186                ),
3187                (
3188                    Value::Keyword("module/revision".into()),
3189                    Value::Number(revision as i64),
3190                ),
3191                (
3192                    Value::Keyword("module/dependencies".into()),
3193                    Value::Vector(PVector::from(dependencies)),
3194                ),
3195            ]))))
3196        }
3197        "vars" => {
3198            if values.len() > 1 {
3199                return Err("std.native.Runtime/vars expects zero or one namespace".into());
3200            }
3201            let name = if values.is_empty() {
3202                registry.current().name().as_str().to_owned()
3203            } else {
3204                namespace_identifier(values[0].clone(), operation)?
3205            };
3206            let namespace = registry
3207                .find(&name)
3208                .ok_or_else(|| format!("namespace/not-found: {name}"))?;
3209            let mut mappings = namespace.mappings();
3210            mappings.retain(|(_, var)| var.symbol().get_namespace() == Some(name.as_str()));
3211            mappings.sort_by(|(left, _), (right, _)| left.as_str().cmp(right.as_str()));
3212            Ok(Value::OrderedMap(Box::new(POrderedMap::from_iter(
3213                mappings.into_iter().map(|(symbol, var)| {
3214                    (
3215                        Value::Symbol(Symbol::create(None, symbol.as_str())),
3216                        Value::Var(var),
3217                    )
3218                }),
3219            ))))
3220        }
3221        "eval" => {
3222            if values.len() != 1 {
3223                return Err("std.native.Runtime/eval expects one form".into());
3224            }
3225            let mut environment = crate::core::current_namespace_environment()?;
3226            let result = eval_value(values[0].clone(), &mut environment);
3227            crate::core::save_namespace_environment(&registry, &mut environment);
3228            result
3229        }
3230        "load-string" => {
3231            let [Value::String(source)] = values.as_slice() else {
3232                return Err("std.native.Runtime/load-string expects one string".into());
3233            };
3234            #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3235            if direct_native_execution() {
3236                return eval_direct_native_source(source);
3237            }
3238            eval_value_text(source, env)
3239        }
3240        "var-sym" => {
3241            let [Value::Var(var)] = values.as_slice() else {
3242                return Err("std.native.Runtime/var-sym expects one Var".into());
3243            };
3244            Ok(Value::Symbol(var.symbol().clone()))
3245        }
3246        "macroexpand-1" => {
3247            if values.len() != 1 {
3248                return Err("std.native.Runtime/macroexpand-1 expects one form".into());
3249            }
3250            let form = value_to_form(&values[0])?;
3251            let mut environment = current_namespace_environment()?;
3252            form_to_value(&macroexpand_once(&form, &mut environment)?)
3253        }
3254        "gensym" => {
3255            let prefix = match values.as_slice() {
3256                [] => "G__".to_owned(),
3257                [Value::String(prefix)] => prefix.clone(),
3258                [value] => {
3259                    return Err(format!(
3260                        "gensym expects a string prefix, got {}",
3261                        portable_type_name(value)
3262                    ))
3263                }
3264                _ => return Err("gensym expects zero or one arguments".into()),
3265            };
3266            Ok(Value::Symbol(Symbol::from(gensym(&prefix))))
3267        }
3268        "eval-in" => {
3269            if values.len() != 2 {
3270                return Err("std.native.Runtime/eval-in expects namespace and forms".into());
3271            }
3272            let target = namespace_identifier(values[0].clone(), operation)?;
3273            if registry.find(&target).is_none() {
3274                return Err(format!(
3275                    "std.native.Runtime/eval-in requires an existing namespace: {target}"
3276                ));
3277            }
3278            let forms = iterator_values(values[1].clone())?
3279                .into_iter()
3280                .map(|value| value_to_form(&value))
3281                .collect::<Result<Vec<_>, _>>()?;
3282            let previous = registry.current().name().as_str().to_owned();
3283            select_namespace_environment(&registry, env, &target);
3284            #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3285            let result = if direct_native_execution() {
3286                let source = if forms.is_empty() {
3287                    "nil".to_owned()
3288                } else {
3289                    Form::List(
3290                        std::iter::once(Form::Symbol("do".into()))
3291                            .chain(forms.iter().cloned())
3292                            .collect(),
3293                    )
3294                    .to_string()
3295                };
3296                eval_direct_native_source(&source)
3297            } else {
3298                let mut result = Value::Nil;
3299                for form in &forms {
3300                    result = eval(form, env)?;
3301                }
3302                Ok(result)
3303            };
3304            #[cfg(not(all(feature = "direct-native", not(target_arch = "wasm32"))))]
3305            let result = {
3306                let mut result = Value::Nil;
3307                for form in &forms {
3308                    result = eval(form, env)?;
3309                }
3310                Ok(result)
3311            };
3312            select_namespace_environment(&registry, env, &previous);
3313            result
3314        }
3315        "alias-state" => {
3316            if values.len() != 1 && values.len() != 2 {
3317                return Err(
3318                    "std.native.Runtime/alias-state expects alias or namespace and alias".into(),
3319                );
3320            }
3321            let (owner, alias_value) = if values.len() == 2 {
3322                (
3323                    namespace_identifier(values[0].clone(), operation)?,
3324                    &values[1],
3325                )
3326            } else {
3327                (registry.current().name().as_str().to_owned(), &values[0])
3328            };
3329            let Value::Symbol(alias) = alias_value else {
3330                return Err(
3331                    "std.native.Runtime/alias-state expects an unqualified alias symbol".into(),
3332                );
3333            };
3334            if alias.get_namespace().is_some() {
3335                return Err(
3336                    "std.native.Runtime/alias-state expects an unqualified alias symbol".into(),
3337                );
3338            }
3339            let Some(namespace) = registry.find(&owner) else {
3340                return Ok(Value::Nil);
3341            };
3342            // Global aliases are resolver-visible even when the owning namespace
3343            // has not materialized a local alias entry. Report the same target
3344            // and lifecycle state that qualified symbol resolution observes.
3345            let target = namespace
3346                .lazy_target(alias.as_str())
3347                .or_else(|| {
3348                    namespace
3349                        .aliases()
3350                        .into_iter()
3351                        .find(|(name, _)| name == alias)
3352                        .map(|(_, target)| target.name().clone())
3353                })
3354                .or_else(|| {
3355                    registry
3356                        .global_aliases()
3357                        .into_iter()
3358                        .find(|(name, _)| name == alias)
3359                        .map(|(_, target)| target)
3360                });
3361            let Some(target) = target else {
3362                return Ok(Value::Nil);
3363            };
3364            let state = registry
3365                .load_state(target.as_str())
3366                .or_else(|| {
3367                    registry
3368                        .find(target.as_str())
3369                        .map(|_| NamespaceLoadState::Loaded)
3370                })
3371                .map(NamespaceLoadState::as_str)
3372                .unwrap_or("unknown");
3373            Ok(Value::Map(PMap::from_iter([
3374                (Value::Keyword("alias".into()), Value::Symbol(alias.clone())),
3375                (Value::Keyword("target".into()), Value::Symbol(target)),
3376                (Value::Keyword("state".into()), Value::Keyword(state.into())),
3377            ])))
3378        }
3379        "intern-var" => {
3380            if values.len() != 3 && values.len() != 4 {
3381                return Err(
3382                    "std.native.Runtime/intern-var expects namespace, symbol, Var, and optional metadata"
3383                        .into(),
3384                );
3385            }
3386            let target = namespace_identifier(values[0].clone(), operation)?;
3387            let Value::Symbol(name) = &values[1] else {
3388                return Err(
3389                    "std.native.Runtime/intern-var expects an unqualified target symbol".into(),
3390                );
3391            };
3392            if name.get_namespace().is_some() {
3393                return Err(
3394                    "std.native.Runtime/intern-var expects an unqualified target symbol".into(),
3395                );
3396            }
3397            let Value::Var(source) = &values[2] else {
3398                return Err("std.native.Runtime/intern-var expects a source Var".into());
3399            };
3400            let mut metadata = source.metadata();
3401            if let Some(extension) = values.get(3) {
3402                let Some(entries) = map_entries(extension) else {
3403                    return Err(
3404                        "std.native.Runtime/intern-var metadata extension must be a map".into(),
3405                    );
3406                };
3407                for (key, value) in entries {
3408                    metadata.extra.insert(key.display(), value.display());
3409                }
3410            }
3411            let value = source.deref_value();
3412            if let Value::Function(function) = &value {
3413                if function.is_macro {
3414                    ACTIVE_MACROS.with(|active| {
3415                        if let Some(macros) = active.borrow().as_ref() {
3416                            macros.borrow_mut().insert(
3417                                (target.clone(), name.as_str().to_owned()),
3418                                function.clone(),
3419                            );
3420                        }
3421                    });
3422                }
3423            }
3424            Ok(Value::Var(
3425                registry.find_or_create(&target).intern_with_metadata(
3426                    name.as_str(),
3427                    value,
3428                    metadata,
3429                ),
3430            ))
3431        }
3432        _ => Err(format!("unknown std.native.Runtime method: {method}")),
3433    }
3434}
3435
3436#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3437fn eval_direct_native_source(source: &str) -> Result<Value, String> {
3438    let context = DirectNativeContext::capture();
3439    let forms = crate::kernel::read_forms(source).map_err(|error| error.to_string())?;
3440    let has_namespace_form = forms.iter().any(|form| {
3441        matches!(
3442            form_without_metadata(&form.form),
3443            Form::List(items)
3444                if matches!(items.first(), Some(Form::Symbol(operator)) if operator == "ns" || operator == "ns+")
3445        )
3446    });
3447    let config = if has_namespace_form {
3448        crate::vm::source_namespace_config(&forms).map_err(|error| error.to_string())?
3449    } else {
3450        crate::kernel::GeneratedNamespaceConfig::defaults()
3451    };
3452    let namespaces = context.namespaces.clone();
3453    let program = context
3454        .with(|| {
3455            without_direct_native_execution(|| {
3456                crate::vm::compile_source_with_config_allow_unbound_globals(
3457                    source,
3458                    &namespaces,
3459                    config,
3460                )
3461            })
3462        })
3463        .map_err(|error| error.to_string())?;
3464    let mut program = program;
3465    if program.namespace.is_none() {
3466        program.namespace = Some(context.namespace.clone());
3467    }
3468    let engine = crate::direct_native::NativeEngine::new();
3469    // `context.with` already installs the captured providers, namespace, and
3470    // multimethod map. Calling the convenience entry point here would wrap
3471    // the same map in a second context and restore an old snapshot over any
3472    // declarations made by the nested program.
3473    let result = context.with(|| engine.execute_blocking(Rc::new(program)));
3474    // Nested Runtime/eval calls execute with a captured multimethod map. Carry
3475    // declarations made there back into the enclosing native frame so a later
3476    // form in the same frame can install methods or invoke the new multifn.
3477    let nested_multimethods = context.multimethods.borrow().clone();
3478    ACTIVE_MULTIMETHODS.with(|active| {
3479        active.borrow_mut().extend(nested_multimethods);
3480    });
3481    result.map(|report| report.value)
3482}
3483
3484fn eval_value(value: Value, env: &mut HashMap<String, Value>) -> Result<Value, String> {
3485    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3486    if direct_native_execution() {
3487        return eval_direct_native_source(&value_to_form(&value)?.to_string());
3488    }
3489    eval(&value_to_form(&value)?, env)
3490}
3491
3492fn native_package_values(
3493    operation: &str,
3494    arguments: Vec<Value>,
3495    env: &mut HashMap<String, Value>,
3496) -> Result<Value, String> {
3497    let method = operation
3498        .strip_prefix("std.native.Package/")
3499        .unwrap_or(operation);
3500    if matches!(method, "build" | "inspect" | "seal" | "inspect-seal" | "verify-seal") {
3501        return native_package_artifact_values(method, arguments);
3502    }
3503    let expected = match method {
3504        "catalog" => 0..=0,
3505        "find" | "ensure" | "load" | "state" => 1..=1,
3506        "unload" => 1..=2,
3507        _ => return Err(format!("unknown std.native.Package method: {method}")),
3508    };
3509    if !expected.contains(&arguments.len()) {
3510        return Err(format!(
3511            "std.native.Package/{method} expects {} arguments",
3512            expected.start()
3513        ));
3514    }
3515    let catalog = package_catalog();
3516    if method == "catalog" {
3517        return Ok(catalog.catalog_value());
3518    }
3519    let target = match arguments.first() {
3520        Some(Value::Symbol(value)) => value.as_str().to_owned(),
3521        Some(Value::String(value)) => value.clone(),
3522        Some(Value::Keyword(value)) => value.as_str().to_owned(),
3523        Some(value @ Value::OrderedMap(_)) if method == "ensure" || method == "unload" => {
3524            package_descriptor_coordinate(value).ok_or_else(|| {
3525                format!("std.native.Package/{method} descriptor requires :package/coordinate")
3526            })?
3527        }
3528        _ => {
3529            return Err(format!(
3530                "std.native.Package/{method} expects a namespace, coordinate, or exact descriptor"
3531            ))
3532        }
3533    };
3534    let found = catalog.find(&target);
3535    if method == "find" {
3536        return Ok(found.map(|(_, value)| value).unwrap_or(Value::Nil));
3537    }
3538    let Some((coordinate, descriptor)) = found else {
3539        if method == "state" {
3540            return Ok(Value::Nil);
3541        }
3542        return Err(format!("package/not-locked: {target}"));
3543    };
3544    if method == "state" {
3545        return Ok(Value::Keyword(
3546            catalog
3547                .state(&coordinate)
3548                .unwrap_or_else(|| "available".into())
3549                .into(),
3550        ));
3551    }
3552    if method == "load" {
3553        if catalog.coordinate_for_namespace(&target).as_deref() != Some(&coordinate) {
3554            return Err("std.native.Package/load expects a locked namespace".into());
3555        }
3556        if catalog.state(&coordinate).as_deref() != Some("ready") {
3557            return Err(format!(
3558                "package/not-ready: {coordinate}; call Package/ensure first"
3559            ));
3560        }
3561        let registry = namespace_registry()?;
3562        require_namespace(&registry, env, &target)?;
3563        return Ok(Value::Symbol(Symbol::parse(&target)));
3564    }
3565    if method == "ensure" {
3566        if catalog.state(&coordinate).as_deref() == Some("ready") {
3567            let promise = Promise::new();
3568            promise.resolve(descriptor);
3569            return Ok(Value::Promise(promise));
3570        }
3571        if let Some(pending) = catalog.pending(&coordinate) {
3572            return Ok(Value::Promise(pending));
3573        }
3574    } else if catalog.state(&coordinate).as_deref() == Some("available") {
3575        let promise = Promise::new();
3576        promise.resolve(Value::Vector(PVector::new()));
3577        return Ok(Value::Promise(promise));
3578    } else if catalog.pending(&coordinate).is_some() {
3579        return Err(format!("package/busy: {coordinate}"));
3580    }
3581    if method == "unload" {
3582        if let Some(options) = arguments.get(1) {
3583            if map_entries(options).is_none() {
3584                return Err("std.native.Package/unload options must be a map".into());
3585            }
3586            if let Some(value) = map_value(options, &Value::Keyword("cascade".into())) {
3587                if !matches!(value, Value::Bool(_)) {
3588                    return Err("std.native.Package/unload :cascade must be boolean".into());
3589                }
3590            }
3591        }
3592    }
3593    let previous_state = catalog
3594        .state(&coordinate)
3595        .unwrap_or_else(|| "available".into());
3596    catalog.set_state(
3597        &coordinate,
3598        if method == "ensure" {
3599            "ensuring"
3600        } else {
3601            "unloading"
3602        },
3603    );
3604    HOST_CALL_HANDLER.with(|active| {
3605        let Some(handler) = active.borrow().as_ref().cloned() else {
3606            let promise = Promise::new();
3607            promise.reject_value(host_error(
3608                "package/unsupported",
3609                "Package capability provider is unavailable",
3610            ));
3611            catalog.set_state(
3612                &coordinate,
3613                if method == "ensure" {
3614                    "failed"
3615                } else {
3616                    &previous_state
3617                },
3618            );
3619            return Ok(Value::Promise(promise));
3620        };
3621        let mut provider_arguments = vec![descriptor];
3622        provider_arguments.extend(arguments.iter().skip(1).cloned());
3623        let result = handler("package".into(), method.into(), provider_arguments);
3624        if let Ok(Value::Promise(promise)) = &result {
3625            let state = catalog.clone();
3626            let coordinate = coordinate.clone();
3627            let operation = method.to_owned();
3628            let rollback = previous_state.clone();
3629            state.set_pending(&coordinate, Some(promise.clone()));
3630            promise.on_settle(Rc::new(move |settlement| {
3631                let next = match (&operation[..], settlement) {
3632                    ("ensure", PromiseState::Fulfilled(_)) => "ready",
3633                    ("ensure", _) => "failed",
3634                    ("unload", PromiseState::Fulfilled(_)) => "available",
3635                    ("unload", _) => rollback.as_str(),
3636                    _ => rollback.as_str(),
3637                };
3638                state.set_state(&coordinate, next);
3639                state.set_pending(&coordinate, None);
3640            }));
3641        } else if result.is_ok() {
3642            catalog.set_state(
3643                &coordinate,
3644                if method == "ensure" {
3645                    "ready"
3646                } else {
3647                    "available"
3648                },
3649            );
3650        } else {
3651            catalog.set_state(
3652                &coordinate,
3653                if method == "ensure" {
3654                    "failed"
3655                } else {
3656                    &previous_state
3657                },
3658            );
3659        }
3660        result
3661    })
3662}
3663
3664#[cfg(not(target_arch = "wasm32"))]
3665fn native_package_artifact_values(method: &str, arguments: Vec<Value>) -> Result<Value, String> {
3666    match method {
3667        "build" => {
3668            if !(1..=4).contains(&arguments.len()) {
3669                return Err("std.native.Package/build expects input and up to three options".into());
3670            }
3671            let input = package_string_argument(&arguments, 0, "std.native.Package/build")?.to_owned();
3672            let output = package_optional_string_argument(&arguments, 1, "std.native.Package/build")?
3673                .map(str::to_owned);
3674            let package = package_optional_string_argument(&arguments, 2, "std.native.Package/build")?
3675                .map(str::to_owned);
3676            let profile = package_optional_string_argument(&arguments, 3, "std.native.Package/build")?
3677                .map(str::to_owned);
3678            let built = std::thread::spawn(move || {
3679                crate::package::build_path_with_package(
3680                    std::path::Path::new(&input),
3681                    output.as_deref().map(std::path::Path::new),
3682                    package.as_deref(),
3683                    profile.as_deref().map(std::path::Path::new),
3684                )
3685            })
3686            .join()
3687            .map_err(|_| "std.native.Package/build thread panicked".to_owned())??;
3688            Ok(Value::String(built.to_string_lossy().into_owned()))
3689        }
3690        "inspect" => {
3691            if arguments.len() != 1 {
3692                return Err("std.native.Package/inspect expects one archive path".into());
3693            }
3694            Ok(Value::String(crate::package::inspect_path(
3695                std::path::Path::new(package_string_argument(
3696                    &arguments,
3697                    0,
3698                    "std.native.Package/inspect",
3699                )?),
3700            )?))
3701        }
3702        "seal" => {
3703            if arguments.len() != 1 {
3704                return Err("std.native.Package/seal expects one descriptor map".into());
3705            }
3706            let manifest = crate::distribution::seal(&sealed_package_spec(&arguments[0])?)?;
3707            Ok(sealed_manifest_value(&manifest))
3708        }
3709        "inspect-seal" | "verify-seal" => {
3710            if arguments.len() != 1 {
3711                return Err(format!("std.native.Package/{method} expects one executable path"));
3712            }
3713            let path = std::path::Path::new(package_string_argument(
3714                &arguments,
3715                0,
3716                &format!("std.native.Package/{method}"),
3717            )?);
3718            let found = if method == "inspect-seal" {
3719                crate::distribution::inspect_sealed(path)?
3720            } else {
3721                crate::distribution::verify_sealed(path)?
3722            };
3723            Ok(found
3724                .as_ref()
3725                .map(sealed_manifest_value)
3726                .unwrap_or(Value::Nil))
3727        }
3728        _ => unreachable!("caller restricts native package artifact methods"),
3729    }
3730}
3731
3732#[cfg(target_arch = "wasm32")]
3733fn native_package_artifact_values(method: &str, _arguments: Vec<Value>) -> Result<Value, String> {
3734    Err(format!("package/unsupported: Package/{method} is unavailable on wasm"))
3735}
3736
3737#[cfg(not(target_arch = "wasm32"))]
3738fn sealed_package_spec(value: &Value) -> Result<crate::distribution::SealSpec, String> {
3739    let entries = map_entries(value)
3740        .ok_or_else(|| "std.native.Package/seal expects one descriptor map".to_owned())?;
3741    let descriptor = Value::OrderedMap(Box::new(POrderedMap::from_iter(entries)));
3742    let required_string = |key: &str| {
3743        match map_value(&descriptor, &Value::Keyword(key.into())) {
3744            Some(Value::String(value)) if !value.is_empty() => Ok(value.clone()),
3745            Some(_) => Err(format!("std.native.Package/seal :{key} must be a non-empty string")),
3746            None => Err(format!("std.native.Package/seal is missing :{key}")),
3747        }
3748    };
3749    let entry = match map_value(&descriptor, &Value::Keyword("entry".into())) {
3750        Some(Value::Symbol(value)) => value.as_str().to_owned(),
3751        Some(_) => return Err("std.native.Package/seal :entry must be a symbol".into()),
3752        None => return Err("std.native.Package/seal is missing :entry".into()),
3753    };
3754    let host = match map_value(&descriptor, &Value::Keyword("host".into())) {
3755        Some(Value::String(value)) if !value.is_empty() => std::path::PathBuf::from(value),
3756        Some(_) => return Err("std.native.Package/seal :host must be a non-empty string".into()),
3757        None => std::env::current_exe()
3758            .map_err(|error| format!("cannot determine current native executable: {error}"))?,
3759    };
3760    let archives = match map_value(&descriptor, &Value::Keyword("archives".into())) {
3761        Some(value) => iterator_values(value.clone())?,
3762        None => return Err("std.native.Package/seal is missing :archives".into()),
3763    }
3764    .into_iter()
3765    .map(|archive| {
3766        let archive_entries = map_entries(&archive)
3767            .ok_or_else(|| "std.native.Package/seal archives must be maps".to_owned())?;
3768        let archive = Value::OrderedMap(Box::new(POrderedMap::from_iter(archive_entries)));
3769        let path = match map_value(&archive, &Value::Keyword("path".into())) {
3770            Some(Value::String(value)) if !value.is_empty() => std::path::PathBuf::from(value),
3771            Some(_) => {
3772                return Err(
3773                    "std.native.Package/seal archive :path must be a non-empty string".into(),
3774                )
3775            }
3776            None => return Err("std.native.Package/seal archive is missing :path".into()),
3777        };
3778        let primary = match map_value(&archive, &Value::Keyword("primary".into())) {
3779            Some(Value::Bool(value)) => *value,
3780            Some(_) => return Err("std.native.Package/seal archive :primary must be boolean".into()),
3781            None => false,
3782        };
3783        Ok(crate::distribution::SealArchive { path, primary })
3784    })
3785    .collect::<Result<Vec<_>, String>>()?;
3786    Ok(crate::distribution::SealSpec {
3787        host,
3788        output: std::path::PathBuf::from(required_string("output")?),
3789        entry,
3790        archives,
3791    })
3792}
3793
3794#[cfg(not(target_arch = "wasm32"))]
3795fn sealed_manifest_value(manifest: &crate::distribution::SealedManifest) -> Value {
3796    let archives: Vec<Value> = manifest
3797        .archives
3798        .iter()
3799        .map(|archive| {
3800            Value::OrderedMap(Box::new(POrderedMap::from_iter([
3801                (
3802                    Value::Keyword("identity".into()),
3803                    Value::String(archive.identity.clone()),
3804                ),
3805                (
3806                    Value::Keyword("version".into()),
3807                    Value::String(archive.version.clone()),
3808                ),
3809                (
3810                    Value::Keyword("sha256".into()),
3811                    Value::String(archive.sha256.clone()),
3812                ),
3813                (
3814                    Value::Keyword("offset".into()),
3815                    Value::Number(i64::try_from(archive.offset).unwrap_or(i64::MAX)),
3816                ),
3817                (
3818                    Value::Keyword("length".into()),
3819                    Value::Number(i64::try_from(archive.length).unwrap_or(i64::MAX)),
3820                ),
3821                (Value::Keyword("primary".into()), Value::Bool(archive.primary)),
3822            ])))
3823        })
3824        .collect();
3825    Value::OrderedMap(Box::new(POrderedMap::from_iter([
3826        (
3827            Value::Keyword("executable/format".into()),
3828            Value::String(crate::distribution::SEALED_FORMAT.into()),
3829        ),
3830        (
3831            Value::Keyword("entry".into()),
3832            Value::Symbol(Symbol::parse(&manifest.entry)),
3833        ),
3834        (Value::Keyword("archives".into()), Value::Vector(PVector::from(archives))),
3835        (
3836            Value::Keyword("host/sha256".into()),
3837            Value::String(manifest.host_sha256.clone()),
3838        ),
3839        (
3840            Value::Keyword("payload/sha256".into()),
3841            Value::String(manifest.payload_sha256.clone()),
3842        ),
3843    ])))
3844}
3845
3846#[cfg(not(target_arch = "wasm32"))]
3847fn package_string_argument<'a>(
3848    arguments: &'a [Value],
3849    index: usize,
3850    operation: &str,
3851) -> Result<&'a str, String> {
3852    match arguments.get(index) {
3853        Some(Value::String(value)) => Ok(value),
3854        _ => Err(format!("{operation} expects a string argument at position {index}")),
3855    }
3856}
3857
3858#[cfg(not(target_arch = "wasm32"))]
3859fn package_optional_string_argument<'a>(
3860    arguments: &'a [Value],
3861    index: usize,
3862    operation: &str,
3863) -> Result<Option<&'a str>, String> {
3864    match arguments.get(index) {
3865        None | Some(Value::Nil) => Ok(None),
3866        Some(Value::String(value)) => Ok(Some(value)),
3867        _ => Err(format!("{operation} expects an optional string at position {index}")),
3868    }
3869}
3870
3871/// Invokes the active host capability provider with already-evaluated VM
3872/// values. This is the bytecode boundary for `std.native.Host/call`; the VM
3873/// remains unaware of timers, sockets, or any other concrete host operation.
3874pub fn call_host_value(service: Value, target: Value, arguments: Value) -> Result<Value, String> {
3875    let service = match service {
3876        Value::String(value) => value,
3877        _ => return Err("std.native.Host/call service must be a string".into()),
3878    };
3879    let target = match target {
3880        Value::String(value) => value,
3881        _ => return Err("std.native.Host/call method must be a string".into()),
3882    };
3883    let arguments = match arguments {
3884        Value::Vector(values) => values.iter().cloned().collect(),
3885        Value::Tuple(values) => values.iter().cloned().collect(),
3886        _ => return Err("std.native.Host/call arguments must be a vector".into()),
3887    };
3888    if !native_capability_granted("host-call") {
3889        return Ok(native_capability_denied_promise(
3890            "Host",
3891            "call",
3892            "host-call",
3893        ));
3894    }
3895    HOST_CALL_HANDLER.with(|active| {
3896        let Some(handler) = active.borrow().as_ref().cloned() else {
3897            let promise = Promise::new();
3898            promise.reject_value(host_error(
3899                "host/unavailable",
3900                "Host capability provider is unavailable",
3901            ));
3902            return Ok(Value::Promise(promise));
3903        };
3904        handler(service, target, arguments)
3905    })
3906}
3907
3908fn host_error(code: &str, message: &str) -> Value {
3909    Value::ExceptionInfo(Rc::new(ExceptionInfo {
3910        message: message.into(),
3911        data: Box::new(Value::Map(
3912            vec![
3913                (
3914                    Value::Keyword("ex/code".into()),
3915                    Value::Keyword(code.into()),
3916                ),
3917                (
3918                    Value::Keyword("ex/class".into()),
3919                    Value::Keyword("ex.class/host".into()),
3920                ),
3921            ]
3922            .into_iter()
3923            .collect(),
3924        )),
3925        cause: None,
3926        provenance: Rc::new(RefCell::new(Default::default())),
3927    }))
3928}
3929/// Installs the explicit host-call boundary for one evaluation.
3930pub fn with_host_calls<R>(
3931    handler: Rc<dyn Fn(String, String, Vec<Value>) -> Result<Value, String>>,
3932    operation: impl FnOnce() -> R,
3933) -> R {
3934    HOST_CALL_HANDLER.with(|active| {
3935        let previous = active.replace(Some(handler));
3936        let result = operation();
3937        active.replace(previous);
3938        result
3939    })
3940}
3941
3942/// Runs an evaluation with a source provider used to satisfy `require` loads.
3943pub fn with_namespace_source<R>(
3944    provider: Rc<dyn Fn(&str) -> Option<NamespaceResource>>,
3945    action: impl FnOnce() -> R,
3946) -> R {
3947    NAMESPACE_SOURCE_PROVIDER.with(|active| {
3948        let previous = active.borrow_mut().replace(provider);
3949        let result = action();
3950        *active.borrow_mut() = previous;
3951        result
3952    })
3953}
3954
3955/// Installs the direct-native namespace loader for one runtime evaluation.
3956/// The ordinary source/bytecode loader remains the default; this hook lets a
3957/// Runtime replace only the execution of a materialized namespace while the
3958/// shared namespace transaction, dependency tracking, and rollback logic stay
3959/// in one place.
3960#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3961pub(crate) fn with_direct_native_namespace_loader<R>(
3962    loader: Rc<dyn Fn(&str, NamespaceResource, &mut HashMap<String, Value>) -> Result<(), String>>,
3963    action: impl FnOnce() -> R,
3964) -> R {
3965    ACTIVE_DIRECT_NATIVE_NAMESPACE_LOADER.with(|active| {
3966        let previous = active.borrow_mut().replace(loader);
3967        let result = action();
3968        *active.borrow_mut() = previous;
3969        result
3970    })
3971}
3972
3973/// Marks the duration of generated direct-native code. Native helpers may
3974/// still call one another during this scope, but any attempt to invoke the
3975/// tree evaluator through a helper is rejected at the shared boundary.
3976#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3977pub(crate) fn with_direct_native_execution<R>(action: impl FnOnce() -> R) -> R {
3978    ACTIVE_DIRECT_NATIVE_EXECUTION.with(|active| {
3979        let previous = active.replace(true);
3980        let result = action();
3981        active.set(previous);
3982        result
3983    })
3984}
3985
3986/// Temporarily leaves the generated-code execution scope while compiling a
3987/// nested program. Macro expansion and namespace configuration are
3988/// compilation-time compatibility seams; they may use the tree evaluator,
3989/// but the validated program must re-enter the direct guard before execution.
3990#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
3991pub(crate) fn without_direct_native_execution<R>(action: impl FnOnce() -> R) -> R {
3992    ACTIVE_DIRECT_NATIVE_EXECUTION.with(|active| {
3993        let previous = active.replace(false);
3994        let result = action();
3995        active.set(previous);
3996        result
3997    })
3998}
3999
4000#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
4001fn direct_native_namespace_loader(
4002) -> Option<Rc<dyn Fn(&str, NamespaceResource, &mut HashMap<String, Value>) -> Result<(), String>>>
4003{
4004    ACTIVE_DIRECT_NATIVE_NAMESPACE_LOADER.with(|active| active.borrow().clone())
4005}
4006
4007#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
4008pub(crate) fn direct_native_execution() -> bool {
4009    ACTIVE_DIRECT_NATIVE_EXECUTION.with(Cell::get)
4010}
4011
4012/// Runtime state captured when a VM closure crosses the synchronous machine
4013/// boundary. The no-op shape keeps ordinary VM and wasm builds free of
4014/// direct-native dependencies while native callbacks and resumptions can
4015/// restore providers, namespace selection, protocols, and the evaluator
4016/// guard on native builds.
4017#[derive(Clone, Default)]
4018pub(crate) struct NativeCallbackContext {
4019    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
4020    scope: Option<crate::direct_native::NativeExecutionScope>,
4021    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
4022    context: Option<DirectNativeContext>,
4023}
4024
4025impl NativeCallbackContext {
4026    pub(crate) fn capture() -> Self {
4027        #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
4028        {
4029            let scope = crate::direct_native::capture_execution_scope();
4030            let context = scope.as_ref().map(|_| DirectNativeContext::capture());
4031            return Self { scope, context };
4032        }
4033        #[cfg(not(all(feature = "direct-native", not(target_arch = "wasm32"))))]
4034        {
4035            Self::default()
4036        }
4037    }
4038
4039    pub(crate) fn with<R>(&self, action: impl FnOnce() -> R) -> R {
4040        #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
4041        {
4042            return crate::direct_native::with_captured_context(
4043                self.scope.as_ref(),
4044                self.context.as_ref(),
4045                action,
4046            );
4047        }
4048        #[cfg(not(all(feature = "direct-native", not(target_arch = "wasm32"))))]
4049        {
4050            action()
4051        }
4052    }
4053}