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 (select ONE only, no bitwise OR).
378    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
379    pub struct Win32ScreencapMethod: u64 {
380        const GDI = 1;
381        const FRAME_POOL = 1 << 1;
382        const DXGI_DESKTOP_DUP = 1 << 2;
383        const DXGI_DESKTOP_DUP_WINDOW = 1 << 3;
384        const PRINT_WINDOW = 1 << 4;
385        const SCREEN_DC = 1 << 5;
386    }
387}
388
389bitflags::bitflags! {
390    /// Win32 input method (select ONE only, no bitwise OR).
391    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
392    pub struct Win32InputMethod: u64 {
393        const SEIZE = 1;
394        const SEND_MESSAGE = 1 << 1;
395        const POST_MESSAGE = 1 << 2;
396        const LEGACY_EVENT = 1 << 3;
397        const POST_THREAD_MESSAGE = 1 << 4;
398        const SEND_MESSAGE_WITH_CURSOR_POS = 1 << 5;
399        const POST_MESSAGE_WITH_CURSOR_POS = 1 << 6;
400        const SEND_MESSAGE_WITH_WINDOW_POS = 1 << 7;
401        const POST_MESSAGE_WITH_WINDOW_POS = 1 << 8;
402    }
403}
404
405/// Details of a recognition operation result.
406#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
407pub struct RecognitionDetail {
408    /// Name of the node that performed recognition
409    pub node_name: String,
410    /// Algorithm used
411    pub algorithm: AlgorithmEnum,
412    /// Whether recognition was successful
413    pub hit: bool,
414    /// Bounding box of the recognized region
415    pub box_rect: Rect,
416    /// Algorithm-specific detail JSON
417    pub detail: serde_json::Value,
418    /// Raw screenshot (PNG encoded, only valid in debug mode)
419    #[serde(skip)]
420    pub raw_image: Option<Vec<u8>>,
421    /// Debug draw images (PNG encoded, only valid in debug mode)
422    #[serde(skip)]
423    pub draw_images: Vec<Vec<u8>>,
424    /// Sub-process recognition details (for And/Or combinators)
425    #[serde(default)]
426    pub sub_details: Vec<RecognitionDetail>,
427}
428
429impl RecognitionDetail {
430    pub fn as_template_match_result(&self) -> Option<TemplateMatchResult> {
431        serde_json::from_value(self.detail.clone()).ok()
432    }
433
434    pub fn as_feature_match_result(&self) -> Option<FeatureMatchResult> {
435        serde_json::from_value(self.detail.clone()).ok()
436    }
437
438    pub fn as_color_match_result(&self) -> Option<ColorMatchResult> {
439        serde_json::from_value(self.detail.clone()).ok()
440    }
441
442    pub fn as_ocr_result(&self) -> Option<OCRResult> {
443        serde_json::from_value(self.detail.clone()).ok()
444    }
445
446    pub fn as_neural_network_result(&self) -> Option<NeuralNetworkResult> {
447        serde_json::from_value(self.detail.clone()).ok()
448    }
449
450    pub fn as_custom_result(&self) -> Option<CustomRecognitionResult> {
451        serde_json::from_value(self.detail.clone()).ok()
452    }
453}
454
455/// Details of an action operation result.
456#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
457pub struct ActionDetail {
458    /// Name of the node that performed the action
459    pub node_name: String,
460    /// Action type
461    pub action: ActionEnum,
462    /// Target bounding box
463    pub box_rect: Rect,
464    /// Whether action was successful
465    pub success: bool,
466    /// Action-specific detail JSON
467    pub detail: serde_json::Value,
468}
469
470impl ActionDetail {
471    pub fn as_click_result(&self) -> Option<ClickActionResult> {
472        serde_json::from_value(self.detail.clone()).ok()
473    }
474
475    pub fn as_long_press_result(&self) -> Option<LongPressActionResult> {
476        serde_json::from_value(self.detail.clone()).ok()
477    }
478
479    pub fn as_swipe_result(&self) -> Option<SwipeActionResult> {
480        serde_json::from_value(self.detail.clone()).ok()
481    }
482
483    pub fn as_multi_swipe_result(&self) -> Option<MultiSwipeActionResult> {
484        serde_json::from_value(self.detail.clone()).ok()
485    }
486
487    pub fn as_click_key_result(&self) -> Option<ClickKeyActionResult> {
488        serde_json::from_value(self.detail.clone()).ok()
489    }
490
491    pub fn as_input_text_result(&self) -> Option<InputTextActionResult> {
492        serde_json::from_value(self.detail.clone()).ok()
493    }
494
495    pub fn as_app_result(&self) -> Option<AppActionResult> {
496        serde_json::from_value(self.detail.clone()).ok()
497    }
498
499    pub fn as_scroll_result(&self) -> Option<ScrollActionResult> {
500        serde_json::from_value(self.detail.clone()).ok()
501    }
502
503    pub fn as_touch_result(&self) -> Option<TouchActionResult> {
504        serde_json::from_value(self.detail.clone()).ok()
505    }
506
507    pub fn as_shell_result(&self) -> Option<ShellActionResult> {
508        serde_json::from_value(self.detail.clone()).ok()
509    }
510}
511
512// ============================================================================
513// Action Result Types
514// ============================================================================
515
516/// Result of a Click action.
517#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
518pub struct ClickActionResult {
519    pub point: Point,
520    pub contact: i32,
521    #[serde(default)]
522    pub pressure: i32,
523}
524
525/// Result of a LongPress action.
526#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
527pub struct LongPressActionResult {
528    pub point: Point,
529    pub duration: i32,
530    pub contact: i32,
531    #[serde(default)]
532    pub pressure: i32,
533}
534
535/// Result of a Swipe action.
536#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
537pub struct SwipeActionResult {
538    pub begin: Point,
539    pub end: Vec<Point>,
540    #[serde(default)]
541    pub end_hold: Vec<i32>,
542    #[serde(default)]
543    pub duration: Vec<i32>,
544    #[serde(default)]
545    pub only_hover: bool,
546    #[serde(default)]
547    pub starting: i32,
548    pub contact: i32,
549    #[serde(default)]
550    pub pressure: i32,
551}
552
553/// Result of a MultiSwipe action.
554#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
555pub struct MultiSwipeActionResult {
556    pub swipes: Vec<SwipeActionResult>,
557}
558
559/// Result of a ClickKey action.
560#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
561pub struct ClickKeyActionResult {
562    pub keycode: Vec<i32>,
563}
564
565/// Result of a LongPressKey action.
566#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
567pub struct LongPressKeyActionResult {
568    pub keycode: Vec<i32>,
569    pub duration: i32,
570}
571
572/// Result of an InputText action.
573#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
574pub struct InputTextActionResult {
575    pub text: String,
576}
577
578/// Result of a StartApp or StopApp action.
579#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
580pub struct AppActionResult {
581    pub package: String,
582}
583
584/// Result of a Scroll action.
585#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
586pub struct ScrollActionResult {
587    #[serde(default)]
588    pub point: Point,
589    pub dx: i32,
590    pub dy: i32,
591}
592
593/// Result of a TouchDown, TouchMove, or TouchUp action.
594#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
595pub struct TouchActionResult {
596    pub contact: i32,
597    pub point: Point,
598    #[serde(default)]
599    pub pressure: i32,
600}
601
602/// Result of a Shell action.
603#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
604pub struct ShellActionResult {
605    pub cmd: String,
606    pub shell_timeout: i32,
607    pub success: bool,
608    pub output: String,
609}
610
611/// Details of a pipeline node execution.
612#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
613pub struct NodeDetail {
614    pub node_name: String,
615    /// ID of the recognition operation
616    pub reco_id: MaaId,
617    /// ID of the action operation
618    pub act_id: MaaId,
619    /// Detailed recognition result
620    #[serde(default)]
621    pub recognition: Option<RecognitionDetail>,
622    /// Detailed action result
623    #[serde(default)]
624    pub action: Option<ActionDetail>,
625    /// Whether the node completed execution
626    pub completed: bool,
627}
628
629/// Details of a task execution.
630#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
631pub struct TaskDetail {
632    /// Entry point node name
633    pub entry: String,
634    /// List of node IDs that were executed
635    pub node_id_list: Vec<MaaId>,
636    /// Final status of the task
637    pub status: MaaStatus,
638    /// Detailed node information (hydrated from node_id_list)
639    #[serde(default)]
640    pub nodes: Vec<Option<NodeDetail>>,
641}
642
643/// Recognition algorithm types.
644#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
645#[serde(into = "String", from = "String")]
646pub enum AlgorithmEnum {
647    DirectHit,
648    TemplateMatch,
649    FeatureMatch,
650    ColorMatch,
651    OCR,
652    NeuralNetworkClassify,
653    NeuralNetworkDetect,
654    And,
655    Or,
656    Custom,
657    Other(String),
658}
659
660impl From<String> for AlgorithmEnum {
661    fn from(s: String) -> Self {
662        match s.as_str() {
663            "DirectHit" => Self::DirectHit,
664            "TemplateMatch" => Self::TemplateMatch,
665            "FeatureMatch" => Self::FeatureMatch,
666            "ColorMatch" => Self::ColorMatch,
667            "OCR" => Self::OCR,
668            "NeuralNetworkClassify" => Self::NeuralNetworkClassify,
669            "NeuralNetworkDetect" => Self::NeuralNetworkDetect,
670            "And" => Self::And,
671            "Or" => Self::Or,
672            "Custom" => Self::Custom,
673            _ => Self::Other(s),
674        }
675    }
676}
677
678impl From<AlgorithmEnum> for String {
679    fn from(algo: AlgorithmEnum) -> Self {
680        match algo {
681            AlgorithmEnum::DirectHit => "DirectHit".to_string(),
682            AlgorithmEnum::TemplateMatch => "TemplateMatch".to_string(),
683            AlgorithmEnum::FeatureMatch => "FeatureMatch".to_string(),
684            AlgorithmEnum::ColorMatch => "ColorMatch".to_string(),
685            AlgorithmEnum::OCR => "OCR".to_string(),
686            AlgorithmEnum::NeuralNetworkClassify => "NeuralNetworkClassify".to_string(),
687            AlgorithmEnum::NeuralNetworkDetect => "NeuralNetworkDetect".to_string(),
688            AlgorithmEnum::And => "And".to_string(),
689            AlgorithmEnum::Or => "Or".to_string(),
690            AlgorithmEnum::Custom => "Custom".to_string(),
691            AlgorithmEnum::Other(s) => s,
692        }
693    }
694}
695
696impl AlgorithmEnum {
697    pub fn as_str(&self) -> &str {
698        match self {
699            Self::DirectHit => "DirectHit",
700            Self::TemplateMatch => "TemplateMatch",
701            Self::FeatureMatch => "FeatureMatch",
702            Self::ColorMatch => "ColorMatch",
703            Self::OCR => "OCR",
704            Self::NeuralNetworkClassify => "NeuralNetworkClassify",
705            Self::NeuralNetworkDetect => "NeuralNetworkDetect",
706            Self::And => "And",
707            Self::Or => "Or",
708            Self::Custom => "Custom",
709            Self::Other(s) => s.as_str(),
710        }
711    }
712}
713
714/// Action types.
715#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
716#[serde(into = "String", from = "String")]
717pub enum ActionEnum {
718    DoNothing,
719    Click,
720    LongPress,
721    Swipe,
722    MultiSwipe,
723    TouchDown,
724    TouchMove,
725    TouchUp,
726    ClickKey,
727    LongPressKey,
728    KeyDown,
729    KeyUp,
730    InputText,
731    StartApp,
732    StopApp,
733    StopTask,
734    Scroll,
735    Command,
736    Shell,
737    Custom,
738    Other(String),
739}
740
741impl From<String> for ActionEnum {
742    fn from(s: String) -> Self {
743        match s.as_str() {
744            "DoNothing" => Self::DoNothing,
745            "Click" => Self::Click,
746            "LongPress" => Self::LongPress,
747            "Swipe" => Self::Swipe,
748            "MultiSwipe" => Self::MultiSwipe,
749            "TouchDown" => Self::TouchDown,
750            "TouchMove" => Self::TouchMove,
751            "TouchUp" => Self::TouchUp,
752            "ClickKey" => Self::ClickKey,
753            "LongPressKey" => Self::LongPressKey,
754            "KeyDown" => Self::KeyDown,
755            "KeyUp" => Self::KeyUp,
756            "InputText" => Self::InputText,
757            "StartApp" => Self::StartApp,
758            "StopApp" => Self::StopApp,
759            "StopTask" => Self::StopTask,
760            "Scroll" => Self::Scroll,
761            "Command" => Self::Command,
762            "Shell" => Self::Shell,
763            "Custom" => Self::Custom,
764            _ => Self::Other(s),
765        }
766    }
767}
768
769impl From<ActionEnum> for String {
770    fn from(act: ActionEnum) -> Self {
771        match act {
772            ActionEnum::DoNothing => "DoNothing".to_string(),
773            ActionEnum::Click => "Click".to_string(),
774            ActionEnum::LongPress => "LongPress".to_string(),
775            ActionEnum::Swipe => "Swipe".to_string(),
776            ActionEnum::MultiSwipe => "MultiSwipe".to_string(),
777            ActionEnum::TouchDown => "TouchDown".to_string(),
778            ActionEnum::TouchMove => "TouchMove".to_string(),
779            ActionEnum::TouchUp => "TouchUp".to_string(),
780            ActionEnum::ClickKey => "ClickKey".to_string(),
781            ActionEnum::LongPressKey => "LongPressKey".to_string(),
782            ActionEnum::KeyDown => "KeyDown".to_string(),
783            ActionEnum::KeyUp => "KeyUp".to_string(),
784            ActionEnum::InputText => "InputText".to_string(),
785            ActionEnum::StartApp => "StartApp".to_string(),
786            ActionEnum::StopApp => "StopApp".to_string(),
787            ActionEnum::StopTask => "StopTask".to_string(),
788            ActionEnum::Scroll => "Scroll".to_string(),
789            ActionEnum::Command => "Command".to_string(),
790            ActionEnum::Shell => "Shell".to_string(),
791            ActionEnum::Custom => "Custom".to_string(),
792            ActionEnum::Other(s) => s,
793        }
794    }
795}
796
797impl ActionEnum {
798    pub fn as_str(&self) -> &str {
799        match self {
800            Self::DoNothing => "DoNothing",
801            Self::Click => "Click",
802            Self::LongPress => "LongPress",
803            Self::Swipe => "Swipe",
804            Self::MultiSwipe => "MultiSwipe",
805            Self::TouchDown => "TouchDown",
806            Self::TouchMove => "TouchMove",
807            Self::TouchUp => "TouchUp",
808            Self::ClickKey => "ClickKey",
809            Self::LongPressKey => "LongPressKey",
810            Self::KeyDown => "KeyDown",
811            Self::KeyUp => "KeyUp",
812            Self::InputText => "InputText",
813            Self::StartApp => "StartApp",
814            Self::StopApp => "StopApp",
815            Self::StopTask => "StopTask",
816            Self::Scroll => "Scroll",
817            Self::Command => "Command",
818            Self::Shell => "Shell",
819            Self::Custom => "Custom",
820            Self::Other(s) => s.as_str(),
821        }
822    }
823}
824
825/// Notification type for event callbacks.
826#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
827#[non_exhaustive]
828pub enum NotificationType {
829    Starting,
830    Succeeded,
831    Failed,
832    Unknown,
833}
834
835impl NotificationType {
836    pub fn from_message(msg: &str) -> Self {
837        if msg.ends_with(".Starting") {
838            Self::Starting
839        } else if msg.ends_with(".Succeeded") {
840            Self::Succeeded
841        } else if msg.ends_with(".Failed") {
842            Self::Failed
843        } else {
844            Self::Unknown
845        }
846    }
847}
848
849// --- Result types ---
850
851#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
852pub struct BoxAndScore {
853    #[serde(rename = "box")]
854    pub box_rect: (i32, i32, i32, i32),
855    pub score: f64,
856}
857
858#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
859pub struct BoxAndCount {
860    #[serde(rename = "box")]
861    pub box_rect: (i32, i32, i32, i32),
862    pub count: i32,
863}
864
865pub type TemplateMatchResult = BoxAndScore;
866pub type FeatureMatchResult = BoxAndCount;
867pub type ColorMatchResult = BoxAndCount;
868
869#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
870pub struct OCRResult {
871    #[serde(flatten)]
872    pub base: BoxAndScore,
873    pub text: String,
874}
875
876#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
877pub struct NeuralNetworkResult {
878    #[serde(flatten)]
879    pub base: BoxAndScore,
880    pub cls_index: i32,
881    pub label: String,
882}
883
884pub type NeuralNetworkClassifyResult = NeuralNetworkResult;
885pub type NeuralNetworkDetectResult = NeuralNetworkResult;
886
887#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
888pub struct CustomRecognitionResult {
889    #[serde(rename = "box")]
890    pub box_rect: (i32, i32, i32, i32),
891    pub detail: serde_json::Value,
892}