Skip to main content

lashlang/runtime/
host.rs

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}