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}