1use crate::action::ActionEnvelope;
10use crate::async_runtime::{
11 JobRef, JobRequestPayload, JobSpec, ResourceExecutionContext, ServiceBindings,
12 ServiceCommandPayload, ServiceSpec, ServiceStartPayload, ServiceStopPayload, ServiceType,
13};
14use crate::capability::CapabilityInvocationPayload;
15use crate::capability::{CapabilityType, OperationCapability};
16use crate::env::RouteLocation;
17use fission_ir::WidgetId;
18use serde::{Deserialize, Serialize};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
25pub struct ReqId(pub u64);
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub struct ResourceId(pub u64);
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub enum RuntimeEffect {
37 Cancel { req_id: u64 },
39 ReleaseResource { resource_id: u64 },
41}
42
43#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
48pub enum Effect {
49 Runtime(RuntimeEffect),
51 Capability(CapabilityInvocationPayload),
53 Job(JobRequestPayload),
55 StartService(ServiceStartPayload),
57 ServiceCommand(ServiceCommandPayload),
59 StopService(ServiceStopPayload),
61}
62
63#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
78pub struct EffectEnvelope {
79 pub req_id: u64,
81 pub effect: Effect,
83 pub on_ok: Option<ActionEnvelope>,
85 pub on_err: Option<ActionEnvelope>,
87 pub service_bindings: Option<ServiceBindings>,
89 pub resource: Option<ResourceExecutionContext>,
91}
92
93#[derive(Clone, Debug, PartialEq)]
113pub enum ActionInput {
114 None,
116 RouteChanged { location: RouteLocation },
118 JobOk {
120 job_name: String,
121 req_id: u64,
122 payload: Vec<u8>,
123 },
124 JobErr {
126 job_name: String,
127 req_id: u64,
128 payload: Option<Vec<u8>>,
129 message: Option<String>,
130 },
131 ServiceStarted {
133 service_name: String,
134 slot_key: String,
135 instance_id: u64,
136 },
137 ServiceStartFailed {
139 service_name: String,
140 slot_key: String,
141 payload: Option<Vec<u8>>,
142 message: Option<String>,
143 },
144 ServiceEvent {
146 service_name: String,
147 slot_key: String,
148 instance_id: u64,
149 payload: Vec<u8>,
150 },
151 ServiceStopped {
153 service_name: String,
154 slot_key: String,
155 instance_id: u64,
156 },
157 ServiceCommandOk {
159 service_name: String,
160 slot_key: String,
161 instance_id: u64,
162 req_id: u64,
163 payload: Option<Vec<u8>>,
164 },
165 ServiceCommandErr {
167 service_name: String,
168 slot_key: String,
169 instance_id: u64,
170 req_id: u64,
171 payload: Option<Vec<u8>>,
172 message: Option<String>,
173 },
174 CapabilityOk {
176 capability: String,
177 req_id: u64,
178 payload: Vec<u8>,
179 },
180 CapabilityErr {
182 capability: String,
183 req_id: u64,
184 payload: Option<Vec<u8>>,
185 message: Option<String>,
186 },
187 TimerTick { payload: Vec<u8> },
189 Pointer {
191 x: f32,
192 y: f32,
193 delta_x: f32,
194 delta_y: f32,
195 },
196 Drop { paths: Vec<String>, x: f32, y: f32 },
198 InternalDrop { payload: Vec<u8>, x: f32, y: f32 },
200 ScopedRaw {
202 scope_id: u128,
203 target: WidgetId,
204 input: Box<ActionInput>,
205 },
206}
207
208impl ActionInput {
209 pub(crate) fn scoped_raw(scope_id: u128, target: WidgetId, input: ActionInput) -> Self {
210 Self::ScopedRaw {
211 scope_id,
212 target: target.into(),
213 input: Box::new(input),
214 }
215 }
216
217 pub fn action_scope_id(&self) -> Option<u128> {
218 match self {
219 ActionInput::ScopedRaw { scope_id, .. } => Some(*scope_id),
220 _ => None,
221 }
222 }
223
224 pub fn scoped_target(&self) -> Option<WidgetId> {
225 match self {
226 ActionInput::ScopedRaw { target, .. } => Some(*target),
227 _ => None,
228 }
229 }
230
231 pub fn unscoped(&self) -> &ActionInput {
232 match self {
233 ActionInput::ScopedRaw { input, .. } => input.unscoped(),
234 _ => self,
235 }
236 }
237
238 pub fn as_bytes(&self) -> Option<&[u8]> {
239 match self.unscoped() {
240 ActionInput::JobOk { payload, .. } => Some(payload),
241 ActionInput::CapabilityOk { payload, .. } => Some(payload),
242 ActionInput::TimerTick { payload } => Some(payload),
243 ActionInput::InternalDrop { payload, .. } => Some(payload),
244 _ => None,
245 }
246 }
247
248 pub fn as_pointer(&self) -> Option<(f32, f32, f32, f32)> {
249 match self.unscoped() {
250 ActionInput::Pointer {
251 x,
252 y,
253 delta_x,
254 delta_y,
255 } => Some((*x, *y, *delta_x, *delta_y)),
256 ActionInput::Drop { x, y, .. } => Some((*x, *y, 0.0, 0.0)),
257 ActionInput::InternalDrop { x, y, .. } => Some((*x, *y, 0.0, 0.0)),
258 _ => None,
259 }
260 }
261
262 pub fn as_drop_paths(&self) -> Option<&[String]> {
263 match self.unscoped() {
264 ActionInput::Drop { paths, .. } => Some(paths),
265 _ => None,
266 }
267 }
268
269 pub fn as_internal_drop(&self) -> Option<&[u8]> {
270 match self.unscoped() {
271 ActionInput::InternalDrop { payload, .. } => Some(payload),
272 _ => None,
273 }
274 }
275
276 pub fn job_ok<J: JobSpec>(&self, job: JobRef<J>) -> Option<J::Ok> {
277 match self.unscoped() {
278 ActionInput::JobOk {
279 job_name, payload, ..
280 } if job_name == job.name => serde_json::from_slice(payload).ok(),
281 _ => None,
282 }
283 }
284
285 pub fn job_err<J: JobSpec>(&self, job: JobRef<J>) -> Option<J::Err> {
286 match self.unscoped() {
287 ActionInput::JobErr {
288 job_name,
289 payload: Some(payload),
290 ..
291 } if job_name == job.name => serde_json::from_slice(payload).ok(),
292 _ => None,
293 }
294 }
295
296 pub fn job_error_message<J: JobSpec>(&self, job: JobRef<J>) -> Option<&str> {
297 match self.unscoped() {
298 ActionInput::JobErr {
299 job_name,
300 message: Some(message),
301 ..
302 } if job_name == job.name => Some(message.as_str()),
303 _ => None,
304 }
305 }
306
307 pub fn capability_ok<C: OperationCapability>(
308 &self,
309 capability: CapabilityType<C>,
310 ) -> Option<C::Ok> {
311 match self.unscoped() {
312 ActionInput::CapabilityOk {
313 capability: actual,
314 payload,
315 ..
316 } if actual == capability.name => serde_json::from_slice(payload).ok(),
317 _ => None,
318 }
319 }
320
321 pub fn capability_error<C: OperationCapability>(
322 &self,
323 capability: CapabilityType<C>,
324 ) -> Option<C::Err> {
325 match self.unscoped() {
326 ActionInput::CapabilityErr {
327 capability: actual,
328 payload: Some(payload),
329 ..
330 } if actual == capability.name => serde_json::from_slice(payload).ok(),
331 _ => None,
332 }
333 }
334
335 pub fn capability_error_message<C: OperationCapability>(
336 &self,
337 capability: CapabilityType<C>,
338 ) -> Option<&str> {
339 match self.unscoped() {
340 ActionInput::CapabilityErr {
341 capability: actual,
342 message: Some(message),
343 ..
344 } if actual == capability.name => Some(message),
345 _ => None,
346 }
347 }
348
349 pub fn service_event<S: ServiceSpec>(&self, service: ServiceType<S>) -> Option<S::Event> {
350 match self.unscoped() {
351 ActionInput::ServiceEvent {
352 service_name,
353 payload,
354 ..
355 } if service_name == service.name => serde_json::from_slice(payload).ok(),
356 _ => None,
357 }
358 }
359
360 pub fn service_start_err<S: ServiceSpec>(
361 &self,
362 service: ServiceType<S>,
363 ) -> Option<S::StartErr> {
364 match self.unscoped() {
365 ActionInput::ServiceStartFailed {
366 service_name,
367 payload: Some(payload),
368 ..
369 } if service_name == service.name => serde_json::from_slice(payload).ok(),
370 _ => None,
371 }
372 }
373
374 pub fn service_start_error_message<S: ServiceSpec>(
375 &self,
376 service: ServiceType<S>,
377 ) -> Option<&str> {
378 match self.unscoped() {
379 ActionInput::ServiceStartFailed {
380 service_name,
381 message: Some(message),
382 ..
383 } if service_name == service.name => Some(message.as_str()),
384 _ => None,
385 }
386 }
387
388 pub fn service_command_ok<S: ServiceSpec>(
389 &self,
390 service: ServiceType<S>,
391 ) -> Option<S::CommandOk> {
392 match self.unscoped() {
393 ActionInput::ServiceCommandOk {
394 service_name,
395 payload: Some(payload),
396 ..
397 } if service_name == service.name => serde_json::from_slice(payload).ok(),
398 _ => None,
399 }
400 }
401
402 pub fn service_command_err<S: ServiceSpec>(
403 &self,
404 service: ServiceType<S>,
405 ) -> Option<S::CommandErr> {
406 match self.unscoped() {
407 ActionInput::ServiceCommandErr {
408 service_name,
409 payload: Some(payload),
410 ..
411 } if service_name == service.name => serde_json::from_slice(payload).ok(),
412 _ => None,
413 }
414 }
415
416 pub fn timer_tick<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
417 match self.unscoped() {
418 ActionInput::TimerTick { payload } => serde_json::from_slice(payload).ok(),
419 _ => None,
420 }
421 }
422
423 pub fn service_slot_key(&self) -> Option<&str> {
424 match self.unscoped() {
425 ActionInput::ServiceStarted { slot_key, .. }
426 | ActionInput::ServiceStartFailed { slot_key, .. }
427 | ActionInput::ServiceEvent { slot_key, .. }
428 | ActionInput::ServiceStopped { slot_key, .. }
429 | ActionInput::ServiceCommandOk { slot_key, .. }
430 | ActionInput::ServiceCommandErr { slot_key, .. } => Some(slot_key.as_str()),
431 _ => None,
432 }
433 }
434
435 pub fn service_instance_id(&self) -> Option<u64> {
436 match self.unscoped() {
437 ActionInput::ServiceStarted { instance_id, .. }
438 | ActionInput::ServiceEvent { instance_id, .. }
439 | ActionInput::ServiceStopped { instance_id, .. }
440 | ActionInput::ServiceCommandOk { instance_id, .. }
441 | ActionInput::ServiceCommandErr { instance_id, .. } => Some(*instance_id),
442 _ => None,
443 }
444 }
445}