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, Copy, PartialEq, Eq, Serialize, Deserialize)]
36pub enum ScrollAxis {
37 Vertical,
39 Horizontal,
41 Both,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
47pub enum ScrollAlignment {
48 Start,
50 Center,
52 End,
54 Nearest,
56 Fraction(f32),
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65pub enum ScrollBehavior {
66 Instant,
68 Smooth,
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct ScrollIntoViewRequest {
94 pub container: Option<WidgetId>,
96 pub target: WidgetId,
98 pub axis: ScrollAxis,
100 pub alignment: ScrollAlignment,
102 pub padding: [f32; 4],
104 pub behavior: ScrollBehavior,
106 pub if_needed: bool,
108}
109
110#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub enum RuntimeEffect {
113 Cancel { req_id: u64 },
115 ReleaseResource { resource_id: u64 },
117 ScrollIntoView(ScrollIntoViewRequest),
119}
120
121#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
126pub enum Effect {
127 Runtime(RuntimeEffect),
129 Capability(CapabilityInvocationPayload),
131 Job(JobRequestPayload),
133 StartService(ServiceStartPayload),
135 ServiceCommand(ServiceCommandPayload),
137 StopService(ServiceStopPayload),
139}
140
141#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
156pub struct EffectEnvelope {
157 pub req_id: u64,
159 pub effect: Effect,
161 pub on_ok: Option<ActionEnvelope>,
163 pub on_err: Option<ActionEnvelope>,
165 pub service_bindings: Option<ServiceBindings>,
167 pub resource: Option<ResourceExecutionContext>,
169}
170
171#[derive(Clone, Debug, PartialEq)]
191pub enum ActionInput {
192 None,
194 RouteChanged { location: RouteLocation },
196 JobOk {
198 job_name: String,
199 req_id: u64,
200 payload: Vec<u8>,
201 },
202 JobErr {
204 job_name: String,
205 req_id: u64,
206 payload: Option<Vec<u8>>,
207 message: Option<String>,
208 },
209 ServiceStarted {
211 service_name: String,
212 slot_key: String,
213 instance_id: u64,
214 },
215 ServiceStartFailed {
217 service_name: String,
218 slot_key: String,
219 payload: Option<Vec<u8>>,
220 message: Option<String>,
221 },
222 ServiceEvent {
224 service_name: String,
225 slot_key: String,
226 instance_id: u64,
227 payload: Vec<u8>,
228 },
229 ServiceStopped {
231 service_name: String,
232 slot_key: String,
233 instance_id: u64,
234 },
235 ServiceCommandOk {
237 service_name: String,
238 slot_key: String,
239 instance_id: u64,
240 req_id: u64,
241 payload: Option<Vec<u8>>,
242 },
243 ServiceCommandErr {
245 service_name: String,
246 slot_key: String,
247 instance_id: u64,
248 req_id: u64,
249 payload: Option<Vec<u8>>,
250 message: Option<String>,
251 },
252 CapabilityOk {
254 capability: String,
255 req_id: u64,
256 payload: Vec<u8>,
257 },
258 CapabilityErr {
260 capability: String,
261 req_id: u64,
262 payload: Option<Vec<u8>>,
263 message: Option<String>,
264 },
265 TimerTick { payload: Vec<u8> },
267 Pointer {
269 x: f32,
270 y: f32,
271 delta_x: f32,
272 delta_y: f32,
273 },
274 Drop {
276 paths: Vec<String>,
277 x: f32,
278 y: f32,
279 modifiers: u8,
282 },
283 InternalDrop {
285 payload: Vec<u8>,
286 x: f32,
287 y: f32,
288 modifiers: u8,
291 },
292 ScopedRaw {
294 scope_id: u128,
295 target: WidgetId,
296 input: Box<ActionInput>,
297 },
298}
299
300impl ActionInput {
301 pub fn scoped_raw(scope_id: u128, target: WidgetId, input: ActionInput) -> Self {
302 Self::ScopedRaw {
303 scope_id,
304 target: target.into(),
305 input: Box::new(input),
306 }
307 }
308
309 pub fn action_scope_id(&self) -> Option<u128> {
310 match self {
311 ActionInput::ScopedRaw { scope_id, .. } => Some(*scope_id),
312 _ => None,
313 }
314 }
315
316 pub fn scoped_target(&self) -> Option<WidgetId> {
317 match self {
318 ActionInput::ScopedRaw { target, .. } => Some(*target),
319 _ => None,
320 }
321 }
322
323 pub fn unscoped(&self) -> &ActionInput {
324 match self {
325 ActionInput::ScopedRaw { input, .. } => input.unscoped(),
326 _ => self,
327 }
328 }
329
330 pub fn as_bytes(&self) -> Option<&[u8]> {
331 match self.unscoped() {
332 ActionInput::JobOk { payload, .. } => Some(payload),
333 ActionInput::CapabilityOk { payload, .. } => Some(payload),
334 ActionInput::TimerTick { payload } => Some(payload),
335 ActionInput::InternalDrop { payload, .. } => Some(payload),
336 _ => None,
337 }
338 }
339
340 pub fn as_pointer(&self) -> Option<(f32, f32, f32, f32)> {
341 match self.unscoped() {
342 ActionInput::Pointer {
343 x,
344 y,
345 delta_x,
346 delta_y,
347 } => Some((*x, *y, *delta_x, *delta_y)),
348 ActionInput::Drop { x, y, .. } => Some((*x, *y, 0.0, 0.0)),
349 ActionInput::InternalDrop { x, y, .. } => Some((*x, *y, 0.0, 0.0)),
350 _ => None,
351 }
352 }
353
354 pub fn as_drop_paths(&self) -> Option<&[String]> {
355 match self.unscoped() {
356 ActionInput::Drop { paths, .. } => Some(paths),
357 _ => None,
358 }
359 }
360
361 pub fn as_internal_drop(&self) -> Option<&[u8]> {
362 match self.unscoped() {
363 ActionInput::InternalDrop { payload, .. } => Some(payload),
364 _ => None,
365 }
366 }
367
368 pub fn as_drop_modifiers(&self) -> Option<u8> {
373 match self.unscoped() {
374 ActionInput::Drop { modifiers, .. } => Some(*modifiers),
375 ActionInput::InternalDrop { modifiers, .. } => Some(*modifiers),
376 _ => None,
377 }
378 }
379
380 pub fn job_ok<J: JobSpec>(&self, job: JobRef<J>) -> Option<J::Ok> {
381 match self.unscoped() {
382 ActionInput::JobOk {
383 job_name, payload, ..
384 } if job_name == job.name => serde_json::from_slice(payload).ok(),
385 _ => None,
386 }
387 }
388
389 pub fn job_err<J: JobSpec>(&self, job: JobRef<J>) -> Option<J::Err> {
390 match self.unscoped() {
391 ActionInput::JobErr {
392 job_name,
393 payload: Some(payload),
394 ..
395 } if job_name == job.name => serde_json::from_slice(payload).ok(),
396 _ => None,
397 }
398 }
399
400 pub fn job_error_message<J: JobSpec>(&self, job: JobRef<J>) -> Option<&str> {
401 match self.unscoped() {
402 ActionInput::JobErr {
403 job_name,
404 message: Some(message),
405 ..
406 } if job_name == job.name => Some(message.as_str()),
407 _ => None,
408 }
409 }
410
411 pub fn capability_ok<C: OperationCapability>(
412 &self,
413 capability: CapabilityType<C>,
414 ) -> Option<C::Ok> {
415 match self.unscoped() {
416 ActionInput::CapabilityOk {
417 capability: actual,
418 payload,
419 ..
420 } if actual == capability.name => serde_json::from_slice(payload).ok(),
421 _ => None,
422 }
423 }
424
425 pub fn capability_error<C: OperationCapability>(
426 &self,
427 capability: CapabilityType<C>,
428 ) -> Option<C::Err> {
429 match self.unscoped() {
430 ActionInput::CapabilityErr {
431 capability: actual,
432 payload: Some(payload),
433 ..
434 } if actual == capability.name => serde_json::from_slice(payload).ok(),
435 _ => None,
436 }
437 }
438
439 pub fn capability_error_message<C: OperationCapability>(
440 &self,
441 capability: CapabilityType<C>,
442 ) -> Option<&str> {
443 match self.unscoped() {
444 ActionInput::CapabilityErr {
445 capability: actual,
446 message: Some(message),
447 ..
448 } if actual == capability.name => Some(message),
449 _ => None,
450 }
451 }
452
453 pub fn service_event<S: ServiceSpec>(&self, service: ServiceType<S>) -> Option<S::Event> {
454 match self.unscoped() {
455 ActionInput::ServiceEvent {
456 service_name,
457 payload,
458 ..
459 } if service_name == service.name => serde_json::from_slice(payload).ok(),
460 _ => None,
461 }
462 }
463
464 pub fn service_start_err<S: ServiceSpec>(
465 &self,
466 service: ServiceType<S>,
467 ) -> Option<S::StartErr> {
468 match self.unscoped() {
469 ActionInput::ServiceStartFailed {
470 service_name,
471 payload: Some(payload),
472 ..
473 } if service_name == service.name => serde_json::from_slice(payload).ok(),
474 _ => None,
475 }
476 }
477
478 pub fn service_start_error_message<S: ServiceSpec>(
479 &self,
480 service: ServiceType<S>,
481 ) -> Option<&str> {
482 match self.unscoped() {
483 ActionInput::ServiceStartFailed {
484 service_name,
485 message: Some(message),
486 ..
487 } if service_name == service.name => Some(message.as_str()),
488 _ => None,
489 }
490 }
491
492 pub fn service_command_ok<S: ServiceSpec>(
493 &self,
494 service: ServiceType<S>,
495 ) -> Option<S::CommandOk> {
496 match self.unscoped() {
497 ActionInput::ServiceCommandOk {
498 service_name,
499 payload: Some(payload),
500 ..
501 } if service_name == service.name => serde_json::from_slice(payload).ok(),
502 _ => None,
503 }
504 }
505
506 pub fn service_command_err<S: ServiceSpec>(
507 &self,
508 service: ServiceType<S>,
509 ) -> Option<S::CommandErr> {
510 match self.unscoped() {
511 ActionInput::ServiceCommandErr {
512 service_name,
513 payload: Some(payload),
514 ..
515 } if service_name == service.name => serde_json::from_slice(payload).ok(),
516 _ => None,
517 }
518 }
519
520 pub fn timer_tick<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
521 match self.unscoped() {
522 ActionInput::TimerTick { payload } => serde_json::from_slice(payload).ok(),
523 _ => None,
524 }
525 }
526
527 pub fn service_slot_key(&self) -> Option<&str> {
528 match self.unscoped() {
529 ActionInput::ServiceStarted { slot_key, .. }
530 | ActionInput::ServiceStartFailed { slot_key, .. }
531 | ActionInput::ServiceEvent { slot_key, .. }
532 | ActionInput::ServiceStopped { slot_key, .. }
533 | ActionInput::ServiceCommandOk { slot_key, .. }
534 | ActionInput::ServiceCommandErr { slot_key, .. } => Some(slot_key.as_str()),
535 _ => None,
536 }
537 }
538
539 pub fn service_instance_id(&self) -> Option<u64> {
540 match self.unscoped() {
541 ActionInput::ServiceStarted { instance_id, .. }
542 | ActionInput::ServiceEvent { instance_id, .. }
543 | ActionInput::ServiceStopped { instance_id, .. }
544 | ActionInput::ServiceCommandOk { instance_id, .. }
545 | ActionInput::ServiceCommandErr { instance_id, .. } => Some(*instance_id),
546 _ => None,
547 }
548 }
549}