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 { paths: Vec<String>, x: f32, y: f32 },
276 InternalDrop { payload: Vec<u8>, x: f32, y: f32 },
278 ScopedRaw {
280 scope_id: u128,
281 target: WidgetId,
282 input: Box<ActionInput>,
283 },
284}
285
286impl ActionInput {
287 pub fn scoped_raw(scope_id: u128, target: WidgetId, input: ActionInput) -> Self {
288 Self::ScopedRaw {
289 scope_id,
290 target: target.into(),
291 input: Box::new(input),
292 }
293 }
294
295 pub fn action_scope_id(&self) -> Option<u128> {
296 match self {
297 ActionInput::ScopedRaw { scope_id, .. } => Some(*scope_id),
298 _ => None,
299 }
300 }
301
302 pub fn scoped_target(&self) -> Option<WidgetId> {
303 match self {
304 ActionInput::ScopedRaw { target, .. } => Some(*target),
305 _ => None,
306 }
307 }
308
309 pub fn unscoped(&self) -> &ActionInput {
310 match self {
311 ActionInput::ScopedRaw { input, .. } => input.unscoped(),
312 _ => self,
313 }
314 }
315
316 pub fn as_bytes(&self) -> Option<&[u8]> {
317 match self.unscoped() {
318 ActionInput::JobOk { payload, .. } => Some(payload),
319 ActionInput::CapabilityOk { payload, .. } => Some(payload),
320 ActionInput::TimerTick { payload } => Some(payload),
321 ActionInput::InternalDrop { payload, .. } => Some(payload),
322 _ => None,
323 }
324 }
325
326 pub fn as_pointer(&self) -> Option<(f32, f32, f32, f32)> {
327 match self.unscoped() {
328 ActionInput::Pointer {
329 x,
330 y,
331 delta_x,
332 delta_y,
333 } => Some((*x, *y, *delta_x, *delta_y)),
334 ActionInput::Drop { x, y, .. } => Some((*x, *y, 0.0, 0.0)),
335 ActionInput::InternalDrop { x, y, .. } => Some((*x, *y, 0.0, 0.0)),
336 _ => None,
337 }
338 }
339
340 pub fn as_drop_paths(&self) -> Option<&[String]> {
341 match self.unscoped() {
342 ActionInput::Drop { paths, .. } => Some(paths),
343 _ => None,
344 }
345 }
346
347 pub fn as_internal_drop(&self) -> Option<&[u8]> {
348 match self.unscoped() {
349 ActionInput::InternalDrop { payload, .. } => Some(payload),
350 _ => None,
351 }
352 }
353
354 pub fn job_ok<J: JobSpec>(&self, job: JobRef<J>) -> Option<J::Ok> {
355 match self.unscoped() {
356 ActionInput::JobOk {
357 job_name, payload, ..
358 } if job_name == job.name => serde_json::from_slice(payload).ok(),
359 _ => None,
360 }
361 }
362
363 pub fn job_err<J: JobSpec>(&self, job: JobRef<J>) -> Option<J::Err> {
364 match self.unscoped() {
365 ActionInput::JobErr {
366 job_name,
367 payload: Some(payload),
368 ..
369 } if job_name == job.name => serde_json::from_slice(payload).ok(),
370 _ => None,
371 }
372 }
373
374 pub fn job_error_message<J: JobSpec>(&self, job: JobRef<J>) -> Option<&str> {
375 match self.unscoped() {
376 ActionInput::JobErr {
377 job_name,
378 message: Some(message),
379 ..
380 } if job_name == job.name => Some(message.as_str()),
381 _ => None,
382 }
383 }
384
385 pub fn capability_ok<C: OperationCapability>(
386 &self,
387 capability: CapabilityType<C>,
388 ) -> Option<C::Ok> {
389 match self.unscoped() {
390 ActionInput::CapabilityOk {
391 capability: actual,
392 payload,
393 ..
394 } if actual == capability.name => serde_json::from_slice(payload).ok(),
395 _ => None,
396 }
397 }
398
399 pub fn capability_error<C: OperationCapability>(
400 &self,
401 capability: CapabilityType<C>,
402 ) -> Option<C::Err> {
403 match self.unscoped() {
404 ActionInput::CapabilityErr {
405 capability: actual,
406 payload: Some(payload),
407 ..
408 } if actual == capability.name => serde_json::from_slice(payload).ok(),
409 _ => None,
410 }
411 }
412
413 pub fn capability_error_message<C: OperationCapability>(
414 &self,
415 capability: CapabilityType<C>,
416 ) -> Option<&str> {
417 match self.unscoped() {
418 ActionInput::CapabilityErr {
419 capability: actual,
420 message: Some(message),
421 ..
422 } if actual == capability.name => Some(message),
423 _ => None,
424 }
425 }
426
427 pub fn service_event<S: ServiceSpec>(&self, service: ServiceType<S>) -> Option<S::Event> {
428 match self.unscoped() {
429 ActionInput::ServiceEvent {
430 service_name,
431 payload,
432 ..
433 } if service_name == service.name => serde_json::from_slice(payload).ok(),
434 _ => None,
435 }
436 }
437
438 pub fn service_start_err<S: ServiceSpec>(
439 &self,
440 service: ServiceType<S>,
441 ) -> Option<S::StartErr> {
442 match self.unscoped() {
443 ActionInput::ServiceStartFailed {
444 service_name,
445 payload: Some(payload),
446 ..
447 } if service_name == service.name => serde_json::from_slice(payload).ok(),
448 _ => None,
449 }
450 }
451
452 pub fn service_start_error_message<S: ServiceSpec>(
453 &self,
454 service: ServiceType<S>,
455 ) -> Option<&str> {
456 match self.unscoped() {
457 ActionInput::ServiceStartFailed {
458 service_name,
459 message: Some(message),
460 ..
461 } if service_name == service.name => Some(message.as_str()),
462 _ => None,
463 }
464 }
465
466 pub fn service_command_ok<S: ServiceSpec>(
467 &self,
468 service: ServiceType<S>,
469 ) -> Option<S::CommandOk> {
470 match self.unscoped() {
471 ActionInput::ServiceCommandOk {
472 service_name,
473 payload: Some(payload),
474 ..
475 } if service_name == service.name => serde_json::from_slice(payload).ok(),
476 _ => None,
477 }
478 }
479
480 pub fn service_command_err<S: ServiceSpec>(
481 &self,
482 service: ServiceType<S>,
483 ) -> Option<S::CommandErr> {
484 match self.unscoped() {
485 ActionInput::ServiceCommandErr {
486 service_name,
487 payload: Some(payload),
488 ..
489 } if service_name == service.name => serde_json::from_slice(payload).ok(),
490 _ => None,
491 }
492 }
493
494 pub fn timer_tick<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
495 match self.unscoped() {
496 ActionInput::TimerTick { payload } => serde_json::from_slice(payload).ok(),
497 _ => None,
498 }
499 }
500
501 pub fn service_slot_key(&self) -> Option<&str> {
502 match self.unscoped() {
503 ActionInput::ServiceStarted { slot_key, .. }
504 | ActionInput::ServiceStartFailed { slot_key, .. }
505 | ActionInput::ServiceEvent { slot_key, .. }
506 | ActionInput::ServiceStopped { slot_key, .. }
507 | ActionInput::ServiceCommandOk { slot_key, .. }
508 | ActionInput::ServiceCommandErr { slot_key, .. } => Some(slot_key.as_str()),
509 _ => None,
510 }
511 }
512
513 pub fn service_instance_id(&self) -> Option<u64> {
514 match self.unscoped() {
515 ActionInput::ServiceStarted { instance_id, .. }
516 | ActionInput::ServiceEvent { instance_id, .. }
517 | ActionInput::ServiceStopped { instance_id, .. }
518 | ActionInput::ServiceCommandOk { instance_id, .. }
519 | ActionInput::ServiceCommandErr { instance_id, .. } => Some(*instance_id),
520 _ => None,
521 }
522 }
523}