Skip to main content

lashlang/runtime/
host.rs

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