harn_vm/vm/
callable_entry.rs1use std::sync::Arc;
2use std::time::Duration;
3
4use crate::chunk::ChunkRef;
5use crate::value::{VmError, VmValue};
6
7use super::{ScopeSpan, Vm};
8
9pub(super) enum TopLevelEntry {
10 Chunk(ChunkRef),
11 Callable {
12 bootstrap: ChunkRef,
13 has_fixture: bool,
14 fixture_expects_harness: bool,
15 expects_harness: bool,
16 args: Vec<VmValue>,
17 },
18}
19
20impl TopLevelEntry {
21 pub(super) async fn run(self, vm: &mut Vm) -> Result<VmValue, VmError> {
22 match self {
23 Self::Chunk(chunk) => vm.run_chunk(chunk).await,
24 Self::Callable {
25 bootstrap,
26 has_fixture,
27 fixture_expects_harness,
28 expects_harness,
29 mut args,
30 } => {
31 let value = vm.run_chunk(bootstrap).await?;
32 let target = if has_fixture {
33 let VmValue::List(callables) = value else {
34 return Err(VmError::Runtime(
35 "callable entry bootstrap did not return [fixture, target]".to_string(),
36 ));
37 };
38 let [VmValue::Closure(fixture), VmValue::Closure(target)] =
39 callables.as_slice()
40 else {
41 return Err(VmError::Runtime(
42 "callable entry bootstrap returned invalid fixture callables"
43 .to_string(),
44 ));
45 };
46 let fixture_args = if fixture_expects_harness {
47 vec![vm.root_harness_value().ok_or_else(|| {
48 VmError::Runtime(
49 "test fixture requires Harness, but no root Harness is installed"
50 .to_string(),
51 )
52 })?]
53 } else {
54 Vec::new()
55 };
56 let fixture_value = vm.call_closure_pub(fixture, &fixture_args).await?;
57 args.insert(0, fixture_value);
58 Arc::clone(target)
59 } else {
60 let VmValue::Closure(target) = value else {
61 return Err(VmError::Runtime(
62 "callable entry bootstrap did not return a callable".to_string(),
63 ));
64 };
65 target
66 };
67 if expects_harness {
68 let harness = vm.root_harness_value().ok_or_else(|| {
69 VmError::Runtime(
70 "callable entry requires Harness, but no root Harness is installed"
71 .to_string(),
72 )
73 })?;
74 args.insert(0, harness);
75 }
76 vm.call_closure_pub(&target, &args).await
77 }
78 }
79 }
80}
81
82impl Vm {
83 pub async fn execute_callable_entry_with_timeout(
91 &mut self,
92 entry: &crate::CompiledCallableEntry,
93 args: &[VmValue],
94 timeout: Duration,
95 ) -> Result<VmValue, VmError> {
96 self.execute_top_level_with_timeout(
97 TopLevelEntry::Callable {
98 bootstrap: Arc::new(entry.bootstrap.clone()),
99 has_fixture: entry.has_fixture,
100 fixture_expects_harness: entry.fixture_expects_harness,
101 expects_harness: entry.expects_harness,
102 args: args.to_vec(),
103 },
104 timeout,
105 )
106 .await
107 }
108
109 pub(super) async fn execute_top_level(
110 &mut self,
111 entry: TopLevelEntry,
112 ) -> Result<VmValue, VmError> {
113 self.ensure_execution_available()?;
114 let registry = self.pool_registry.clone();
115 let owner = crate::observability::execution_scope::mint_execution_scope();
116 let ambient = crate::orchestration::AmbientExecutionScope::capture_for_top_level_execution(
117 owner,
118 self.llm_mock_context.clone(),
119 );
120 let execution = crate::stdlib::pool::with_pool_registry_scope(registry, async {
121 self.execute_entry_scoped(entry).await
122 });
123 crate::orchestration::scope_ambient(ambient, execution).await
124 }
125
126 async fn execute_entry_scoped(&mut self, entry: TopLevelEntry) -> Result<VmValue, VmError> {
127 let _execution_activity = self
128 .wait_for_graph
129 .register_task(self.runtime_context.task_id.clone());
130 let _span = ScopeSpan::new(crate::tracing::SpanKind::Pipeline, "main".into());
131 match entry.run(self).await {
132 Ok(value) => self.run_pipeline_finish_lifecycle(value).await,
133 Err(error) => {
134 crate::orchestration::clear_pipeline_on_finish();
135 Err(error)
136 }
137 }
138 }
139}