Skip to main content

coil_wasm/invocation/
execution.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use crate::error::WasmModelError;
5use crate::grants::{
6    HostCapabilityGrant, HostGrantSet, MetadataGrant, ResourceLimits, StorageClassGrant,
7};
8use crate::host_services::{HostServiceExecution, HostServiceExecutor, HostServiceSessionState};
9use crate::ids::{ExtensionId, ExtensionPointKind, HandlerId};
10use crate::output::TypedExecutionOutput;
11
12use super::InvocationContext;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct InvocationPlan {
16    pub extension_id: ExtensionId,
17    pub handler_id: HandlerId,
18    pub point: ExtensionPointKind,
19    pub customer_app_id: String,
20    pub granted_capabilities: HostGrantSet,
21    pub limits: ResourceLimits,
22    pub context: InvocationContext,
23}
24
25impl InvocationPlan {
26    pub fn begin_execution(self) -> WasmExecutionSession {
27        WasmExecutionSession::new(self)
28    }
29
30    pub fn begin_synthetic_execution(self) -> WasmExecutionSession {
31        WasmExecutionSession::with_executor(
32            self,
33            Arc::new(crate::host_services::SyntheticHostServiceExecutor::default()),
34        )
35    }
36
37    pub fn begin_execution_with_executor(
38        self,
39        executor: Arc<dyn HostServiceExecutor>,
40    ) -> WasmExecutionSession {
41        WasmExecutionSession::with_executor(self, executor)
42    }
43
44    pub fn grant_slots(&self) -> Vec<HostCapabilityGrant> {
45        self.granted_capabilities.iter().cloned().collect()
46    }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum HostCall {
51    DataRead {
52        resource: String,
53    },
54    DataWrite {
55        resource: String,
56    },
57    AuthCheck,
58    AuthList,
59    AuthLookup,
60    AuthTupleWrite,
61    StorageRead {
62        class: StorageClassGrant,
63    },
64    StorageWrite {
65        class: StorageClassGrant,
66        bytes: u64,
67    },
68    RenderFragment {
69        slot: String,
70    },
71    MetadataWrite {
72        kind: MetadataGrant,
73    },
74    CacheHintWrite,
75    OutboundHttp {
76        integration: String,
77        response_bytes: u64,
78    },
79    SecretRead {
80        secret: String,
81    },
82    EnqueueJob {
83        queue: String,
84    },
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub enum InvocationOutcome {
89    Page,
90    ApiJson,
91    JobCompleted,
92    ScheduledJobCompleted,
93    WebhookAccepted,
94    AdminWidget,
95    RenderHook,
96}
97
98impl InvocationOutcome {
99    pub fn engine_code(&self) -> i32 {
100        match self {
101            Self::Page => 0,
102            Self::ApiJson => 1,
103            Self::JobCompleted => 2,
104            Self::ScheduledJobCompleted => 3,
105            Self::WebhookAccepted => 4,
106            Self::AdminWidget => 5,
107            Self::RenderHook => 6,
108        }
109    }
110
111    pub fn from_engine_code(code: i32, handler_id: String) -> Result<Self, WasmModelError> {
112        match code {
113            0 => Ok(Self::Page),
114            1 => Ok(Self::ApiJson),
115            2 => Ok(Self::JobCompleted),
116            3 => Ok(Self::ScheduledJobCompleted),
117            4 => Ok(Self::WebhookAccepted),
118            5 => Ok(Self::AdminWidget),
119            6 => Ok(Self::RenderHook),
120            _ => Err(WasmModelError::InvalidOutcomeCode { handler_id, code }),
121        }
122    }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Default)]
126pub struct ExecutionUsage {
127    pub outbound_requests: u32,
128    pub outbound_response_bytes: u64,
129    pub storage_writes: u32,
130    pub storage_bytes: u64,
131    pub peak_concurrency: u16,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct ExecutionReceipt {
136    pub extension_id: ExtensionId,
137    pub handler_id: HandlerId,
138    pub point: ExtensionPointKind,
139    pub runtime: Duration,
140    pub usage: ExecutionUsage,
141    pub outcome: InvocationOutcome,
142    pub host_calls: Vec<HostCall>,
143    pub host_service_executions: Vec<HostServiceExecution>,
144    pub typed_output: Option<TypedExecutionOutput>,
145}
146
147#[derive(Debug, Clone)]
148pub struct WasmExecutionSession {
149    state: HostServiceSessionState,
150}
151
152impl WasmExecutionSession {
153    pub fn new(plan: InvocationPlan) -> Self {
154        Self {
155            state: HostServiceSessionState::new(plan),
156        }
157    }
158
159    pub fn with_executor(plan: InvocationPlan, executor: Arc<dyn HostServiceExecutor>) -> Self {
160        Self {
161            state: HostServiceSessionState::with_executor(plan, executor),
162        }
163    }
164
165    pub fn plan(&self) -> &InvocationPlan {
166        self.state.plan()
167    }
168
169    pub fn usage(&self) -> &ExecutionUsage {
170        self.state.usage()
171    }
172
173    pub fn host_calls(&self) -> &[HostCall] {
174        self.state.host_calls()
175    }
176
177    pub fn host_service_executions(&self) -> &[HostServiceExecution] {
178        self.state.host_service_executions()
179    }
180
181    pub fn grant_slots(&self) -> Vec<HostCapabilityGrant> {
182        self.state.grant_slots()
183    }
184
185    pub fn execute_host_call(
186        &mut self,
187        call: HostCall,
188    ) -> Result<HostServiceExecution, WasmModelError> {
189        self.state.execute_host_call(call)
190    }
191
192    pub fn record_host_call(&mut self, call: HostCall) -> Result<(), WasmModelError> {
193        self.state.record_host_call(call)
194    }
195
196    pub fn reserve_concurrency(&mut self, units: u16) -> Result<(), WasmModelError> {
197        self.state.reserve_concurrency(units)
198    }
199
200    pub fn release_concurrency(&mut self, units: u16) {
201        self.state.release_concurrency(units)
202    }
203
204    pub fn finish(
205        self,
206        runtime: Duration,
207        outcome: InvocationOutcome,
208        typed_output: Option<TypedExecutionOutput>,
209    ) -> Result<ExecutionReceipt, WasmModelError> {
210        self.state.finish(runtime, outcome, typed_output)
211    }
212}