1use crate::{ModuleRef, ProcessRef, RequiredSurfaceRef};
2
3use super::{ExecutionScratch, ProfileReport, ProjectedBindings, Record, RuntimeFailure, Value};
4use crate::LashlangExecutionObservation;
5use std::future::Future;
6use std::sync::Mutex;
7use thiserror::Error;
8
9#[derive(Clone, Debug)]
10pub enum AbilityOp {
11 ResourceOperation(ResourceOperation),
12 Await(Value),
13 Cancel(Value),
14 Print(Value),
15 Submit(Value),
16 Finish(Value),
17 Fail(Value),
18 StartProcess(Box<ProcessStart>),
19 ProcessEvent(ProcessEvent),
20 Sleep(Sleep),
21 WaitSignal,
22 SignalRun(ProcessSignal),
23}
24
25#[derive(Clone, Debug)]
26pub enum AbilityResult {
27 Value(Value),
28 Unit,
29}
30
31impl AbilityResult {
32 pub fn into_value(self, op: &'static str) -> Result<Value, ExecutionHostError> {
33 match self {
34 Self::Value(value) => Ok(value),
35 Self::Unit => Err(ExecutionHostError::new(format!("{op} returned no value"))),
36 }
37 }
38}
39
40#[derive(Clone, Debug)]
41pub struct ProcessStart {
42 pub module_ref: ModuleRef,
43 pub process_ref: ProcessRef,
44 pub required_surface_ref: RequiredSurfaceRef,
45 pub process_name: String,
46 pub args: Record,
47}
48
49#[derive(Clone, Debug)]
50pub struct ResourceOperation {
51 pub receiver: Value,
52 pub operation: String,
53 pub args: Vec<Value>,
54 pub call_site: Option<crate::LashlangExecutionCallSite>,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum ProcessEventKind {
59 Yield,
60 Wake,
61}
62
63#[derive(Clone, Debug)]
64pub struct ProcessEvent {
65 pub kind: ProcessEventKind,
66 pub value: Value,
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub enum SleepKind {
71 For,
72 Until,
73}
74
75#[derive(Clone, Debug)]
76pub struct Sleep {
77 pub kind: SleepKind,
78 pub value: Value,
79}
80
81#[derive(Clone, Debug)]
82pub struct ProcessSignal {
83 pub run: Value,
84 pub payload: Value,
85}
86
87#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
88pub enum ExecutionMode {
89 #[default]
90 Foreground,
91 Process,
92}
93
94pub trait ExecutionHost: Sync {
95 fn perform(
96 &self,
97 op: AbilityOp,
98 ) -> impl Future<Output = Result<AbilityResult, ExecutionHostError>> + Send;
99
100 fn yield_now(&self) -> impl Future<Output = ()> + Send {
101 async {}
102 }
103
104 fn execution_mode(&self) -> ExecutionMode {
105 ExecutionMode::Foreground
106 }
107
108 fn projected_bindings(&self) -> ProjectedBindings {
109 ProjectedBindings::default()
110 }
111
112 fn trace_runtime_errors(&self) -> bool {
113 false
114 }
115
116 fn profile_execution(&self) -> bool {
117 false
118 }
119
120 fn take_scratch(&self) -> Option<ExecutionScratch> {
121 None
122 }
123
124 fn store_scratch(&self, _scratch: ExecutionScratch) {}
125
126 fn observe_runtime_failure(&self, _failure: RuntimeFailure) {}
127
128 fn observe_profile(&self, _profile: ProfileReport) {}
129
130 fn observe_lashlang_execution(&self, _observation: LashlangExecutionObservation) {}
131}
132
133pub struct ExecutionEnvironment<'host, H: ExecutionHost> {
134 host: &'host H,
135 mode: ExecutionMode,
136 projected: ProjectedBindings,
137 scratch: Mutex<Option<ExecutionScratch>>,
138 trace_runtime_errors: bool,
139 profile_execution: bool,
140 runtime_failure: Mutex<Option<RuntimeFailure>>,
141 profile: Mutex<Option<ProfileReport>>,
142}
143
144impl<'host, H: ExecutionHost> ExecutionEnvironment<'host, H> {
145 pub fn new(host: &'host H) -> Self {
146 Self {
147 host,
148 mode: host.execution_mode(),
149 projected: host.projected_bindings(),
150 scratch: Mutex::new(host.take_scratch()),
151 trace_runtime_errors: host.trace_runtime_errors(),
152 profile_execution: host.profile_execution(),
153 runtime_failure: Mutex::new(None),
154 profile: Mutex::new(None),
155 }
156 }
157
158 pub fn with_mode(mut self, mode: ExecutionMode) -> Self {
159 self.mode = mode;
160 self
161 }
162
163 pub fn process(self) -> Self {
164 self.with_mode(ExecutionMode::Process)
165 }
166
167 pub fn foreground(self) -> Self {
168 self.with_mode(ExecutionMode::Foreground)
169 }
170
171 pub fn with_projected_bindings(mut self, projected: ProjectedBindings) -> Self {
172 self.projected = projected;
173 self
174 }
175
176 pub fn with_scratch(mut self, scratch: ExecutionScratch) -> Self {
177 self.scratch = Mutex::new(Some(scratch));
178 self
179 }
180
181 pub fn traced(mut self) -> Self {
182 self.trace_runtime_errors = true;
183 self
184 }
185
186 pub fn profiled(mut self) -> Self {
187 self.profile_execution = true;
188 self
189 }
190
191 pub fn take_runtime_failure(&self) -> Option<RuntimeFailure> {
192 self.runtime_failure.lock().ok()?.take()
193 }
194
195 pub fn take_profile(&self) -> Option<ProfileReport> {
196 self.profile.lock().ok()?.take()
197 }
198
199 pub fn take_recycled_scratch(&self) -> Option<ExecutionScratch> {
200 self.scratch.lock().ok()?.take()
201 }
202}
203
204impl<H: ExecutionHost> ExecutionHost for ExecutionEnvironment<'_, H> {
205 async fn perform(&self, op: AbilityOp) -> Result<AbilityResult, ExecutionHostError> {
206 self.host.perform(op).await
207 }
208
209 async fn yield_now(&self) {
210 self.host.yield_now().await;
211 }
212
213 fn execution_mode(&self) -> ExecutionMode {
214 self.mode
215 }
216
217 fn projected_bindings(&self) -> ProjectedBindings {
218 self.projected.clone()
219 }
220
221 fn trace_runtime_errors(&self) -> bool {
222 self.trace_runtime_errors
223 }
224
225 fn profile_execution(&self) -> bool {
226 self.profile_execution
227 }
228
229 fn take_scratch(&self) -> Option<ExecutionScratch> {
230 self.scratch.lock().ok()?.take()
231 }
232
233 fn store_scratch(&self, scratch: ExecutionScratch) {
234 if let Ok(mut guard) = self.scratch.lock() {
235 *guard = Some(scratch);
236 }
237 }
238
239 fn observe_runtime_failure(&self, failure: RuntimeFailure) {
240 self.host.observe_runtime_failure(failure.clone());
241 if let Ok(mut guard) = self.runtime_failure.lock() {
242 *guard = Some(failure);
243 }
244 }
245
246 fn observe_profile(&self, profile: ProfileReport) {
247 self.host.observe_profile(profile.clone());
248 if let Ok(mut guard) = self.profile.lock() {
249 *guard = Some(profile);
250 }
251 }
252
253 fn observe_lashlang_execution(&self, observation: LashlangExecutionObservation) {
254 self.host.observe_lashlang_execution(observation);
255 }
256}
257
258#[derive(Clone, Debug, Error, PartialEq, Eq)]
259#[error("{message}")]
260pub struct ExecutionHostError {
261 message: String,
262}
263
264impl ExecutionHostError {
265 pub fn new(message: impl Into<String>) -> Self {
266 Self {
267 message: message.into(),
268 }
269 }
270}