1use serde::{Deserialize, Serialize};
28use serde_json::Value;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum NotificationType {
35 Starting,
37 Succeeded,
39 Failed,
41 Unknown,
43}
44
45impl NotificationType {
46 pub fn is_starting(&self) -> bool {
48 matches!(self, Self::Starting)
49 }
50
51 pub fn is_succeeded(&self) -> bool {
53 matches!(self, Self::Succeeded)
54 }
55
56 pub fn is_failed(&self) -> bool {
58 matches!(self, Self::Failed)
59 }
60
61 pub fn is_complete(&self) -> bool {
63 matches!(self, Self::Succeeded | Self::Failed)
64 }
65}
66
67impl From<&str> for NotificationType {
68 fn from(s: &str) -> Self {
69 if s.ends_with(".Starting") {
70 NotificationType::Starting
71 } else if s.ends_with(".Succeeded") {
72 NotificationType::Succeeded
73 } else if s.ends_with(".Failed") {
74 NotificationType::Failed
75 } else {
76 NotificationType::Unknown
77 }
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct ResourceLoadingDetail {
86 pub res_id: i64,
88 pub hash: String,
90 pub path: String,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ControllerActionDetail {
97 pub ctrl_id: i64,
99 pub uuid: String,
101 pub action: String,
103 #[serde(default)]
105 pub param: Value,
106 #[serde(default)]
108 pub info: Value,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct TaskerTaskDetail {
114 pub task_id: i64,
116 pub entry: String,
118 #[serde(default)]
120 pub uuid: String,
121 #[serde(default)]
123 pub hash: String,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct NextListItem {
129 pub name: String,
131 #[serde(default)]
133 pub jump_back: bool,
134 #[serde(default)]
136 pub anchor: bool,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct NodeNextListDetail {
142 pub task_id: i64,
144 pub name: String,
146 #[serde(default)]
148 pub list: Vec<NextListItem>,
149 #[serde(default)]
151 pub focus: Value,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct NodeWaitFreezesDetail {
157 pub task_id: i64,
159 pub wf_id: i64,
161 pub name: String,
163 pub phase: String,
165 pub roi: crate::common::Rect,
167 #[serde(default)]
169 pub param: Value,
170 #[serde(default)]
172 pub reco_ids: Vec<i64>,
173 #[serde(default)]
175 pub elapsed: Option<u64>,
176 #[serde(default)]
178 pub focus: Value,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct NodeRecognitionDetail {
184 pub task_id: i64,
186 pub reco_id: i64,
188 pub name: String,
190 #[serde(default)]
192 pub focus: Value,
193 pub anchor: Option<String>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct NodeActionDetail {
200 pub task_id: i64,
202 pub action_id: i64,
204 pub name: String,
206 #[serde(default)]
208 pub focus: Value,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct NodePipelineNodeDetail {
214 pub task_id: i64,
216 pub node_id: i64,
218 pub name: String,
220 #[serde(default)]
222 pub focus: Value,
223}
224
225pub mod msg {
229 pub const RESOURCE_LOADING_STARTING: &str = "Resource.Loading.Starting";
231 pub const RESOURCE_LOADING_SUCCEEDED: &str = "Resource.Loading.Succeeded";
232 pub const RESOURCE_LOADING_FAILED: &str = "Resource.Loading.Failed";
233
234 pub const CONTROLLER_ACTION_STARTING: &str = "Controller.Action.Starting";
236 pub const CONTROLLER_ACTION_SUCCEEDED: &str = "Controller.Action.Succeeded";
237 pub const CONTROLLER_ACTION_FAILED: &str = "Controller.Action.Failed";
238
239 pub const TASKER_TASK_STARTING: &str = "Tasker.Task.Starting";
241 pub const TASKER_TASK_SUCCEEDED: &str = "Tasker.Task.Succeeded";
242 pub const TASKER_TASK_FAILED: &str = "Tasker.Task.Failed";
243
244 pub const NODE_PIPELINE_NODE_STARTING: &str = "Node.PipelineNode.Starting";
246 pub const NODE_PIPELINE_NODE_SUCCEEDED: &str = "Node.PipelineNode.Succeeded";
247 pub const NODE_PIPELINE_NODE_FAILED: &str = "Node.PipelineNode.Failed";
248
249 pub const NODE_RECOGNITION_STARTING: &str = "Node.Recognition.Starting";
251 pub const NODE_RECOGNITION_SUCCEEDED: &str = "Node.Recognition.Succeeded";
252 pub const NODE_RECOGNITION_FAILED: &str = "Node.Recognition.Failed";
253
254 pub const NODE_ACTION_STARTING: &str = "Node.Action.Starting";
256 pub const NODE_ACTION_SUCCEEDED: &str = "Node.Action.Succeeded";
257 pub const NODE_ACTION_FAILED: &str = "Node.Action.Failed";
258
259 pub const NODE_WAIT_FREEZES_STARTING: &str = "Node.WaitFreezes.Starting";
261 pub const NODE_WAIT_FREEZES_SUCCEEDED: &str = "Node.WaitFreezes.Succeeded";
262 pub const NODE_WAIT_FREEZES_FAILED: &str = "Node.WaitFreezes.Failed";
263
264 pub const NODE_NEXT_LIST_STARTING: &str = "Node.NextList.Starting";
266 pub const NODE_NEXT_LIST_SUCCEEDED: &str = "Node.NextList.Succeeded";
267 pub const NODE_NEXT_LIST_FAILED: &str = "Node.NextList.Failed";
268
269 pub const NODE_RECOGNITION_NODE_STARTING: &str = "Node.RecognitionNode.Starting";
271 pub const NODE_RECOGNITION_NODE_SUCCEEDED: &str = "Node.RecognitionNode.Succeeded";
272 pub const NODE_RECOGNITION_NODE_FAILED: &str = "Node.RecognitionNode.Failed";
273
274 pub const NODE_ACTION_NODE_STARTING: &str = "Node.ActionNode.Starting";
276 pub const NODE_ACTION_NODE_SUCCEEDED: &str = "Node.ActionNode.Succeeded";
277 pub const NODE_ACTION_NODE_FAILED: &str = "Node.ActionNode.Failed";
278}
279
280pub fn parse_type(msg: &str) -> NotificationType {
292 NotificationType::from(msg)
293}
294
295pub fn parse_resource_loading(details: &str) -> Option<ResourceLoadingDetail> {
297 serde_json::from_str(details).ok()
298}
299
300pub fn parse_controller_action(details: &str) -> Option<ControllerActionDetail> {
302 serde_json::from_str(details).ok()
303}
304
305pub fn parse_tasker_task(details: &str) -> Option<TaskerTaskDetail> {
307 serde_json::from_str(details).ok()
308}
309
310pub fn parse_node_next_list(details: &str) -> Option<NodeNextListDetail> {
312 serde_json::from_str(details).ok()
313}
314
315pub fn parse_node_wait_freezes(details: &str) -> Option<NodeWaitFreezesDetail> {
317 serde_json::from_str(details).ok()
318}
319
320pub fn parse_node_recognition(details: &str) -> Option<NodeRecognitionDetail> {
322 serde_json::from_str(details).ok()
323}
324
325pub fn parse_node_action(details: &str) -> Option<NodeActionDetail> {
327 serde_json::from_str(details).ok()
328}
329
330pub fn parse_node_pipeline_node(details: &str) -> Option<NodePipelineNodeDetail> {
332 serde_json::from_str(details).ok()
333}
334
335#[derive(Debug, Clone)]
339pub enum ContextEvent {
340 NodeNextList(NotificationType, NodeNextListDetail),
341 NodeRecognition(NotificationType, NodeRecognitionDetail),
342 NodeAction(NotificationType, NodeActionDetail),
343 NodeWaitFreezes(NotificationType, NodeWaitFreezesDetail),
344 NodePipelineNode(NotificationType, NodePipelineNodeDetail),
345 NodeRecognitionNode(NotificationType, NodePipelineNodeDetail),
346 NodeActionNode(NotificationType, NodePipelineNodeDetail),
347 Unknown(String, Value),
348}
349
350impl ContextEvent {
351 pub fn from_notification(msg: &str, details: &str) -> Option<Self> {
353 let noti_type = NotificationType::from(msg);
354
355 let parse_json = || -> Option<Value> { serde_json::from_str(details).ok() };
356
357 if msg.starts_with("Node.NextList") {
358 let detail = parse_node_next_list(details)?;
359 return Some(ContextEvent::NodeNextList(noti_type, detail));
360 }
361
362 if msg.starts_with("Node.Recognition.") {
363 let detail = parse_node_recognition(details)?;
364 return Some(ContextEvent::NodeRecognition(noti_type, detail));
365 }
366
367 if msg.starts_with("Node.Action.") {
368 let detail = parse_node_action(details)?;
369 return Some(ContextEvent::NodeAction(noti_type, detail));
370 }
371
372 if msg.starts_with("Node.WaitFreezes") {
373 let detail = parse_node_wait_freezes(details)?;
374 return Some(ContextEvent::NodeWaitFreezes(noti_type, detail));
375 }
376
377 if msg.starts_with("Node.PipelineNode") {
378 let detail = parse_node_pipeline_node(details)?;
379 return Some(ContextEvent::NodePipelineNode(noti_type, detail));
380 }
381
382 if msg.starts_with("Node.RecognitionNode") {
383 let detail = parse_node_pipeline_node(details)?;
384 return Some(ContextEvent::NodeRecognitionNode(noti_type, detail));
385 }
386
387 if msg.starts_with("Node.ActionNode") {
388 let detail = parse_node_pipeline_node(details)?;
389 return Some(ContextEvent::NodeActionNode(noti_type, detail));
390 }
391
392 Some(ContextEvent::Unknown(
393 msg.to_string(),
394 parse_json().unwrap_or(Value::Null),
395 ))
396 }
397}
398
399pub trait ResourceEventHandler: Send + Sync {
403 fn on_resource_loading(&self, _noti_type: NotificationType, _detail: ResourceLoadingDetail) {}
405
406 fn on_unknown(&self, _msg: &str, _details: &Value) {}
408}
409
410pub trait ControllerEventHandler: Send + Sync {
412 fn on_controller_action(&self, _noti_type: NotificationType, _detail: ControllerActionDetail) {}
414
415 fn on_unknown(&self, _msg: &str, _details: &Value) {}
417}
418
419pub trait TaskerEventHandler: Send + Sync {
421 fn on_tasker_task(&self, _noti_type: NotificationType, _detail: TaskerTaskDetail) {}
423
424 fn on_unknown(&self, _msg: &str, _details: &Value) {}
426}
427
428pub trait ContextEventHandler: Send + Sync {
430 fn on_node_next_list(&self, _noti_type: NotificationType, _detail: NodeNextListDetail) {}
432
433 fn on_node_recognition(&self, _noti_type: NotificationType, _detail: NodeRecognitionDetail) {}
435
436 fn on_node_action(&self, _noti_type: NotificationType, _detail: NodeActionDetail) {}
438
439 fn on_node_wait_freezes(&self, _noti_type: NotificationType, _detail: NodeWaitFreezesDetail) {}
441
442 fn on_node_pipeline_node(&self, _noti_type: NotificationType, _detail: NodePipelineNodeDetail) {
444 }
445
446 fn on_node_recognition_node(
448 &self,
449 _noti_type: NotificationType,
450 _detail: NodePipelineNodeDetail,
451 ) {
452 }
453
454 fn on_node_action_node(&self, _noti_type: NotificationType, _detail: NodePipelineNodeDetail) {}
456
457 fn on_unknown(&self, _msg: &str, _details: &Value) {}
459}
460
461#[derive(Debug, Clone)]
473pub enum MaaEvent {
474 ResourceLoadingStarting(ResourceLoadingDetail),
477 ResourceLoadingSucceeded(ResourceLoadingDetail),
479 ResourceLoadingFailed(ResourceLoadingDetail),
481
482 ControllerActionStarting(ControllerActionDetail),
485 ControllerActionSucceeded(ControllerActionDetail),
487 ControllerActionFailed(ControllerActionDetail),
489
490 TaskerTaskStarting(TaskerTaskDetail),
493 TaskerTaskSucceeded(TaskerTaskDetail),
495 TaskerTaskFailed(TaskerTaskDetail),
497
498 NodePipelineNodeStarting(NodePipelineNodeDetail),
501 NodePipelineNodeSucceeded(NodePipelineNodeDetail),
503 NodePipelineNodeFailed(NodePipelineNodeDetail),
505
506 NodeRecognitionStarting(NodeRecognitionDetail),
509 NodeRecognitionSucceeded(NodeRecognitionDetail),
511 NodeRecognitionFailed(NodeRecognitionDetail),
513
514 NodeActionStarting(NodeActionDetail),
517 NodeActionSucceeded(NodeActionDetail),
519 NodeActionFailed(NodeActionDetail),
521
522 NodeWaitFreezesStarting(NodeWaitFreezesDetail),
525 NodeWaitFreezesSucceeded(NodeWaitFreezesDetail),
527 NodeWaitFreezesFailed(NodeWaitFreezesDetail),
529
530 NodeNextListStarting(NodeNextListDetail),
533 NodeNextListSucceeded(NodeNextListDetail),
535 NodeNextListFailed(NodeNextListDetail),
537
538 NodeRecognitionNodeStarting(NodePipelineNodeDetail),
541 NodeRecognitionNodeSucceeded(NodePipelineNodeDetail),
543 NodeRecognitionNodeFailed(NodePipelineNodeDetail),
545
546 NodeActionNodeStarting(NodePipelineNodeDetail),
548 NodeActionNodeSucceeded(NodePipelineNodeDetail),
550 NodeActionNodeFailed(NodePipelineNodeDetail),
552
553 Unknown {
559 msg: String,
561 raw_json: String,
563 err: Option<String>,
564 },
565}
566
567impl MaaEvent {
568 pub fn from_json(msg: &str, details: &str) -> Self {
577 match msg {
578 msg::RESOURCE_LOADING_STARTING => match serde_json::from_str(details) {
580 Ok(d) => MaaEvent::ResourceLoadingStarting(d),
581 Err(e) => MaaEvent::Unknown {
582 msg: msg.to_string(),
583 raw_json: details.to_string(),
584 err: Some(e.to_string()),
585 },
586 },
587 msg::RESOURCE_LOADING_SUCCEEDED => match serde_json::from_str(details) {
588 Ok(d) => MaaEvent::ResourceLoadingSucceeded(d),
589 Err(e) => MaaEvent::Unknown {
590 msg: msg.to_string(),
591 raw_json: details.to_string(),
592 err: Some(e.to_string()),
593 },
594 },
595 msg::RESOURCE_LOADING_FAILED => match serde_json::from_str(details) {
596 Ok(d) => MaaEvent::ResourceLoadingFailed(d),
597 Err(e) => MaaEvent::Unknown {
598 msg: msg.to_string(),
599 raw_json: details.to_string(),
600 err: Some(e.to_string()),
601 },
602 },
603
604 msg::CONTROLLER_ACTION_STARTING => match serde_json::from_str(details) {
606 Ok(d) => MaaEvent::ControllerActionStarting(d),
607 Err(e) => MaaEvent::Unknown {
608 msg: msg.to_string(),
609 raw_json: details.to_string(),
610 err: Some(e.to_string()),
611 },
612 },
613 msg::CONTROLLER_ACTION_SUCCEEDED => match serde_json::from_str(details) {
614 Ok(d) => MaaEvent::ControllerActionSucceeded(d),
615 Err(e) => MaaEvent::Unknown {
616 msg: msg.to_string(),
617 raw_json: details.to_string(),
618 err: Some(e.to_string()),
619 },
620 },
621 msg::CONTROLLER_ACTION_FAILED => match serde_json::from_str(details) {
622 Ok(d) => MaaEvent::ControllerActionFailed(d),
623 Err(e) => MaaEvent::Unknown {
624 msg: msg.to_string(),
625 raw_json: details.to_string(),
626 err: Some(e.to_string()),
627 },
628 },
629
630 msg::TASKER_TASK_STARTING => match serde_json::from_str(details) {
632 Ok(d) => MaaEvent::TaskerTaskStarting(d),
633 Err(e) => MaaEvent::Unknown {
634 msg: msg.to_string(),
635 raw_json: details.to_string(),
636 err: Some(e.to_string()),
637 },
638 },
639 msg::TASKER_TASK_SUCCEEDED => match serde_json::from_str(details) {
640 Ok(d) => MaaEvent::TaskerTaskSucceeded(d),
641 Err(e) => MaaEvent::Unknown {
642 msg: msg.to_string(),
643 raw_json: details.to_string(),
644 err: Some(e.to_string()),
645 },
646 },
647 msg::TASKER_TASK_FAILED => match serde_json::from_str(details) {
648 Ok(d) => MaaEvent::TaskerTaskFailed(d),
649 Err(e) => MaaEvent::Unknown {
650 msg: msg.to_string(),
651 raw_json: details.to_string(),
652 err: Some(e.to_string()),
653 },
654 },
655
656 msg::NODE_PIPELINE_NODE_STARTING => match serde_json::from_str(details) {
658 Ok(d) => MaaEvent::NodePipelineNodeStarting(d),
659 Err(e) => MaaEvent::Unknown {
660 msg: msg.to_string(),
661 raw_json: details.to_string(),
662 err: Some(e.to_string()),
663 },
664 },
665 msg::NODE_PIPELINE_NODE_SUCCEEDED => match serde_json::from_str(details) {
666 Ok(d) => MaaEvent::NodePipelineNodeSucceeded(d),
667 Err(e) => MaaEvent::Unknown {
668 msg: msg.to_string(),
669 raw_json: details.to_string(),
670 err: Some(e.to_string()),
671 },
672 },
673 msg::NODE_PIPELINE_NODE_FAILED => match serde_json::from_str(details) {
674 Ok(d) => MaaEvent::NodePipelineNodeFailed(d),
675 Err(e) => MaaEvent::Unknown {
676 msg: msg.to_string(),
677 raw_json: details.to_string(),
678 err: Some(e.to_string()),
679 },
680 },
681
682 msg::NODE_RECOGNITION_STARTING => match serde_json::from_str(details) {
684 Ok(d) => MaaEvent::NodeRecognitionStarting(d),
685 Err(e) => MaaEvent::Unknown {
686 msg: msg.to_string(),
687 raw_json: details.to_string(),
688 err: Some(e.to_string()),
689 },
690 },
691 msg::NODE_RECOGNITION_SUCCEEDED => match serde_json::from_str(details) {
692 Ok(d) => MaaEvent::NodeRecognitionSucceeded(d),
693 Err(e) => MaaEvent::Unknown {
694 msg: msg.to_string(),
695 raw_json: details.to_string(),
696 err: Some(e.to_string()),
697 },
698 },
699 msg::NODE_RECOGNITION_FAILED => match serde_json::from_str(details) {
700 Ok(d) => MaaEvent::NodeRecognitionFailed(d),
701 Err(e) => MaaEvent::Unknown {
702 msg: msg.to_string(),
703 raw_json: details.to_string(),
704 err: Some(e.to_string()),
705 },
706 },
707
708 msg::NODE_ACTION_STARTING => match serde_json::from_str(details) {
710 Ok(d) => MaaEvent::NodeActionStarting(d),
711 Err(e) => MaaEvent::Unknown {
712 msg: msg.to_string(),
713 raw_json: details.to_string(),
714 err: Some(e.to_string()),
715 },
716 },
717 msg::NODE_ACTION_SUCCEEDED => match serde_json::from_str(details) {
718 Ok(d) => MaaEvent::NodeActionSucceeded(d),
719 Err(e) => MaaEvent::Unknown {
720 msg: msg.to_string(),
721 raw_json: details.to_string(),
722 err: Some(e.to_string()),
723 },
724 },
725 msg::NODE_ACTION_FAILED => match serde_json::from_str(details) {
726 Ok(d) => MaaEvent::NodeActionFailed(d),
727 Err(e) => MaaEvent::Unknown {
728 msg: msg.to_string(),
729 raw_json: details.to_string(),
730 err: Some(e.to_string()),
731 },
732 },
733
734 msg::NODE_WAIT_FREEZES_STARTING => match serde_json::from_str(details) {
736 Ok(d) => MaaEvent::NodeWaitFreezesStarting(d),
737 Err(e) => MaaEvent::Unknown {
738 msg: msg.to_string(),
739 raw_json: details.to_string(),
740 err: Some(e.to_string()),
741 },
742 },
743 msg::NODE_WAIT_FREEZES_SUCCEEDED => match serde_json::from_str(details) {
744 Ok(d) => MaaEvent::NodeWaitFreezesSucceeded(d),
745 Err(e) => MaaEvent::Unknown {
746 msg: msg.to_string(),
747 raw_json: details.to_string(),
748 err: Some(e.to_string()),
749 },
750 },
751 msg::NODE_WAIT_FREEZES_FAILED => match serde_json::from_str(details) {
752 Ok(d) => MaaEvent::NodeWaitFreezesFailed(d),
753 Err(e) => MaaEvent::Unknown {
754 msg: msg.to_string(),
755 raw_json: details.to_string(),
756 err: Some(e.to_string()),
757 },
758 },
759
760 msg::NODE_NEXT_LIST_STARTING => match serde_json::from_str(details) {
762 Ok(d) => MaaEvent::NodeNextListStarting(d),
763 Err(e) => MaaEvent::Unknown {
764 msg: msg.to_string(),
765 raw_json: details.to_string(),
766 err: Some(e.to_string()),
767 },
768 },
769 msg::NODE_NEXT_LIST_SUCCEEDED => match serde_json::from_str(details) {
770 Ok(d) => MaaEvent::NodeNextListSucceeded(d),
771 Err(e) => MaaEvent::Unknown {
772 msg: msg.to_string(),
773 raw_json: details.to_string(),
774 err: Some(e.to_string()),
775 },
776 },
777 msg::NODE_NEXT_LIST_FAILED => match serde_json::from_str(details) {
778 Ok(d) => MaaEvent::NodeNextListFailed(d),
779 Err(e) => MaaEvent::Unknown {
780 msg: msg.to_string(),
781 raw_json: details.to_string(),
782 err: Some(e.to_string()),
783 },
784 },
785
786 msg::NODE_RECOGNITION_NODE_STARTING => match serde_json::from_str(details) {
788 Ok(d) => MaaEvent::NodeRecognitionNodeStarting(d),
789 Err(e) => MaaEvent::Unknown {
790 msg: msg.to_string(),
791 raw_json: details.to_string(),
792 err: Some(e.to_string()),
793 },
794 },
795 msg::NODE_RECOGNITION_NODE_SUCCEEDED => match serde_json::from_str(details) {
796 Ok(d) => MaaEvent::NodeRecognitionNodeSucceeded(d),
797 Err(e) => MaaEvent::Unknown {
798 msg: msg.to_string(),
799 raw_json: details.to_string(),
800 err: Some(e.to_string()),
801 },
802 },
803 msg::NODE_RECOGNITION_NODE_FAILED => match serde_json::from_str(details) {
804 Ok(d) => MaaEvent::NodeRecognitionNodeFailed(d),
805 Err(e) => MaaEvent::Unknown {
806 msg: msg.to_string(),
807 raw_json: details.to_string(),
808 err: Some(e.to_string()),
809 },
810 },
811
812 msg::NODE_ACTION_NODE_STARTING => match serde_json::from_str(details) {
814 Ok(d) => MaaEvent::NodeActionNodeStarting(d),
815 Err(e) => MaaEvent::Unknown {
816 msg: msg.to_string(),
817 raw_json: details.to_string(),
818 err: Some(e.to_string()),
819 },
820 },
821 msg::NODE_ACTION_NODE_SUCCEEDED => match serde_json::from_str(details) {
822 Ok(d) => MaaEvent::NodeActionNodeSucceeded(d),
823 Err(e) => MaaEvent::Unknown {
824 msg: msg.to_string(),
825 raw_json: details.to_string(),
826 err: Some(e.to_string()),
827 },
828 },
829 msg::NODE_ACTION_NODE_FAILED => match serde_json::from_str(details) {
830 Ok(d) => MaaEvent::NodeActionNodeFailed(d),
831 Err(e) => MaaEvent::Unknown {
832 msg: msg.to_string(),
833 raw_json: details.to_string(),
834 err: Some(e.to_string()),
835 },
836 },
837
838 _ => MaaEvent::Unknown {
839 msg: msg.to_string(),
840 raw_json: details.to_string(),
841 err: None,
842 },
843 }
844 }
845}
846
847#[cfg(test)]
850mod tests {
851 use super::*;
852 use serde_json::json;
853
854 #[test]
855 fn test_context_event_parsing_logic() {
856 let msg_wf = "Node.WaitFreezes.Succeeded";
857 let detail_wf = json!({
858 "task_id": 1,
859 "wf_id": 10,
860 "name": "WF",
861 "phase": "pre",
862 "roi": [1, 2, 3, 4],
863 "param": {},
864 "reco_ids": [100, 101],
865 "elapsed": 233,
866 "focus": null
867 }).to_string();
868
869 if let Some(ContextEvent::NodeWaitFreezes(t, d)) =
870 ContextEvent::from_notification(msg_wf, &detail_wf)
871 {
872 assert_eq!(t, NotificationType::Succeeded);
873 assert_eq!(d.wf_id, 10);
874 assert_eq!(d.reco_ids, vec![100, 101]);
875 assert_eq!(d.elapsed, Some(233));
876 } else {
877 panic!("Node.WaitFreezes parse failed");
878 }
879 let msg_reco = "Node.Recognition.Succeeded";
880 let detail_reco =
881 json!({ "task_id": 1, "reco_id": 100, "name": "R", "focus": null, "anchor": "A1" }).to_string();
882
883 if let Some(ContextEvent::NodeRecognition(t, d)) =
884 ContextEvent::from_notification(msg_reco, &detail_reco)
885 {
886 assert_eq!(t, NotificationType::Succeeded);
887 assert_eq!(d.anchor.as_deref(), Some("A1"));
888 } else {
889 panic!("Node.Recognition parse failed");
890 }
891
892 let msg_node = "Node.RecognitionNode.Starting";
893 let detail_node =
894 json!({ "task_id": 1, "node_id": 200, "name": "N", "focus": null }).to_string();
895
896 if let Some(ContextEvent::NodeRecognitionNode(t, _)) =
897 ContextEvent::from_notification(msg_node, &detail_node)
898 {
899 assert_eq!(t, NotificationType::Starting);
900 } else {
901 panic!("Node.RecognitionNode parse failed");
902 }
903 }
904}