Skip to main content

maa_framework/
common.rs

1//! Common types and data structures used throughout the SDK.
2
3use crate::sys;
4use std::fmt;
5
6/// Status of an asynchronous operation.
7///
8/// Most SDK operations are asynchronous and return immediately with an ID.
9/// Use this status to check completion state.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
11pub struct MaaStatus(pub i32);
12
13/// Unique identifier for operations, tasks, and nodes.
14pub type MaaId = i64;
15
16impl MaaStatus {
17    pub const INVALID: Self = Self(sys::MaaStatusEnum_MaaStatus_Invalid as i32);
18    pub const PENDING: Self = Self(sys::MaaStatusEnum_MaaStatus_Pending as i32);
19    pub const RUNNING: Self = Self(sys::MaaStatusEnum_MaaStatus_Running as i32);
20    pub const SUCCEEDED: Self = Self(sys::MaaStatusEnum_MaaStatus_Succeeded as i32);
21    pub const FAILED: Self = Self(sys::MaaStatusEnum_MaaStatus_Failed as i32);
22
23    /// Check if the operation succeeded.
24    pub fn is_success(&self) -> bool {
25        *self == Self::SUCCEEDED
26    }
27
28    /// Check if the operation succeeded (alias for is_success).
29    pub fn succeeded(&self) -> bool {
30        *self == Self::SUCCEEDED
31    }
32
33    /// Check if the operation failed.
34    pub fn is_failed(&self) -> bool {
35        *self == Self::FAILED
36    }
37
38    /// Check if the operation failed (alias for is_failed).
39    pub fn failed(&self) -> bool {
40        *self == Self::FAILED
41    }
42
43    /// Check if the operation is done (succeeded or failed).
44    pub fn done(&self) -> bool {
45        *self == Self::SUCCEEDED || *self == Self::FAILED
46    }
47
48    /// Check if the operation is pending.
49    pub fn pending(&self) -> bool {
50        *self == Self::PENDING
51    }
52
53    /// Check if the operation is running.
54    pub fn running(&self) -> bool {
55        *self == Self::RUNNING
56    }
57}
58
59impl fmt::Display for MaaStatus {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match *self {
62            Self::INVALID => write!(f, "Invalid"),
63            Self::PENDING => write!(f, "Pending"),
64            Self::RUNNING => write!(f, "Running"),
65            Self::SUCCEEDED => write!(f, "Succeeded"),
66            Self::FAILED => write!(f, "Failed"),
67            _ => write!(f, "Unknown({})", self.0),
68        }
69    }
70}
71
72pub fn check_bool(ret: sys::MaaBool) -> crate::MaaResult<()> {
73    if ret != 0 {
74        Ok(())
75    } else {
76        Err(crate::MaaError::FrameworkError(0))
77    }
78}
79
80impl From<i32> for MaaStatus {
81    fn from(value: i32) -> Self {
82        Self(value)
83    }
84}
85
86/// A rectangle representing a region on screen.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
88pub struct Rect {
89    pub x: i32,
90    pub y: i32,
91    pub width: i32,
92    pub height: i32,
93}
94
95impl From<sys::MaaRect> for Rect {
96    fn from(r: sys::MaaRect) -> Self {
97        Self {
98            x: r.x,
99            y: r.y,
100            width: r.width,
101            height: r.height,
102        }
103    }
104}
105
106impl Default for Rect {
107    fn default() -> Self {
108        Self {
109            x: 0,
110            y: 0,
111            width: 0,
112            height: 0,
113        }
114    }
115}
116
117/// A point representing a location on screen.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
119pub struct Point {
120    pub x: i32,
121    pub y: i32,
122}
123
124impl Point {
125    pub fn new(x: i32, y: i32) -> Self {
126        Self { x, y }
127    }
128}
129
130// ============================================================================
131// Gamepad Types (Windows only, requires ViGEm Bus Driver)
132// ============================================================================
133
134/// Virtual gamepad type for GamepadController.
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
136#[repr(u64)]
137#[non_exhaustive]
138pub enum GamepadType {
139    /// Microsoft Xbox 360 Controller (wired)
140    Xbox360 = 0,
141    /// Sony DualShock 4 Controller (wired)
142    DualShock4 = 1,
143}
144
145/// Gamepad contact (analog stick or trigger) for touch mapping.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
147#[repr(i32)]
148#[non_exhaustive]
149pub enum GamepadContact {
150    /// Left analog stick: x/y range -32768~32767
151    LeftStick = 0,
152    /// Right analog stick: x/y range -32768~32767
153    RightStick = 1,
154    /// Left trigger: pressure 0~255
155    LeftTrigger = 2,
156    /// Right trigger: pressure 0~255
157    RightTrigger = 3,
158}
159
160bitflags::bitflags! {
161    /// Gamepad button flags (XUSB protocol values).
162    ///
163    /// Use bitwise OR to combine multiple buttons.
164    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
165    pub struct GamepadButton: u32 {
166        // D-pad
167        const DPAD_UP = 0x0001;
168        const DPAD_DOWN = 0x0002;
169        const DPAD_LEFT = 0x0004;
170        const DPAD_RIGHT = 0x0008;
171
172        // Control buttons
173        const START = 0x0010;
174        const BACK = 0x0020;
175        const LEFT_THUMB = 0x0040;  // L3
176        const RIGHT_THUMB = 0x0080; // R3
177
178        // Shoulder buttons
179        const LB = 0x0100; // Left Bumper / L1
180        const RB = 0x0200; // Right Bumper / R1
181
182        // Guide button
183        const GUIDE = 0x0400;
184
185        // Face buttons (Xbox layout)
186        const A = 0x1000;
187        const B = 0x2000;
188        const X = 0x4000;
189        const Y = 0x8000;
190
191        // DS4 special buttons
192        const PS = 0x10000;
193        const TOUCHPAD = 0x20000;
194    }
195}
196
197impl GamepadButton {
198    // DS4 face button aliases
199    pub const CROSS: Self = Self::A;
200    pub const CIRCLE: Self = Self::B;
201    pub const SQUARE: Self = Self::X;
202    pub const TRIANGLE: Self = Self::Y;
203    pub const L1: Self = Self::LB;
204    pub const R1: Self = Self::RB;
205    pub const L3: Self = Self::LEFT_THUMB;
206    pub const R3: Self = Self::RIGHT_THUMB;
207    pub const OPTIONS: Self = Self::START;
208    pub const SHARE: Self = Self::BACK;
209}
210
211// ============================================================================
212// Controller Feature Flags
213// ============================================================================
214
215bitflags::bitflags! {
216    /// Controller feature flags for CustomController.
217    ///
218    /// These flags indicate which input methods the controller supports/prefers.
219    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
220    pub struct ControllerFeature: u64 {
221        /// Controller prefers touch_down/touch_move/touch_up instead of click/swipe.
222        /// When set, ControllerAgent will use touch_down/touch_up to simulate click,
223        /// and touch_down/touch_move/touch_up to simulate swipe.
224        const USE_MOUSE_DOWN_UP_INSTEAD_OF_CLICK = 1;
225        /// Controller prefers key_down/key_up instead of click_key.
226        /// When set, ControllerAgent will use key_down + key_up to simulate click_key.
227        const USE_KEY_DOWN_UP_INSTEAD_OF_CLICK = 1 << 1;
228        /// Controller does not scale touch points automatically.
229        /// When set, ControllerAgent will skip coordinate scaling for touch operations.
230        const NO_SCALING_TOUCH_POINTS = 1 << 2;
231    }
232}
233
234// ============================================================================
235// ADB Controller Methods
236// ============================================================================
237
238bitflags::bitflags! {
239    /// ADB screencap method flags.
240    ///
241    /// Use bitwise OR to set the methods you need.
242    /// MaaFramework will test all provided methods and use the fastest available one.
243    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244    pub struct AdbScreencapMethod: u64 {
245        const ENCODE_TO_FILE_AND_PULL = 1;
246        const ENCODE = 1 << 1;
247        const RAW_WITH_GZIP = 1 << 2;
248        const RAW_BY_NETCAT = 1 << 3;
249        const MINICAP_DIRECT = 1 << 4;
250        const MINICAP_STREAM = 1 << 5;
251        const EMULATOR_EXTRAS = 1 << 6;
252        const ALL = !0;
253    }
254}
255
256impl AdbScreencapMethod {
257    /// Default methods (all except RawByNetcat, MinicapDirect, MinicapStream)
258    pub const DEFAULT: Self = Self::from_bits_truncate(
259        Self::ALL.bits()
260            & !Self::RAW_BY_NETCAT.bits()
261            & !Self::MINICAP_DIRECT.bits()
262            & !Self::MINICAP_STREAM.bits(),
263    );
264}
265
266impl Default for AdbScreencapMethod {
267    fn default() -> Self {
268        Self::DEFAULT
269    }
270}
271
272bitflags::bitflags! {
273    /// ADB input method flags.
274    ///
275    /// Use bitwise OR to set the methods you need.
276    /// MaaFramework will select the first available method according to priority.
277    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
278    pub struct AdbInputMethod: u64 {
279        const ADB_SHELL = 1;
280        const MINITOUCH_AND_ADB_KEY = 1 << 1;
281        const MAATOUCH = 1 << 2;
282        const EMULATOR_EXTRAS = 1 << 3;
283        const ALL = !0;
284    }
285}
286
287impl AdbInputMethod {
288    /// Default methods (all except EmulatorExtras)
289    pub const DEFAULT: Self =
290        Self::from_bits_truncate(Self::ALL.bits() & !Self::EMULATOR_EXTRAS.bits());
291}
292
293impl Default for AdbInputMethod {
294    fn default() -> Self {
295        Self::DEFAULT
296    }
297}
298
299// ============================================================================
300// Win32 Controller Methods
301// ============================================================================
302
303bitflags::bitflags! {
304    /// Win32 screencap method (select ONE only, no bitwise OR).
305    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
306    pub struct Win32ScreencapMethod: u64 {
307        const GDI = 1;
308        const FRAME_POOL = 1 << 1;
309        const DXGI_DESKTOP_DUP = 1 << 2;
310        const DXGI_DESKTOP_DUP_WINDOW = 1 << 3;
311        const PRINT_WINDOW = 1 << 4;
312        const SCREEN_DC = 1 << 5;
313    }
314}
315
316bitflags::bitflags! {
317    /// Win32 input method (select ONE only, no bitwise OR).
318    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
319    pub struct Win32InputMethod: u64 {
320        const SEIZE = 1;
321        const SEND_MESSAGE = 1 << 1;
322        const POST_MESSAGE = 1 << 2;
323        const LEGACY_EVENT = 1 << 3;
324        const POST_THREAD_MESSAGE = 1 << 4;
325        const SEND_MESSAGE_WITH_CURSOR_POS = 1 << 5;
326        const POST_MESSAGE_WITH_CURSOR_POS = 1 << 6;
327    }
328}
329
330/// Details of a recognition operation result.
331#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
332pub struct RecognitionDetail {
333    /// Name of the node that performed recognition
334    pub node_name: String,
335    /// Algorithm used
336    pub algorithm: AlgorithmEnum,
337    /// Whether recognition was successful
338    pub hit: bool,
339    /// Bounding box of the recognized region
340    pub box_rect: Rect,
341    /// Algorithm-specific detail JSON
342    pub detail: serde_json::Value,
343    /// Raw screenshot (PNG encoded, only valid in debug mode)
344    #[serde(skip)]
345    pub raw_image: Option<Vec<u8>>,
346    /// Debug draw images (PNG encoded, only valid in debug mode)
347    #[serde(skip)]
348    pub draw_images: Vec<Vec<u8>>,
349    /// Sub-process recognition details (for And/Or combinators)
350    #[serde(default)]
351    pub sub_details: Vec<RecognitionDetail>,
352}
353
354impl RecognitionDetail {
355    pub fn as_template_match_result(&self) -> Option<TemplateMatchResult> {
356        serde_json::from_value(self.detail.clone()).ok()
357    }
358
359    pub fn as_feature_match_result(&self) -> Option<FeatureMatchResult> {
360        serde_json::from_value(self.detail.clone()).ok()
361    }
362
363    pub fn as_color_match_result(&self) -> Option<ColorMatchResult> {
364        serde_json::from_value(self.detail.clone()).ok()
365    }
366
367    pub fn as_ocr_result(&self) -> Option<OCRResult> {
368        serde_json::from_value(self.detail.clone()).ok()
369    }
370
371    pub fn as_neural_network_result(&self) -> Option<NeuralNetworkResult> {
372        serde_json::from_value(self.detail.clone()).ok()
373    }
374
375    pub fn as_custom_result(&self) -> Option<CustomRecognitionResult> {
376        serde_json::from_value(self.detail.clone()).ok()
377    }
378}
379
380/// Details of an action operation result.
381#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
382pub struct ActionDetail {
383    /// Name of the node that performed the action
384    pub node_name: String,
385    /// Action type
386    pub action: ActionEnum,
387    /// Target bounding box
388    pub box_rect: Rect,
389    /// Whether action was successful
390    pub success: bool,
391    /// Action-specific detail JSON
392    pub detail: serde_json::Value,
393}
394
395impl ActionDetail {
396    pub fn as_click_result(&self) -> Option<ClickActionResult> {
397        serde_json::from_value(self.detail.clone()).ok()
398    }
399
400    pub fn as_long_press_result(&self) -> Option<LongPressActionResult> {
401        serde_json::from_value(self.detail.clone()).ok()
402    }
403
404    pub fn as_swipe_result(&self) -> Option<SwipeActionResult> {
405        serde_json::from_value(self.detail.clone()).ok()
406    }
407
408    pub fn as_multi_swipe_result(&self) -> Option<MultiSwipeActionResult> {
409        serde_json::from_value(self.detail.clone()).ok()
410    }
411
412    pub fn as_click_key_result(&self) -> Option<ClickKeyActionResult> {
413        serde_json::from_value(self.detail.clone()).ok()
414    }
415
416    pub fn as_input_text_result(&self) -> Option<InputTextActionResult> {
417        serde_json::from_value(self.detail.clone()).ok()
418    }
419
420    pub fn as_app_result(&self) -> Option<AppActionResult> {
421        serde_json::from_value(self.detail.clone()).ok()
422    }
423
424    pub fn as_scroll_result(&self) -> Option<ScrollActionResult> {
425        serde_json::from_value(self.detail.clone()).ok()
426    }
427
428    pub fn as_touch_result(&self) -> Option<TouchActionResult> {
429        serde_json::from_value(self.detail.clone()).ok()
430    }
431
432    pub fn as_shell_result(&self) -> Option<ShellActionResult> {
433        serde_json::from_value(self.detail.clone()).ok()
434    }
435}
436
437// ============================================================================
438// Action Result Types
439// ============================================================================
440
441/// Result of a Click action.
442#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
443pub struct ClickActionResult {
444    pub point: Point,
445    pub contact: i32,
446}
447
448/// Result of a LongPress action.
449#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
450pub struct LongPressActionResult {
451    pub point: Point,
452    pub duration: i32,
453    pub contact: i32,
454}
455
456/// Result of a Swipe action.
457#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
458pub struct SwipeActionResult {
459    pub begin: Point,
460    pub end: Vec<Point>,
461    #[serde(default)]
462    pub end_hold: Vec<i32>,
463    #[serde(default)]
464    pub duration: Vec<i32>,
465    #[serde(default)]
466    pub only_hover: bool,
467    #[serde(default)]
468    pub starting: i32,
469    pub contact: i32,
470}
471
472/// Result of a MultiSwipe action.
473#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
474pub struct MultiSwipeActionResult {
475    pub swipes: Vec<SwipeActionResult>,
476}
477
478/// Result of a ClickKey action.
479#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
480pub struct ClickKeyActionResult {
481    pub keycode: Vec<i32>,
482}
483
484/// Result of a LongPressKey action.
485#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
486pub struct LongPressKeyActionResult {
487    pub keycode: Vec<i32>,
488    pub duration: i32,
489}
490
491/// Result of an InputText action.
492#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
493pub struct InputTextActionResult {
494    pub text: String,
495}
496
497/// Result of a StartApp or StopApp action.
498#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
499pub struct AppActionResult {
500    pub package: String,
501}
502
503/// Result of a Scroll action.
504#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
505pub struct ScrollActionResult {
506    pub dx: i32,
507    pub dy: i32,
508}
509
510/// Result of a TouchDown, TouchMove, or TouchUp action.
511#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
512pub struct TouchActionResult {
513    pub contact: i32,
514    pub point: Point,
515    #[serde(default)]
516    pub pressure: i32,
517}
518
519/// Result of a Shell action.
520#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
521pub struct ShellActionResult {
522    pub cmd: String,
523    pub timeout: i32,
524    pub success: bool,
525    pub output: String,
526}
527
528/// Details of a pipeline node execution.
529#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
530pub struct NodeDetail {
531    pub node_name: String,
532    /// ID of the recognition operation
533    pub reco_id: MaaId,
534    /// ID of the action operation
535    pub act_id: MaaId,
536    /// Detailed recognition result
537    #[serde(default)]
538    pub recognition: Option<RecognitionDetail>,
539    /// Detailed action result
540    #[serde(default)]
541    pub action: Option<ActionDetail>,
542    /// Whether the node completed execution
543    pub completed: bool,
544}
545
546/// Details of a task execution.
547#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
548pub struct TaskDetail {
549    /// Entry point node name
550    pub entry: String,
551    /// List of node IDs that were executed
552    pub node_id_list: Vec<MaaId>,
553    /// Final status of the task
554    pub status: MaaStatus,
555    /// Detailed node information (hydrated from node_id_list)
556    #[serde(default)]
557    pub nodes: Vec<Option<NodeDetail>>,
558}
559
560/// Recognition algorithm types.
561#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
562#[serde(into = "String", from = "String")]
563pub enum AlgorithmEnum {
564    DirectHit,
565    TemplateMatch,
566    FeatureMatch,
567    ColorMatch,
568    OCR,
569    NeuralNetworkClassify,
570    NeuralNetworkDetect,
571    And,
572    Or,
573    Custom,
574    Other(String),
575}
576
577impl From<String> for AlgorithmEnum {
578    fn from(s: String) -> Self {
579        match s.as_str() {
580            "DirectHit" => Self::DirectHit,
581            "TemplateMatch" => Self::TemplateMatch,
582            "FeatureMatch" => Self::FeatureMatch,
583            "ColorMatch" => Self::ColorMatch,
584            "OCR" => Self::OCR,
585            "NeuralNetworkClassify" => Self::NeuralNetworkClassify,
586            "NeuralNetworkDetect" => Self::NeuralNetworkDetect,
587            "And" => Self::And,
588            "Or" => Self::Or,
589            "Custom" => Self::Custom,
590            _ => Self::Other(s),
591        }
592    }
593}
594
595impl From<AlgorithmEnum> for String {
596    fn from(algo: AlgorithmEnum) -> Self {
597        match algo {
598            AlgorithmEnum::DirectHit => "DirectHit".to_string(),
599            AlgorithmEnum::TemplateMatch => "TemplateMatch".to_string(),
600            AlgorithmEnum::FeatureMatch => "FeatureMatch".to_string(),
601            AlgorithmEnum::ColorMatch => "ColorMatch".to_string(),
602            AlgorithmEnum::OCR => "OCR".to_string(),
603            AlgorithmEnum::NeuralNetworkClassify => "NeuralNetworkClassify".to_string(),
604            AlgorithmEnum::NeuralNetworkDetect => "NeuralNetworkDetect".to_string(),
605            AlgorithmEnum::And => "And".to_string(),
606            AlgorithmEnum::Or => "Or".to_string(),
607            AlgorithmEnum::Custom => "Custom".to_string(),
608            AlgorithmEnum::Other(s) => s,
609        }
610    }
611}
612
613impl AlgorithmEnum {
614    pub fn as_str(&self) -> &str {
615        match self {
616            Self::DirectHit => "DirectHit",
617            Self::TemplateMatch => "TemplateMatch",
618            Self::FeatureMatch => "FeatureMatch",
619            Self::ColorMatch => "ColorMatch",
620            Self::OCR => "OCR",
621            Self::NeuralNetworkClassify => "NeuralNetworkClassify",
622            Self::NeuralNetworkDetect => "NeuralNetworkDetect",
623            Self::And => "And",
624            Self::Or => "Or",
625            Self::Custom => "Custom",
626            Self::Other(s) => s.as_str(),
627        }
628    }
629}
630
631/// Action types.
632#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
633#[serde(into = "String", from = "String")]
634pub enum ActionEnum {
635    DoNothing,
636    Click,
637    LongPress,
638    Swipe,
639    MultiSwipe,
640    TouchDown,
641    TouchMove,
642    TouchUp,
643    ClickKey,
644    LongPressKey,
645    KeyDown,
646    KeyUp,
647    InputText,
648    StartApp,
649    StopApp,
650    StopTask,
651    Scroll,
652    Command,
653    Shell,
654    Custom,
655    Other(String),
656}
657
658impl From<String> for ActionEnum {
659    fn from(s: String) -> Self {
660        match s.as_str() {
661            "DoNothing" => Self::DoNothing,
662            "Click" => Self::Click,
663            "LongPress" => Self::LongPress,
664            "Swipe" => Self::Swipe,
665            "MultiSwipe" => Self::MultiSwipe,
666            "TouchDown" => Self::TouchDown,
667            "TouchMove" => Self::TouchMove,
668            "TouchUp" => Self::TouchUp,
669            "ClickKey" => Self::ClickKey,
670            "LongPressKey" => Self::LongPressKey,
671            "KeyDown" => Self::KeyDown,
672            "KeyUp" => Self::KeyUp,
673            "InputText" => Self::InputText,
674            "StartApp" => Self::StartApp,
675            "StopApp" => Self::StopApp,
676            "StopTask" => Self::StopTask,
677            "Scroll" => Self::Scroll,
678            "Command" => Self::Command,
679            "Shell" => Self::Shell,
680            "Custom" => Self::Custom,
681            _ => Self::Other(s),
682        }
683    }
684}
685
686impl From<ActionEnum> for String {
687    fn from(act: ActionEnum) -> Self {
688        match act {
689            ActionEnum::DoNothing => "DoNothing".to_string(),
690            ActionEnum::Click => "Click".to_string(),
691            ActionEnum::LongPress => "LongPress".to_string(),
692            ActionEnum::Swipe => "Swipe".to_string(),
693            ActionEnum::MultiSwipe => "MultiSwipe".to_string(),
694            ActionEnum::TouchDown => "TouchDown".to_string(),
695            ActionEnum::TouchMove => "TouchMove".to_string(),
696            ActionEnum::TouchUp => "TouchUp".to_string(),
697            ActionEnum::ClickKey => "ClickKey".to_string(),
698            ActionEnum::LongPressKey => "LongPressKey".to_string(),
699            ActionEnum::KeyDown => "KeyDown".to_string(),
700            ActionEnum::KeyUp => "KeyUp".to_string(),
701            ActionEnum::InputText => "InputText".to_string(),
702            ActionEnum::StartApp => "StartApp".to_string(),
703            ActionEnum::StopApp => "StopApp".to_string(),
704            ActionEnum::StopTask => "StopTask".to_string(),
705            ActionEnum::Scroll => "Scroll".to_string(),
706            ActionEnum::Command => "Command".to_string(),
707            ActionEnum::Shell => "Shell".to_string(),
708            ActionEnum::Custom => "Custom".to_string(),
709            ActionEnum::Other(s) => s,
710        }
711    }
712}
713
714impl ActionEnum {
715    pub fn as_str(&self) -> &str {
716        match self {
717            Self::DoNothing => "DoNothing",
718            Self::Click => "Click",
719            Self::LongPress => "LongPress",
720            Self::Swipe => "Swipe",
721            Self::MultiSwipe => "MultiSwipe",
722            Self::TouchDown => "TouchDown",
723            Self::TouchMove => "TouchMove",
724            Self::TouchUp => "TouchUp",
725            Self::ClickKey => "ClickKey",
726            Self::LongPressKey => "LongPressKey",
727            Self::KeyDown => "KeyDown",
728            Self::KeyUp => "KeyUp",
729            Self::InputText => "InputText",
730            Self::StartApp => "StartApp",
731            Self::StopApp => "StopApp",
732            Self::StopTask => "StopTask",
733            Self::Scroll => "Scroll",
734            Self::Command => "Command",
735            Self::Shell => "Shell",
736            Self::Custom => "Custom",
737            Self::Other(s) => s.as_str(),
738        }
739    }
740}
741
742/// Notification type for event callbacks.
743#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
744#[non_exhaustive]
745pub enum NotificationType {
746    Starting,
747    Succeeded,
748    Failed,
749    Unknown,
750}
751
752impl NotificationType {
753    pub fn from_message(msg: &str) -> Self {
754        if msg.ends_with(".Starting") {
755            Self::Starting
756        } else if msg.ends_with(".Succeeded") {
757            Self::Succeeded
758        } else if msg.ends_with(".Failed") {
759            Self::Failed
760        } else {
761            Self::Unknown
762        }
763    }
764}
765
766// --- Result types ---
767
768#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
769pub struct BoxAndScore {
770    #[serde(rename = "box")]
771    pub box_rect: (i32, i32, i32, i32),
772    pub score: f64,
773}
774
775#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
776pub struct BoxAndCount {
777    #[serde(rename = "box")]
778    pub box_rect: (i32, i32, i32, i32),
779    pub count: i32,
780}
781
782pub type TemplateMatchResult = BoxAndScore;
783pub type FeatureMatchResult = BoxAndCount;
784pub type ColorMatchResult = BoxAndCount;
785
786#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
787pub struct OCRResult {
788    #[serde(flatten)]
789    pub base: BoxAndScore,
790    pub text: String,
791}
792
793#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
794pub struct NeuralNetworkResult {
795    #[serde(flatten)]
796    pub base: BoxAndScore,
797    pub cls_index: i32,
798    pub label: String,
799}
800
801pub type NeuralNetworkClassifyResult = NeuralNetworkResult;
802pub type NeuralNetworkDetectResult = NeuralNetworkResult;
803
804#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
805pub struct CustomRecognitionResult {
806    #[serde(rename = "box")]
807    pub box_rect: (i32, i32, i32, i32),
808    pub detail: serde_json::Value,
809}