Skip to main content

maa_framework/
common.rs

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