use std::sync::Arc;
use std::time::Duration;
use crate::chunk::ChunkRef;
use crate::value::{VmError, VmValue};
use super::{ScopeSpan, Vm};
pub(super) enum TopLevelEntry {
Chunk(ChunkRef),
Callable {
bootstrap: ChunkRef,
has_fixture: bool,
args: Vec<VmValue>,
},
}
impl TopLevelEntry {
pub(super) async fn run(self, vm: &mut Vm) -> Result<VmValue, VmError> {
match self {
Self::Chunk(chunk) => vm.run_chunk(chunk).await,
Self::Callable {
bootstrap,
has_fixture,
mut args,
} => {
let value = vm.run_chunk(bootstrap).await?;
let target = if has_fixture {
let VmValue::List(callables) = value else {
return Err(VmError::Runtime(
"callable entry bootstrap did not return [fixture, target]".to_string(),
));
};
let [VmValue::Closure(fixture), VmValue::Closure(target)] =
callables.as_slice()
else {
return Err(VmError::Runtime(
"callable entry bootstrap returned invalid fixture callables"
.to_string(),
));
};
let fixture_value = vm.call_closure_pub(fixture, &[]).await?;
args.insert(0, fixture_value);
Arc::clone(target)
} else {
let VmValue::Closure(target) = value else {
return Err(VmError::Runtime(
"callable entry bootstrap did not return a callable".to_string(),
));
};
target
};
vm.call_closure_pub(&target, &args).await
}
}
}
}
impl Vm {
pub async fn execute_callable_entry_with_timeout(
&mut self,
entry: &crate::CompiledCallableEntry,
args: &[VmValue],
timeout: Duration,
) -> Result<VmValue, VmError> {
self.execute_top_level_with_timeout(
TopLevelEntry::Callable {
bootstrap: Arc::new(entry.bootstrap.clone()),
has_fixture: entry.has_fixture,
args: args.to_vec(),
},
timeout,
)
.await
}
pub(super) async fn execute_top_level(
&mut self,
entry: TopLevelEntry,
) -> Result<VmValue, VmError> {
self.ensure_execution_available()?;
let registry = self.pool_registry.clone();
let owner = crate::observability::execution_scope::mint_execution_scope();
let ambient = crate::orchestration::AmbientExecutionScope::capture_for_top_level_execution(
owner,
self.llm_mock_context.clone(),
);
let execution = crate::stdlib::pool::with_pool_registry_scope(registry, async {
self.execute_entry_scoped(entry).await
});
crate::orchestration::scope_ambient(ambient, execution).await
}
async fn execute_entry_scoped(&mut self, entry: TopLevelEntry) -> Result<VmValue, VmError> {
let _execution_activity = self
.wait_for_graph
.register_task(self.runtime_context.task_id.clone());
let _span = ScopeSpan::new(crate::tracing::SpanKind::Pipeline, "main".into());
match entry.run(self).await {
Ok(value) => self.run_pipeline_finish_lifecycle(value).await,
Err(error) => {
crate::orchestration::clear_pipeline_on_finish();
Err(error)
}
}
}
}