1use serde::Serialize;
8use serde_json::Value;
9
10#[derive(Debug, Serialize)]
24pub struct OutgoingEvent {
25 #[serde(rename = "type")]
27 pub message_type: &'static str,
28 pub session: String,
30 pub family: String,
32 pub id: String,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub value: Option<Value>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub tag: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub modifiers: Option<KeyModifiers>,
43 #[serde(skip_serializing_if = "Option::is_none")]
46 pub data: Option<Value>,
47 #[serde(skip_serializing_if = "Option::is_none")]
51 pub captured: Option<bool>,
52}
53
54impl OutgoingEvent {
55 pub fn with_captured(mut self, captured: bool) -> Self {
57 self.captured = Some(captured);
58 self
59 }
60
61 pub fn with_session(mut self, session: impl Into<String>) -> Self {
63 self.session = session.into();
64 self
65 }
66}
67
68#[derive(Debug, Serialize)]
70pub struct KeyModifiers {
71 pub shift: bool,
72 pub ctrl: bool,
73 pub alt: bool,
74 pub logo: bool,
75 pub command: bool,
76}
77
78impl OutgoingEvent {
83 fn bare(family: impl Into<String>, id: String) -> Self {
85 Self {
86 message_type: "event",
87 session: String::new(),
88 family: family.into(),
89 id,
90 value: None,
91 tag: None,
92 modifiers: None,
93 data: None,
94 captured: None,
95 }
96 }
97
98 fn tagged(family: impl Into<String>, tag: String) -> Self {
100 Self {
101 message_type: "event",
102 session: String::new(),
103 family: family.into(),
104 id: String::new(),
105 value: None,
106 tag: Some(tag),
107 modifiers: None,
108 data: None,
109 captured: None,
110 }
111 }
112
113 pub fn generic(family: impl Into<String>, id: String, data: Option<Value>) -> Self {
116 Self {
117 data,
118 ..Self::bare(family, id)
119 }
120 }
121
122 pub fn extension_event(family: String, id: String, data: Option<Value>) -> Self {
124 Self::generic(family, id, data)
125 }
126
127 pub fn click(id: String) -> Self {
128 Self::bare("click", id)
129 }
130
131 pub fn input(id: String, value: String) -> Self {
132 Self {
133 value: Some(Value::String(value)),
134 ..Self::bare("input", id)
135 }
136 }
137
138 pub fn submit(id: String, value: String) -> Self {
139 Self {
140 value: Some(Value::String(value)),
141 ..Self::bare("submit", id)
142 }
143 }
144
145 pub fn toggle(id: String, checked: bool) -> Self {
146 Self {
147 value: Some(Value::Bool(checked)),
148 ..Self::bare("toggle", id)
149 }
150 }
151
152 pub fn slide(id: String, value: f64) -> Self {
153 Self {
154 value: Some(serde_json::json!(sanitize_f64(value))),
155 ..Self::bare("slide", id)
156 }
157 }
158
159 pub fn slide_release(id: String, value: f64) -> Self {
160 Self {
161 value: Some(serde_json::json!(sanitize_f64(value))),
162 ..Self::bare("slide_release", id)
163 }
164 }
165
166 pub fn select(id: String, value: String) -> Self {
167 Self {
168 value: Some(Value::String(value)),
169 ..Self::bare("select", id)
170 }
171 }
172
173 pub fn key_press(tag: String, data: &crate::message::KeyEventData) -> Self {
178 Self {
179 modifiers: Some(crate::message::serialize_modifiers(data.modifiers)),
180 value: Some(Value::String(crate::message::serialize_key(&data.key))),
181 data: Some(serde_json::json!({
182 "modified_key": crate::message::serialize_key(&data.modified_key),
183 "physical_key": crate::message::serialize_physical_key(&data.physical_key),
184 "location": crate::message::serialize_location(&data.location),
185 "text": data.text.as_deref(),
186 "repeat": data.repeat,
187 })),
188 ..Self::tagged("key_press", tag)
189 }
190 }
191
192 pub fn key_release(tag: String, data: &crate::message::KeyEventData) -> Self {
193 Self {
194 modifiers: Some(crate::message::serialize_modifiers(data.modifiers)),
195 value: Some(Value::String(crate::message::serialize_key(&data.key))),
196 data: Some(serde_json::json!({
197 "modified_key": crate::message::serialize_key(&data.modified_key),
198 "physical_key": crate::message::serialize_physical_key(&data.physical_key),
199 "location": crate::message::serialize_location(&data.location),
200 })),
201 ..Self::tagged("key_release", tag)
202 }
203 }
204
205 pub fn modifiers_changed(tag: String, modifiers: KeyModifiers) -> Self {
206 Self {
207 modifiers: Some(modifiers),
208 ..Self::tagged("modifiers_changed", tag)
209 }
210 }
211
212 pub fn cursor_moved(tag: String, x: f32, y: f32) -> Self {
217 Self {
218 data: Some(serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y)})),
219 ..Self::tagged("cursor_moved", tag)
220 }
221 }
222
223 pub fn cursor_entered(tag: String) -> Self {
224 Self::tagged("cursor_entered", tag)
225 }
226
227 pub fn cursor_left(tag: String) -> Self {
228 Self::tagged("cursor_left", tag)
229 }
230
231 pub fn button_pressed(tag: String, button: String) -> Self {
232 Self {
233 value: Some(Value::String(button)),
234 ..Self::tagged("button_pressed", tag)
235 }
236 }
237
238 pub fn button_released(tag: String, button: String) -> Self {
239 Self {
240 value: Some(Value::String(button)),
241 ..Self::tagged("button_released", tag)
242 }
243 }
244
245 pub fn wheel_scrolled(tag: String, delta_x: f32, delta_y: f32, unit: &str) -> Self {
246 Self {
247 data: Some(serde_json::json!({
248 "delta_x": sanitize_f32(delta_x),
249 "delta_y": sanitize_f32(delta_y),
250 "unit": unit,
251 })),
252 ..Self::tagged("wheel_scrolled", tag)
253 }
254 }
255
256 fn touch_event(family: &str, tag: String, finger_id: u64, x: f32, y: f32) -> Self {
261 Self {
262 data: Some(serde_json::json!({
263 "finger_id": finger_id,
264 "x": sanitize_f32(x),
265 "y": sanitize_f32(y),
266 })),
267 ..Self::tagged(family, tag)
268 }
269 }
270
271 pub fn finger_pressed(tag: String, finger_id: u64, x: f32, y: f32) -> Self {
272 Self::touch_event("finger_pressed", tag, finger_id, x, y)
273 }
274
275 pub fn finger_moved(tag: String, finger_id: u64, x: f32, y: f32) -> Self {
276 Self::touch_event("finger_moved", tag, finger_id, x, y)
277 }
278
279 pub fn finger_lifted(tag: String, finger_id: u64, x: f32, y: f32) -> Self {
280 Self::touch_event("finger_lifted", tag, finger_id, x, y)
281 }
282
283 pub fn finger_lost(tag: String, finger_id: u64, x: f32, y: f32) -> Self {
284 Self::touch_event("finger_lost", tag, finger_id, x, y)
285 }
286
287 pub fn ime_opened(tag: String) -> Self {
292 Self {
293 data: Some(serde_json::json!({"kind": "opened"})),
294 ..Self::tagged("ime", tag)
295 }
296 }
297
298 pub fn ime_preedit(tag: String, text: String, cursor: Option<std::ops::Range<usize>>) -> Self {
299 let cursor_val = cursor
300 .map(|r| serde_json::json!({"start": r.start, "end": r.end}))
301 .unwrap_or(serde_json::Value::Null);
302 Self {
303 data: Some(serde_json::json!({"kind": "preedit", "text": text, "cursor": cursor_val})),
304 ..Self::tagged("ime", tag)
305 }
306 }
307
308 pub fn ime_commit(tag: String, text: String) -> Self {
309 Self {
310 data: Some(serde_json::json!({"kind": "commit", "text": text})),
311 ..Self::tagged("ime", tag)
312 }
313 }
314
315 pub fn ime_closed(tag: String) -> Self {
316 Self {
317 data: Some(serde_json::json!({"kind": "closed"})),
318 ..Self::tagged("ime", tag)
319 }
320 }
321
322 pub fn window_opened(
327 tag: String,
328 window_id: String,
329 position: Option<(f32, f32)>,
330 width: f32,
331 height: f32,
332 scale_factor: f32,
333 ) -> Self {
334 let pos =
335 position.map(|(x, y)| serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y)}));
336 Self {
337 data: Some(serde_json::json!({
338 "window_id": window_id,
339 "position": pos,
340 "width": sanitize_f32(width),
341 "height": sanitize_f32(height),
342 "scale_factor": sanitize_f32(scale_factor),
343 })),
344 ..Self::tagged("window_opened", tag)
345 }
346 }
347
348 fn window_event(family: &str, tag: String, window_id: String) -> Self {
350 Self {
351 data: Some(serde_json::json!({"window_id": window_id})),
352 ..Self::tagged(family, tag)
353 }
354 }
355
356 pub fn window_closed(tag: String, window_id: String) -> Self {
357 Self::window_event("window_closed", tag, window_id)
358 }
359
360 pub fn window_close_requested(tag: String, window_id: String) -> Self {
361 Self::window_event("window_close_requested", tag, window_id)
362 }
363
364 pub fn window_moved(tag: String, window_id: String, x: f32, y: f32) -> Self {
365 Self {
366 data: Some(serde_json::json!({
367 "window_id": window_id,
368 "x": sanitize_f32(x),
369 "y": sanitize_f32(y),
370 })),
371 ..Self::tagged("window_moved", tag)
372 }
373 }
374
375 pub fn window_resized(tag: String, window_id: String, width: f32, height: f32) -> Self {
376 Self {
377 data: Some(serde_json::json!({
378 "window_id": window_id,
379 "width": sanitize_f32(width),
380 "height": sanitize_f32(height),
381 })),
382 ..Self::tagged("window_resized", tag)
383 }
384 }
385
386 pub fn window_focused(tag: String, window_id: String) -> Self {
387 Self::window_event("window_focused", tag, window_id)
388 }
389
390 pub fn window_unfocused(tag: String, window_id: String) -> Self {
391 Self::window_event("window_unfocused", tag, window_id)
392 }
393
394 pub fn window_rescaled(tag: String, window_id: String, scale_factor: f32) -> Self {
395 Self {
396 data: Some(serde_json::json!({
397 "window_id": window_id,
398 "scale_factor": sanitize_f32(scale_factor),
399 })),
400 ..Self::tagged("window_rescaled", tag)
401 }
402 }
403
404 pub fn file_hovered(tag: String, window_id: String, path: String) -> Self {
405 Self {
406 data: Some(serde_json::json!({
407 "window_id": window_id,
408 "path": path,
409 })),
410 ..Self::tagged("file_hovered", tag)
411 }
412 }
413
414 pub fn file_dropped(tag: String, window_id: String, path: String) -> Self {
415 Self {
416 data: Some(serde_json::json!({
417 "window_id": window_id,
418 "path": path,
419 })),
420 ..Self::tagged("file_dropped", tag)
421 }
422 }
423
424 pub fn files_hovered_left(tag: String, window_id: String) -> Self {
425 Self::window_event("files_hovered_left", tag, window_id)
426 }
427
428 pub fn animation_frame(tag: String, timestamp_millis: u128) -> Self {
433 Self {
434 data: Some(serde_json::json!({"timestamp": timestamp_millis})),
435 ..Self::tagged("animation_frame", tag)
436 }
437 }
438
439 pub fn theme_changed(tag: String, mode: String) -> Self {
440 Self {
441 value: Some(Value::String(mode)),
442 ..Self::tagged("theme_changed", tag)
443 }
444 }
445
446 pub fn sensor_resize(id: String, width: f32, height: f32) -> Self {
451 Self {
452 data: Some(
453 serde_json::json!({"width": sanitize_f32(width), "height": sanitize_f32(height)}),
454 ),
455 ..Self::bare("sensor_resize", id)
456 }
457 }
458
459 pub fn canvas_press(id: String, x: f32, y: f32, button: String) -> Self {
464 Self {
465 data: Some(
466 serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y), "button": button}),
467 ),
468 ..Self::bare("canvas_press", id)
469 }
470 }
471
472 pub fn canvas_release(id: String, x: f32, y: f32, button: String) -> Self {
473 Self {
474 data: Some(
475 serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y), "button": button}),
476 ),
477 ..Self::bare("canvas_release", id)
478 }
479 }
480
481 pub fn canvas_move(id: String, x: f32, y: f32) -> Self {
482 Self {
483 data: Some(serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y)})),
484 ..Self::bare("canvas_move", id)
485 }
486 }
487
488 pub fn canvas_scroll(id: String, x: f32, y: f32, delta_x: f32, delta_y: f32) -> Self {
489 Self {
490 data: Some(
491 serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y), "delta_x": sanitize_f32(delta_x), "delta_y": sanitize_f32(delta_y)}),
492 ),
493 ..Self::bare("canvas_scroll", id)
494 }
495 }
496
497 pub fn mouse_right_press(id: String) -> Self {
502 Self::bare("mouse_right_press", id)
503 }
504
505 pub fn mouse_right_release(id: String) -> Self {
506 Self::bare("mouse_right_release", id)
507 }
508
509 pub fn mouse_middle_press(id: String) -> Self {
510 Self::bare("mouse_middle_press", id)
511 }
512
513 pub fn mouse_middle_release(id: String) -> Self {
514 Self::bare("mouse_middle_release", id)
515 }
516
517 pub fn mouse_double_click(id: String) -> Self {
518 Self::bare("mouse_double_click", id)
519 }
520
521 pub fn mouse_enter(id: String) -> Self {
522 Self::bare("mouse_enter", id)
523 }
524
525 pub fn mouse_exit(id: String) -> Self {
526 Self::bare("mouse_exit", id)
527 }
528
529 pub fn mouse_area_move(id: String, x: f32, y: f32) -> Self {
530 Self {
531 data: Some(serde_json::json!({"x": sanitize_f32(x), "y": sanitize_f32(y)})),
532 ..Self::bare("mouse_move", id)
533 }
534 }
535
536 pub fn mouse_area_scroll(id: String, delta_x: f32, delta_y: f32) -> Self {
537 Self {
538 data: Some(
539 serde_json::json!({"delta_x": sanitize_f32(delta_x), "delta_y": sanitize_f32(delta_y)}),
540 ),
541 ..Self::bare("mouse_scroll", id)
542 }
543 }
544
545 pub fn pane_resized(id: String, split: String, ratio: f32) -> Self {
550 Self {
551 data: Some(serde_json::json!({"split": split, "ratio": sanitize_f32(ratio)})),
552 ..Self::bare("pane_resized", id)
553 }
554 }
555
556 pub fn pane_dragged(
557 id: String,
558 kind: &str,
559 pane: String,
560 target: Option<String>,
561 region: Option<&str>,
562 edge: Option<&str>,
563 ) -> Self {
564 let mut data = serde_json::json!({"action": kind, "pane": pane});
565 if let Some(t) = target {
566 data["target"] = serde_json::json!(t);
567 }
568 if let Some(r) = region {
569 data["region"] = serde_json::json!(r);
570 }
571 if let Some(e) = edge {
572 data["edge"] = serde_json::json!(e);
573 }
574 Self {
575 data: Some(data),
576 ..Self::bare("pane_dragged", id)
577 }
578 }
579
580 pub fn pane_clicked(id: String, pane: String) -> Self {
581 Self {
582 data: Some(serde_json::json!({"pane": pane})),
583 ..Self::bare("pane_clicked", id)
584 }
585 }
586
587 pub fn pane_focus_cycle(id: String, pane: String) -> Self {
588 Self {
589 data: Some(serde_json::json!({"pane": pane})),
590 ..Self::bare("pane_focus_cycle", id)
591 }
592 }
593
594 pub fn paste(id: String, text: String) -> Self {
599 Self {
600 value: Some(Value::String(text)),
601 ..Self::bare("paste", id)
602 }
603 }
604
605 pub fn scripting_key_press(key: String, modifiers_json: Value) -> Self {
611 Self {
612 value: Some(Value::String(key)),
613 data: Some(serde_json::json!({"modifiers": modifiers_json})),
614 ..Self::bare("key_press", String::new())
615 }
616 }
617
618 pub fn scripting_key_release(key: String, modifiers_json: Value) -> Self {
620 Self {
621 value: Some(Value::String(key)),
622 data: Some(serde_json::json!({"modifiers": modifiers_json})),
623 ..Self::bare("key_release", String::new())
624 }
625 }
626
627 pub fn scripting_cursor_moved(x: f64, y: f64) -> Self {
629 Self {
630 data: Some(serde_json::json!({"x": x, "y": y})),
631 ..Self::bare("cursor_moved", String::new())
632 }
633 }
634
635 pub fn scripting_scroll(delta_x: f64, delta_y: f64) -> Self {
637 Self {
638 data: Some(serde_json::json!({"delta_x": delta_x, "delta_y": delta_y})),
639 ..Self::bare("scroll", String::new())
640 }
641 }
642
643 pub fn option_hovered(id: String, value: String) -> Self {
648 Self {
649 value: Some(Value::String(value)),
650 ..Self::bare("option_hovered", id)
651 }
652 }
653
654 #[allow(clippy::too_many_arguments)]
659 pub fn scroll(
660 id: String,
661 abs_x: f32,
662 abs_y: f32,
663 rel_x: f32,
664 rel_y: f32,
665 bounds_w: f32,
666 bounds_h: f32,
667 content_w: f32,
668 content_h: f32,
669 ) -> Self {
670 Self {
671 data: Some(serde_json::json!({
672 "absolute_x": sanitize_f32(abs_x), "absolute_y": sanitize_f32(abs_y),
673 "relative_x": sanitize_f32(rel_x), "relative_y": sanitize_f32(rel_y),
674 "bounds_width": sanitize_f32(bounds_w), "bounds_height": sanitize_f32(bounds_h),
675 "content_width": sanitize_f32(content_w), "content_height": sanitize_f32(content_h),
676 })),
677 ..Self::bare("scroll", id)
678 }
679 }
680}
681
682fn sanitize_f32(v: f32) -> f32 {
688 if v.is_finite() {
689 v
690 } else {
691 log::warn!("non-finite f32 ({v}) replaced with 0.0 in outgoing event");
692 0.0
693 }
694}
695
696fn sanitize_f64(v: f64) -> f64 {
698 if v.is_finite() {
699 v
700 } else {
701 log::warn!("non-finite f64 ({v}) replaced with 0.0 in outgoing event");
702 0.0
703 }
704}
705
706#[derive(Debug, Serialize)]
712pub struct EffectResponse {
713 #[serde(rename = "type")]
714 pub message_type: &'static str,
715 pub session: String,
716 pub id: String,
717 pub status: &'static str,
718 #[serde(skip_serializing_if = "Option::is_none")]
719 pub result: Option<Value>,
720 #[serde(skip_serializing_if = "Option::is_none")]
721 pub error: Option<String>,
722}
723
724impl EffectResponse {
725 pub fn ok(id: String, result: Value) -> Self {
727 Self {
728 message_type: "effect_response",
729 session: String::new(),
730 id,
731 status: "ok",
732 result: Some(result),
733 error: None,
734 }
735 }
736
737 pub fn error(id: String, reason: String) -> Self {
739 Self {
740 message_type: "effect_response",
741 session: String::new(),
742 id,
743 status: "error",
744 result: None,
745 error: Some(reason),
746 }
747 }
748
749 pub fn unsupported(id: String) -> Self {
751 Self::error(id, "unsupported".to_string())
752 }
753
754 pub fn cancelled(id: String) -> Self {
758 Self {
759 message_type: "effect_response",
760 session: String::new(),
761 id,
762 status: "cancelled",
763 result: None,
764 error: None,
765 }
766 }
767
768 pub fn with_session(mut self, session: impl Into<String>) -> Self {
770 self.session = session.into();
771 self
772 }
773}
774
775#[derive(Debug, Serialize)]
777pub struct QueryResponse {
778 #[serde(rename = "type")]
779 pub message_type: &'static str,
780 pub session: String,
781 pub id: String,
782 pub target: String,
783 pub data: Value,
784}
785
786impl QueryResponse {
787 pub fn new(id: String, target: String, data: Value) -> Self {
788 Self {
789 message_type: "query_response",
790 session: String::new(),
791 id,
792 target,
793 data,
794 }
795 }
796
797 pub fn with_session(mut self, session: impl Into<String>) -> Self {
799 self.session = session.into();
800 self
801 }
802}
803
804#[derive(Debug, Serialize)]
806pub struct InteractResponse {
807 #[serde(rename = "type")]
808 pub message_type: &'static str,
809 pub session: String,
810 pub id: String,
811 pub events: Vec<OutgoingEvent>,
812}
813
814impl InteractResponse {
815 pub fn new(id: String, events: Vec<OutgoingEvent>) -> Self {
816 Self {
817 message_type: "interact_response",
818 session: String::new(),
819 id,
820 events,
821 }
822 }
823
824 pub fn with_session(mut self, session: impl Into<String>) -> Self {
826 let session = session.into();
827 for event in &mut self.events {
828 event.session.clone_from(&session);
829 }
830 self.session = session;
831 self
832 }
833}
834
835#[derive(Debug, Serialize)]
840#[allow(dead_code)]
841pub struct TreeHashResponse {
842 #[serde(rename = "type")]
843 pub message_type: &'static str,
844 pub session: String,
845 pub id: String,
846 pub name: String,
847 pub hash: String,
848}
849
850#[allow(dead_code)]
851impl TreeHashResponse {
852 pub fn new(id: String, name: String, hash: String) -> Self {
853 Self {
854 message_type: "tree_hash_response",
855 session: String::new(),
856 id,
857 name,
858 hash,
859 }
860 }
861
862 pub fn with_session(mut self, session: impl Into<String>) -> Self {
864 self.session = session.into();
865 self
866 }
867}
868
869#[derive(Debug, Serialize)]
871pub struct ResetResponse {
872 #[serde(rename = "type")]
873 pub message_type: &'static str,
874 pub session: String,
875 pub id: String,
876 pub status: &'static str,
877}
878
879impl ResetResponse {
880 pub fn ok(id: String) -> Self {
881 Self {
882 message_type: "reset_response",
883 session: String::new(),
884 id,
885 status: "ok",
886 }
887 }
888
889 pub fn with_session(mut self, session: impl Into<String>) -> Self {
891 self.session = session.into();
892 self
893 }
894}
895
896#[cfg(test)]
897mod tests {
898 use super::*;
899 use serde_json::json;
900
901 #[test]
906 fn serialize_click_event() {
907 let evt = OutgoingEvent::click("btn1".to_string());
908 let json = serde_json::to_value(&evt).unwrap();
909 assert_eq!(json["type"], "event");
910 assert_eq!(json["family"], "click");
911 assert_eq!(json["id"], "btn1");
912 assert!(json.get("value").is_none());
913 assert!(json.get("tag").is_none());
914 assert!(json.get("modifiers").is_none());
915 }
916
917 #[test]
918 fn serialize_input_event() {
919 let evt = OutgoingEvent::input("inp1".to_string(), "hello".to_string());
920 let json = serde_json::to_value(&evt).unwrap();
921 assert_eq!(json["family"], "input");
922 assert_eq!(json["id"], "inp1");
923 assert_eq!(json["value"], "hello");
924 }
925
926 #[test]
927 fn serialize_submit_event() {
928 let evt = OutgoingEvent::submit("form1".to_string(), "data".to_string());
929 let json = serde_json::to_value(&evt).unwrap();
930 assert_eq!(json["family"], "submit");
931 assert_eq!(json["value"], "data");
932 }
933
934 #[test]
935 fn serialize_toggle_event_true() {
936 let evt = OutgoingEvent::toggle("chk1".to_string(), true);
937 let json = serde_json::to_value(&evt).unwrap();
938 assert_eq!(json["family"], "toggle");
939 assert_eq!(json["value"], true);
940 }
941
942 #[test]
943 fn serialize_toggle_event_false() {
944 let evt = OutgoingEvent::toggle("chk1".to_string(), false);
945 let json = serde_json::to_value(&evt).unwrap();
946 assert_eq!(json["value"], false);
947 }
948
949 #[test]
950 fn serialize_slide_event() {
951 let evt = OutgoingEvent::slide("slider1".to_string(), 0.75);
952 let json = serde_json::to_value(&evt).unwrap();
953 assert_eq!(json["family"], "slide");
954 assert_eq!(json["value"], 0.75);
955 }
956
957 #[test]
958 fn serialize_slide_release_event() {
959 let evt = OutgoingEvent::slide_release("slider1".to_string(), 0.5);
960 let json = serde_json::to_value(&evt).unwrap();
961 assert_eq!(json["family"], "slide_release");
962 assert_eq!(json["value"], 0.5);
963 }
964
965 #[test]
966 fn serialize_select_event() {
967 let evt = OutgoingEvent::select("picker1".to_string(), "option_b".to_string());
968 let json = serde_json::to_value(&evt).unwrap();
969 assert_eq!(json["family"], "select");
970 assert_eq!(json["value"], "option_b");
971 }
972
973 fn make_key_event_data(key_str: &str, shift: bool, alt: bool) -> crate::message::KeyEventData {
978 use iced::keyboard;
979 crate::message::KeyEventData {
980 key: if key_str.len() == 1 {
981 keyboard::Key::Character(key_str.into())
982 } else {
983 keyboard::Key::Named(keyboard::key::Named::Escape)
984 },
985 modified_key: if key_str.len() == 1 {
986 keyboard::Key::Character(key_str.to_uppercase().into())
987 } else {
988 keyboard::Key::Named(keyboard::key::Named::Escape)
989 },
990 physical_key: keyboard::key::Physical::Code(keyboard::key::Code::KeyA),
991 location: keyboard::Location::Standard,
992 modifiers: {
993 let mut m = keyboard::Modifiers::empty();
994 if shift {
995 m |= keyboard::Modifiers::SHIFT;
996 }
997 if alt {
998 m |= keyboard::Modifiers::ALT;
999 }
1000 m
1001 },
1002 text: if key_str.len() == 1 {
1003 Some(key_str.to_string())
1004 } else {
1005 None
1006 },
1007 repeat: false,
1008 captured: false,
1009 }
1010 }
1011
1012 #[test]
1013 fn serialize_key_press_with_modifiers() {
1014 let data = make_key_event_data("a", true, true);
1015 let evt = OutgoingEvent::key_press("keys".to_string(), &data);
1016 let json = serde_json::to_value(&evt).unwrap();
1017 assert_eq!(json["family"], "key_press");
1018 assert_eq!(json["tag"], "keys");
1019 assert_eq!(json["value"], "a");
1020 assert!(json["id"].as_str().unwrap().is_empty());
1021 assert_eq!(json["modifiers"]["shift"], true);
1022 assert_eq!(json["modifiers"]["ctrl"], false);
1023 assert_eq!(json["modifiers"]["alt"], true);
1024 assert_eq!(json["modifiers"]["logo"], false);
1025 assert_eq!(json["modifiers"]["command"], false);
1026 assert_eq!(json["data"]["modified_key"], "A");
1028 assert_eq!(json["data"]["physical_key"], "KeyA");
1029 assert_eq!(json["data"]["location"], "standard");
1030 assert_eq!(json["data"]["text"], "a");
1031 assert_eq!(json["data"]["repeat"], false);
1032 }
1033
1034 #[test]
1035 fn serialize_key_release() {
1036 let data = make_key_event_data("Escape", false, false);
1037 let evt = OutgoingEvent::key_release("keys".to_string(), &data);
1038 let json = serde_json::to_value(&evt).unwrap();
1039 assert_eq!(json["family"], "key_release");
1040 assert_eq!(json["value"], "Escape");
1041 assert!(json.get("text").is_none() || json["text"].is_null());
1043 }
1044
1045 #[test]
1046 fn serialize_modifiers_changed() {
1047 let mods = KeyModifiers {
1048 shift: true,
1049 ctrl: true,
1050 alt: false,
1051 logo: false,
1052 command: false,
1053 };
1054 let evt = OutgoingEvent::modifiers_changed("mods".to_string(), mods);
1055 let json = serde_json::to_value(&evt).unwrap();
1056 assert_eq!(json["family"], "modifiers_changed");
1057 assert!(json.get("value").is_none());
1058 assert_eq!(json["modifiers"]["shift"], true);
1059 assert_eq!(json["modifiers"]["ctrl"], true);
1060 }
1061
1062 #[test]
1067 fn serialize_cursor_moved() {
1068 let evt = OutgoingEvent::cursor_moved("mouse".to_string(), 100.0, 200.0);
1069 let json = serde_json::to_value(&evt).unwrap();
1070 assert_eq!(json["family"], "cursor_moved");
1071 assert_eq!(json["data"]["x"], 100.0);
1072 assert_eq!(json["data"]["y"], 200.0);
1073 }
1074
1075 #[test]
1076 fn serialize_cursor_entered() {
1077 let evt = OutgoingEvent::cursor_entered("mouse".to_string());
1078 let json = serde_json::to_value(&evt).unwrap();
1079 assert_eq!(json["family"], "cursor_entered");
1080 assert_eq!(json["tag"], "mouse");
1081 }
1082
1083 #[test]
1084 fn serialize_cursor_left() {
1085 let evt = OutgoingEvent::cursor_left("mouse".to_string());
1086 let json = serde_json::to_value(&evt).unwrap();
1087 assert_eq!(json["family"], "cursor_left");
1088 }
1089
1090 #[test]
1091 fn serialize_button_pressed() {
1092 let evt = OutgoingEvent::button_pressed("mouse".to_string(), "Left".to_string());
1093 let json = serde_json::to_value(&evt).unwrap();
1094 assert_eq!(json["family"], "button_pressed");
1095 assert_eq!(json["value"], "Left");
1096 }
1097
1098 #[test]
1099 fn serialize_button_released() {
1100 let evt = OutgoingEvent::button_released("mouse".to_string(), "Right".to_string());
1101 let json = serde_json::to_value(&evt).unwrap();
1102 assert_eq!(json["family"], "button_released");
1103 assert_eq!(json["value"], "Right");
1104 }
1105
1106 #[test]
1107 fn serialize_wheel_scrolled() {
1108 let evt = OutgoingEvent::wheel_scrolled("mouse".to_string(), 0.0, -3.0, "line");
1109 let json = serde_json::to_value(&evt).unwrap();
1110 assert_eq!(json["family"], "wheel_scrolled");
1111 assert_eq!(json["data"]["delta_x"], 0.0);
1112 assert_eq!(json["data"]["delta_y"], -3.0);
1113 assert_eq!(json["data"]["unit"], "line");
1114 }
1115
1116 #[test]
1121 fn serialize_finger_pressed() {
1122 let evt = OutgoingEvent::finger_pressed("touch".to_string(), 1, 50.0, 75.0);
1123 let json = serde_json::to_value(&evt).unwrap();
1124 assert_eq!(json["family"], "finger_pressed");
1125 assert_eq!(json["data"]["finger_id"], 1);
1126 assert_eq!(json["data"]["x"], 50.0);
1127 assert_eq!(json["data"]["y"], 75.0);
1128 }
1129
1130 #[test]
1131 fn serialize_finger_moved() {
1132 let evt = OutgoingEvent::finger_moved("touch".to_string(), 2, 60.0, 80.0);
1133 let json = serde_json::to_value(&evt).unwrap();
1134 assert_eq!(json["family"], "finger_moved");
1135 assert_eq!(json["data"]["finger_id"], 2);
1136 }
1137
1138 #[test]
1139 fn serialize_finger_lifted() {
1140 let evt = OutgoingEvent::finger_lifted("touch".to_string(), 1, 55.0, 78.0);
1141 let json = serde_json::to_value(&evt).unwrap();
1142 assert_eq!(json["family"], "finger_lifted");
1143 }
1144
1145 #[test]
1146 fn serialize_finger_lost() {
1147 let evt = OutgoingEvent::finger_lost("touch".to_string(), 3, 0.0, 0.0);
1148 let json = serde_json::to_value(&evt).unwrap();
1149 assert_eq!(json["family"], "finger_lost");
1150 assert_eq!(json["data"]["finger_id"], 3);
1151 }
1152
1153 #[test]
1158 fn serialize_window_opened_with_position() {
1159 let evt = OutgoingEvent::window_opened(
1160 "win_events".to_string(),
1161 "main".to_string(),
1162 Some((10.0, 20.0)),
1163 800.0,
1164 600.0,
1165 2.0,
1166 );
1167 let json = serde_json::to_value(&evt).unwrap();
1168 assert_eq!(json["family"], "window_opened");
1169 assert_eq!(json["data"]["window_id"], "main");
1170 assert_eq!(json["data"]["width"], 800.0);
1171 assert_eq!(json["data"]["height"], 600.0);
1172 assert_eq!(json["data"]["position"]["x"], 10.0);
1173 assert_eq!(json["data"]["position"]["y"], 20.0);
1174 assert_eq!(json["data"]["scale_factor"], 2.0);
1175 }
1176
1177 #[test]
1178 fn serialize_window_opened_without_position() {
1179 let evt = OutgoingEvent::window_opened(
1180 "win_events".to_string(),
1181 "main".to_string(),
1182 None,
1183 1024.0,
1184 768.0,
1185 1.0,
1186 );
1187 let json = serde_json::to_value(&evt).unwrap();
1188 assert_eq!(json["family"], "window_opened");
1189 assert!(json["data"]["position"].is_null());
1190 assert_eq!(json["data"]["scale_factor"], 1.0);
1191 }
1192
1193 #[test]
1194 fn serialize_window_closed() {
1195 let evt = OutgoingEvent::window_closed("win_events".to_string(), "popup".to_string());
1196 let json = serde_json::to_value(&evt).unwrap();
1197 assert_eq!(json["family"], "window_closed");
1198 assert_eq!(json["data"]["window_id"], "popup");
1199 }
1200
1201 #[test]
1202 fn serialize_window_close_requested() {
1203 let evt =
1204 OutgoingEvent::window_close_requested("win_events".to_string(), "main".to_string());
1205 let json = serde_json::to_value(&evt).unwrap();
1206 assert_eq!(json["family"], "window_close_requested");
1207 }
1208
1209 #[test]
1210 fn serialize_window_moved() {
1211 let evt =
1212 OutgoingEvent::window_moved("win_events".to_string(), "main".to_string(), 50.0, 100.0);
1213 let json = serde_json::to_value(&evt).unwrap();
1214 assert_eq!(json["family"], "window_moved");
1215 assert_eq!(json["data"]["x"], 50.0);
1216 assert_eq!(json["data"]["y"], 100.0);
1217 }
1218
1219 #[test]
1220 fn serialize_window_resized() {
1221 let evt = OutgoingEvent::window_resized(
1222 "win_events".to_string(),
1223 "main".to_string(),
1224 1920.0,
1225 1080.0,
1226 );
1227 let json = serde_json::to_value(&evt).unwrap();
1228 assert_eq!(json["family"], "window_resized");
1229 assert_eq!(json["data"]["width"], 1920.0);
1230 }
1231
1232 #[test]
1233 fn serialize_window_focused() {
1234 let evt = OutgoingEvent::window_focused("win_events".to_string(), "main".to_string());
1235 let json = serde_json::to_value(&evt).unwrap();
1236 assert_eq!(json["family"], "window_focused");
1237 }
1238
1239 #[test]
1240 fn serialize_window_unfocused() {
1241 let evt = OutgoingEvent::window_unfocused("win_events".to_string(), "main".to_string());
1242 let json = serde_json::to_value(&evt).unwrap();
1243 assert_eq!(json["family"], "window_unfocused");
1244 }
1245
1246 #[test]
1247 fn serialize_window_rescaled() {
1248 let evt = OutgoingEvent::window_rescaled("win_events".to_string(), "main".to_string(), 2.0);
1249 let json = serde_json::to_value(&evt).unwrap();
1250 assert_eq!(json["family"], "window_rescaled");
1251 assert_eq!(json["data"]["scale_factor"], 2.0);
1252 }
1253
1254 #[test]
1255 fn serialize_file_hovered() {
1256 let evt = OutgoingEvent::file_hovered(
1257 "win_events".to_string(),
1258 "main".to_string(),
1259 "/tmp/a.txt".to_string(),
1260 );
1261 let json = serde_json::to_value(&evt).unwrap();
1262 assert_eq!(json["family"], "file_hovered");
1263 assert_eq!(json["data"]["path"], "/tmp/a.txt");
1264 }
1265
1266 #[test]
1267 fn serialize_file_dropped() {
1268 let evt = OutgoingEvent::file_dropped(
1269 "win_events".to_string(),
1270 "main".to_string(),
1271 "/tmp/b.txt".to_string(),
1272 );
1273 let json = serde_json::to_value(&evt).unwrap();
1274 assert_eq!(json["family"], "file_dropped");
1275 assert_eq!(json["data"]["path"], "/tmp/b.txt");
1276 }
1277
1278 #[test]
1279 fn serialize_files_hovered_left() {
1280 let evt = OutgoingEvent::files_hovered_left("win_events".to_string(), "main".to_string());
1281 let json = serde_json::to_value(&evt).unwrap();
1282 assert_eq!(json["family"], "files_hovered_left");
1283 }
1284
1285 #[test]
1290 fn serialize_sensor_resize() {
1291 let evt = OutgoingEvent::sensor_resize("s1".to_string(), 100.0, 200.0);
1292 let json = serde_json::to_value(&evt).unwrap();
1293 assert_eq!(json["family"], "sensor_resize");
1294 assert_eq!(json["id"], "s1");
1295 assert_eq!(json["data"]["width"], 100.0);
1296 assert_eq!(json["data"]["height"], 200.0);
1297 }
1298
1299 #[test]
1304 fn serialize_canvas_press() {
1305 let evt = OutgoingEvent::canvas_press("c1".to_string(), 10.0, 20.0, "Left".to_string());
1306 let json = serde_json::to_value(&evt).unwrap();
1307 assert_eq!(json["family"], "canvas_press");
1308 assert_eq!(json["data"]["x"], 10.0);
1309 assert_eq!(json["data"]["button"], "Left");
1310 }
1311
1312 #[test]
1313 fn serialize_canvas_release() {
1314 let evt = OutgoingEvent::canvas_release("c1".to_string(), 10.0, 20.0, "Left".to_string());
1315 let json = serde_json::to_value(&evt).unwrap();
1316 assert_eq!(json["family"], "canvas_release");
1317 }
1318
1319 #[test]
1320 fn serialize_canvas_move() {
1321 let evt = OutgoingEvent::canvas_move("c1".to_string(), 30.0, 40.0);
1322 let json = serde_json::to_value(&evt).unwrap();
1323 assert_eq!(json["family"], "canvas_move");
1324 assert_eq!(json["data"]["x"], 30.0);
1325 assert_eq!(json["data"]["y"], 40.0);
1326 }
1327
1328 #[test]
1329 fn serialize_canvas_scroll() {
1330 let evt = OutgoingEvent::canvas_scroll("c1".to_string(), 5.0, 5.0, 0.0, -1.0);
1331 let json = serde_json::to_value(&evt).unwrap();
1332 assert_eq!(json["family"], "canvas_scroll");
1333 assert_eq!(json["data"]["delta_y"], -1.0);
1334 }
1335
1336 #[test]
1341 fn serialize_mouse_right_press() {
1342 let evt = OutgoingEvent::mouse_right_press("zone".to_string());
1343 let json = serde_json::to_value(&evt).unwrap();
1344 assert_eq!(json["family"], "mouse_right_press");
1345 assert_eq!(json["id"], "zone");
1346 }
1347
1348 #[test]
1349 fn serialize_mouse_right_release() {
1350 let evt = OutgoingEvent::mouse_right_release("zone".to_string());
1351 let json = serde_json::to_value(&evt).unwrap();
1352 assert_eq!(json["family"], "mouse_right_release");
1353 }
1354
1355 #[test]
1356 fn serialize_mouse_middle_press() {
1357 let evt = OutgoingEvent::mouse_middle_press("zone".to_string());
1358 let json = serde_json::to_value(&evt).unwrap();
1359 assert_eq!(json["family"], "mouse_middle_press");
1360 assert_eq!(json["id"], "zone");
1361 }
1362
1363 #[test]
1364 fn serialize_mouse_middle_release() {
1365 let evt = OutgoingEvent::mouse_middle_release("zone".to_string());
1366 let json = serde_json::to_value(&evt).unwrap();
1367 assert_eq!(json["family"], "mouse_middle_release");
1368 }
1369
1370 #[test]
1371 fn serialize_mouse_double_click() {
1372 let evt = OutgoingEvent::mouse_double_click("zone".to_string());
1373 let json = serde_json::to_value(&evt).unwrap();
1374 assert_eq!(json["family"], "mouse_double_click");
1375 }
1376
1377 #[test]
1378 fn serialize_mouse_enter() {
1379 let evt = OutgoingEvent::mouse_enter("zone".to_string());
1380 let json = serde_json::to_value(&evt).unwrap();
1381 assert_eq!(json["family"], "mouse_enter");
1382 }
1383
1384 #[test]
1385 fn serialize_mouse_exit() {
1386 let evt = OutgoingEvent::mouse_exit("zone".to_string());
1387 let json = serde_json::to_value(&evt).unwrap();
1388 assert_eq!(json["family"], "mouse_exit");
1389 }
1390
1391 #[test]
1392 fn serialize_mouse_area_move() {
1393 let evt = OutgoingEvent::mouse_area_move("zone".to_string(), 10.5, 20.3);
1394 let json = serde_json::to_value(&evt).unwrap();
1395 assert_eq!(json["family"], "mouse_move");
1396 assert_eq!(json["id"], "zone");
1397 let data = &json["data"];
1398 assert!((data["x"].as_f64().unwrap() - 10.5).abs() < 0.01);
1399 assert!((data["y"].as_f64().unwrap() - 20.3).abs() < 0.01);
1400 }
1401
1402 #[test]
1403 fn serialize_mouse_area_scroll() {
1404 let evt = OutgoingEvent::mouse_area_scroll("zone".to_string(), 0.0, -3.0);
1405 let json = serde_json::to_value(&evt).unwrap();
1406 assert_eq!(json["family"], "mouse_scroll");
1407 assert_eq!(json["id"], "zone");
1408 assert_eq!(json["data"]["delta_x"], 0.0);
1409 assert_eq!(json["data"]["delta_y"], -3.0);
1410 }
1411
1412 #[test]
1417 fn serialize_pane_resized() {
1418 let evt = OutgoingEvent::pane_resized("pg1".to_string(), "split_0".to_string(), 0.5);
1419 let json = serde_json::to_value(&evt).unwrap();
1420 assert_eq!(json["family"], "pane_resized");
1421 assert_eq!(json["data"]["split"], "split_0");
1422 assert_eq!(json["data"]["ratio"], json!(0.5));
1423 }
1424
1425 #[test]
1426 fn serialize_pane_dragged_dropped() {
1427 let evt = OutgoingEvent::pane_dragged(
1428 "pg1".to_string(),
1429 "dropped",
1430 "pane_a".to_string(),
1431 Some("pane_b".to_string()),
1432 Some("center"),
1433 None,
1434 );
1435 let json = serde_json::to_value(&evt).unwrap();
1436 assert_eq!(json["family"], "pane_dragged");
1437 assert_eq!(json["data"]["action"], "dropped");
1438 assert_eq!(json["data"]["pane"], "pane_a");
1439 assert_eq!(json["data"]["target"], "pane_b");
1440 assert_eq!(json["data"]["region"], "center");
1441 }
1442
1443 #[test]
1444 fn serialize_pane_dragged_picked() {
1445 let evt = OutgoingEvent::pane_dragged(
1446 "pg1".to_string(),
1447 "picked",
1448 "pane_a".to_string(),
1449 None,
1450 None,
1451 None,
1452 );
1453 let json = serde_json::to_value(&evt).unwrap();
1454 assert_eq!(json["data"]["action"], "picked");
1455 assert_eq!(json["data"]["pane"], "pane_a");
1456 assert!(json["data"].get("target").is_none());
1457 }
1458
1459 #[test]
1460 fn serialize_pane_dragged_canceled() {
1461 let evt = OutgoingEvent::pane_dragged(
1462 "pg1".to_string(),
1463 "canceled",
1464 "pane_a".to_string(),
1465 None,
1466 None,
1467 None,
1468 );
1469 let json = serde_json::to_value(&evt).unwrap();
1470 assert_eq!(json["data"]["action"], "canceled");
1471 }
1472
1473 #[test]
1474 fn serialize_pane_focus_cycle() {
1475 let evt = OutgoingEvent::pane_focus_cycle("pg1".to_string(), "pane_a".to_string());
1476 let json = serde_json::to_value(&evt).unwrap();
1477 assert_eq!(json["family"], "pane_focus_cycle");
1478 assert_eq!(json["data"]["pane"], "pane_a");
1479 }
1480
1481 #[test]
1482 fn serialize_pane_clicked() {
1483 let evt = OutgoingEvent::pane_clicked("pg1".to_string(), "pane_x".to_string());
1484 let json = serde_json::to_value(&evt).unwrap();
1485 assert_eq!(json["family"], "pane_clicked");
1486 assert_eq!(json["data"]["pane"], "pane_x");
1487 }
1488
1489 #[test]
1494 fn serialize_animation_frame() {
1495 let evt = OutgoingEvent::animation_frame("anim".to_string(), 16000);
1496 let json = serde_json::to_value(&evt).unwrap();
1497 assert_eq!(json["family"], "animation_frame");
1498 assert_eq!(json["data"]["timestamp"], 16000);
1499 }
1500
1501 #[test]
1502 fn serialize_theme_changed() {
1503 let evt = OutgoingEvent::theme_changed("theme".to_string(), "dark".to_string());
1504 let json = serde_json::to_value(&evt).unwrap();
1505 assert_eq!(json["family"], "theme_changed");
1506 assert_eq!(json["value"], "dark");
1507 }
1508
1509 #[test]
1514 fn outgoing_event_roundtrip_all_fields_present() {
1515 let data = make_key_event_data("a", true, false);
1516 let evt = OutgoingEvent::key_press("kb".to_string(), &data);
1517 let serialized = serde_json::to_string(&evt).unwrap();
1518 let parsed: Value = serde_json::from_str(&serialized).unwrap();
1519 assert_eq!(parsed["type"], "event");
1520 assert_eq!(parsed["family"], "key_press");
1521 assert_eq!(parsed["value"], "a");
1522 assert_eq!(parsed["tag"], "kb");
1523 assert_eq!(parsed["modifiers"]["shift"], true);
1524 assert!(parsed["data"].get("modified_key").is_some());
1526 assert!(parsed["data"].get("physical_key").is_some());
1527 assert!(parsed["data"].get("location").is_some());
1528 }
1529
1530 #[test]
1531 fn ime_opened_event() {
1532 let evt = OutgoingEvent::ime_opened("ime_tag".to_string());
1533 let json = serde_json::to_value(&evt).unwrap();
1534 assert_eq!(json["type"], "event");
1535 assert_eq!(json["family"], "ime");
1536 assert_eq!(json["tag"], "ime_tag");
1537 assert_eq!(json["data"]["kind"], "opened");
1538 }
1539
1540 #[test]
1541 fn ime_preedit_event_with_cursor() {
1542 let evt =
1543 OutgoingEvent::ime_preedit("ime_tag".to_string(), "hello".to_string(), Some(2..5));
1544 let json = serde_json::to_value(&evt).unwrap();
1545 assert_eq!(json["family"], "ime");
1546 assert_eq!(json["data"]["kind"], "preedit");
1547 assert_eq!(json["data"]["text"], "hello");
1548 assert_eq!(json["data"]["cursor"]["start"], 2);
1549 assert_eq!(json["data"]["cursor"]["end"], 5);
1550 }
1551
1552 #[test]
1553 fn ime_preedit_event_without_cursor() {
1554 let evt = OutgoingEvent::ime_preedit("ime_tag".to_string(), "hi".to_string(), None);
1555 let json = serde_json::to_value(&evt).unwrap();
1556 assert_eq!(json["data"]["kind"], "preedit");
1557 assert_eq!(json["data"]["text"], "hi");
1558 assert!(json["data"]["cursor"].is_null());
1559 }
1560
1561 #[test]
1562 fn ime_commit_event() {
1563 let evt = OutgoingEvent::ime_commit("ime_tag".to_string(), "final".to_string());
1564 let json = serde_json::to_value(&evt).unwrap();
1565 assert_eq!(json["family"], "ime");
1566 assert_eq!(json["data"]["kind"], "commit");
1567 assert_eq!(json["data"]["text"], "final");
1568 }
1569
1570 #[test]
1571 fn ime_closed_event() {
1572 let evt = OutgoingEvent::ime_closed("ime_tag".to_string());
1573 let json = serde_json::to_value(&evt).unwrap();
1574 assert_eq!(json["family"], "ime");
1575 assert_eq!(json["data"]["kind"], "closed");
1576 }
1577
1578 #[test]
1579 fn outgoing_event_bare_omits_optional_fields() {
1580 let evt = OutgoingEvent::click("b".to_string());
1581 let serialized = serde_json::to_string(&evt).unwrap();
1582 assert!(!serialized.contains("\"value\""));
1584 assert!(!serialized.contains("\"tag\""));
1585 assert!(!serialized.contains("\"modifiers\""));
1586 assert!(!serialized.contains("\"captured\""));
1587 }
1588
1589 #[test]
1590 fn outgoing_event_with_captured_true() {
1591 let evt = OutgoingEvent::cursor_moved("m".to_string(), 1.0, 2.0).with_captured(true);
1592 let json = serde_json::to_value(&evt).unwrap();
1593 assert_eq!(json["captured"], true);
1594 }
1595
1596 #[test]
1597 fn outgoing_event_with_captured_false() {
1598 let evt =
1599 OutgoingEvent::key_press("kb".to_string(), &make_key_event_data("a", false, false))
1600 .with_captured(false);
1601 let json = serde_json::to_value(&evt).unwrap();
1602 assert_eq!(json["captured"], false);
1603 }
1604
1605 #[test]
1606 fn outgoing_event_without_captured_omits_field() {
1607 let evt = OutgoingEvent::click("btn".to_string());
1608 let json = serde_json::to_value(&evt).unwrap();
1609 assert!(json.get("captured").is_none());
1610 }
1611
1612 #[test]
1615 fn outgoing_slide_with_nan_produces_zero() {
1616 let event = OutgoingEvent::slide("s1".to_string(), f64::NAN);
1617 let val = event.value.unwrap();
1619 assert_eq!(val.as_f64(), Some(0.0));
1620 }
1621
1622 #[test]
1623 fn outgoing_cursor_moved_with_infinity_produces_zero() {
1624 let event =
1625 OutgoingEvent::cursor_moved("tag".to_string(), f32::INFINITY, f32::NEG_INFINITY);
1626 let data = event.data.unwrap();
1627 assert_eq!(data["x"].as_f64(), Some(0.0));
1628 assert_eq!(data["y"].as_f64(), Some(0.0));
1629 }
1630
1631 #[test]
1634 fn sanitize_f32_passes_finite() {
1635 assert_eq!(sanitize_f32(1.5), 1.5);
1636 assert_eq!(sanitize_f32(-0.0), -0.0);
1637 assert_eq!(sanitize_f32(0.0), 0.0);
1638 }
1639
1640 #[test]
1641 fn sanitize_f32_replaces_nan() {
1642 assert_eq!(sanitize_f32(f32::NAN), 0.0);
1643 }
1644
1645 #[test]
1646 fn sanitize_f32_replaces_infinity() {
1647 assert_eq!(sanitize_f32(f32::INFINITY), 0.0);
1648 assert_eq!(sanitize_f32(f32::NEG_INFINITY), 0.0);
1649 }
1650
1651 #[test]
1652 fn sanitize_f64_passes_finite() {
1653 assert_eq!(sanitize_f64(42.0), 42.0);
1654 }
1655
1656 #[test]
1657 fn sanitize_f64_replaces_nan() {
1658 assert_eq!(sanitize_f64(f64::NAN), 0.0);
1659 }
1660
1661 #[test]
1662 fn sanitize_f64_replaces_infinity() {
1663 assert_eq!(sanitize_f64(f64::INFINITY), 0.0);
1664 assert_eq!(sanitize_f64(f64::NEG_INFINITY), 0.0);
1665 }
1666
1667 #[test]
1672 fn effect_response_ok() {
1673 let resp = EffectResponse::ok("e1".to_string(), json!("clipboard content"));
1674 let json = serde_json::to_value(&resp).unwrap();
1675 assert_eq!(json["type"], "effect_response");
1676 assert_eq!(json["id"], "e1");
1677 assert_eq!(json["status"], "ok");
1678 assert_eq!(json["result"], "clipboard content");
1679 assert!(json.get("error").is_none());
1680 }
1681
1682 #[test]
1683 fn effect_response_error() {
1684 let resp = EffectResponse::error("e2".to_string(), "not found".to_string());
1685 let json = serde_json::to_value(&resp).unwrap();
1686 assert_eq!(json["type"], "effect_response");
1687 assert_eq!(json["id"], "e2");
1688 assert_eq!(json["status"], "error");
1689 assert_eq!(json["error"], "not found");
1690 assert!(json.get("result").is_none());
1691 }
1692
1693 #[test]
1694 fn effect_response_unsupported() {
1695 let resp = EffectResponse::unsupported("e3".to_string());
1696 let json = serde_json::to_value(&resp).unwrap();
1697 assert_eq!(json["status"], "error");
1698 assert_eq!(json["error"], "unsupported");
1699 }
1700
1701 #[test]
1702 fn effect_response_ok_with_object_result() {
1703 let resp = EffectResponse::ok("e4".to_string(), json!({"files": ["/a.txt", "/b.txt"]}));
1704 let json = serde_json::to_value(&resp).unwrap();
1705 assert_eq!(json["result"]["files"][0], "/a.txt");
1706 assert_eq!(json["result"]["files"][1], "/b.txt");
1707 }
1708}