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