Skip to main content

maa_framework/
pipeline.rs

1//! Pipeline configuration types for recognition and action definitions.
2
3use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
4use serde_json::Value;
5
6// --- Custom Deserializers for Scalar/Array Polymorphism ---
7// The C API may return either a scalar or an array for some fields.
8
9/// Deserialize a value that can be either T or Vec<T> into Vec<T>
10fn scalar_or_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
11where
12    D: Deserializer<'de>,
13    T: DeserializeOwned,
14{
15    let value = Value::deserialize(deserializer)?;
16
17    // Try to parse as Vec<T> first
18    if let Ok(vec) = serde_json::from_value::<Vec<T>>(value.clone()) {
19        return Ok(vec);
20    }
21
22    // Fallback to T
23    if let Ok(single) = serde_json::from_value::<T>(value) {
24        return Ok(vec![single]);
25    }
26
27    Err(serde::de::Error::custom("Expected T or Vec<T>"))
28}
29
30// --- Common Types ---
31
32/// Rectangle coordinates: (x, y, width, height).
33pub type Rect = (i32, i32, i32, i32);
34/// Region of interest: (x, y, width, height). Use [0, 0, 0, 0] for full screen.
35pub type Roi = (i32, i32, i32, i32);
36
37/// Target can be:
38/// - true: recognized position
39/// - "NodeName": position from previously executed node
40/// - \[ x, y \]: point (2 elements)
41/// - \[ x, y, w, h \]: area (4 elements)
42#[derive(Serialize, Deserialize, Debug, Clone)]
43#[serde(untagged)]
44pub enum Target {
45    Bool(bool),
46    Name(String),
47    Point((i32, i32)),
48    Rect(Rect),
49}
50
51impl Default for Target {
52    fn default() -> Self {
53        Target::Bool(true)
54    }
55}
56
57// --- Node Attribute ---
58
59/// Node attribute for specifying behavior in `next` and `on_error` lists.
60///
61/// Allows setting additional control parameters when referencing nodes.
62#[derive(Serialize, Deserialize, Debug, Clone, Default)]
63pub struct NodeAttr {
64    /// Node name to reference.
65    #[serde(default)]
66    pub name: String,
67    /// Whether to return to this node after the referenced node completes.
68    #[serde(default)]
69    pub jump_back: bool,
70    /// Whether to use an anchor reference.
71    #[serde(default)]
72    pub anchor: bool,
73}
74
75// --- Wait Freezes ---
76
77/// Configuration for waiting until the screen stops changing.
78///
79/// Used in `pre_wait_freezes`, `post_wait_freezes`, and `repeat_wait_freezes`
80/// to wait for the screen to stabilize before/after actions.
81#[derive(Serialize, Deserialize, Debug, Clone)]
82pub struct WaitFreezes {
83    /// Duration in milliseconds the screen must remain stable. Default: 1.
84    #[serde(default = "default_wait_time")]
85    pub time: i32,
86    /// Target area to monitor for changes.
87    #[serde(default)]
88    pub target: Target,
89    /// Offset applied to the target area.
90    #[serde(default)]
91    pub target_offset: Rect,
92    /// Similarity threshold for detecting changes. Default: 0.95.
93    #[serde(default = "default_wait_threshold")]
94    pub threshold: f64,
95    /// Comparison method (cv::TemplateMatchModes). Default: 5.
96    #[serde(default = "default_wait_method")]
97    pub method: i32,
98    /// Minimum interval between checks in milliseconds. Default: 1000.
99    #[serde(default = "default_rate_limit")]
100    pub rate_limit: i32,
101    /// Overall timeout in milliseconds. Default: 20000.
102    #[serde(default = "default_timeout")]
103    pub timeout: i32,
104}
105
106impl Default for WaitFreezes {
107    fn default() -> Self {
108        Self {
109            time: default_wait_time(),
110            target: Target::default(),
111            target_offset: (0, 0, 0, 0),
112            threshold: default_wait_threshold(),
113            method: default_wait_method(),
114            rate_limit: default_rate_limit(),
115            timeout: default_timeout(),
116        }
117    }
118}
119
120// --- Recognition Enums ---
121
122/// Recognition algorithm types.
123///
124/// Determines how the framework identifies targets on screen:
125/// - [`DirectHit`] - No recognition, always matches
126/// - [`TemplateMatch`] - Image template matching
127/// - [`FeatureMatch`] - Feature-based matching (rotation/scale invariant)
128/// - [`ColorMatch`] - Color-based matching
129/// - [`OCR`] - Optical character recognition
130/// - [`NeuralNetworkClassify`] - Deep learning classification
131/// - [`NeuralNetworkDetect`] - Deep learning object detection
132/// - [`And`] - Logical AND of multiple recognitions
133/// - [`Or`] - Logical OR of multiple recognitions
134/// - `Custom` - User-defined recognition
135#[derive(Serialize, Deserialize, Debug, Clone)]
136#[serde(tag = "type", content = "param")]
137pub enum Recognition {
138    DirectHit(DirectHit),
139    TemplateMatch(TemplateMatch),
140    FeatureMatch(FeatureMatch),
141    ColorMatch(ColorMatch),
142    OCR(OCR),
143    NeuralNetworkClassify(NeuralNetworkClassify),
144    NeuralNetworkDetect(NeuralNetworkDetect),
145    And(And),
146    Or(Or),
147    Custom(CustomRecognition),
148}
149
150// --- Specific Recognition Structs ---
151
152/// Direct hit recognition - always matches without performing actual recognition.
153///
154/// Use when you want to execute an action without image matching.
155#[derive(Serialize, Deserialize, Debug, Clone, Default)]
156pub struct DirectHit {
157    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
158    #[serde(default = "default_roi_zero")]
159    pub roi: Target,
160    /// Offset applied to the ROI.
161    #[serde(default)]
162    pub roi_offset: Rect,
163}
164
165/// Template matching recognition - finds images using OpenCV template matching.
166///
167/// The most common recognition method for "finding images" on screen.
168#[derive(Serialize, Deserialize, Debug, Clone)]
169pub struct TemplateMatch {
170    /// Template image paths relative to `image` folder. Required.
171    #[serde(deserialize_with = "scalar_or_vec")]
172    pub template: Vec<String>,
173    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
174    #[serde(default = "default_roi_zero")]
175    pub roi: Target,
176    /// Offset applied to the ROI.
177    #[serde(default)]
178    pub roi_offset: Rect,
179    /// Matching threshold(s). Default: [0.7].
180    #[serde(default = "default_threshold", deserialize_with = "scalar_or_vec")]
181    pub threshold: Vec<f64>,
182    /// Result sorting: "Horizontal", "Vertical", "Score", "Random". Default: "Horizontal".
183    #[serde(default = "default_order_by")]
184    pub order_by: String,
185    /// Which result to select (0-indexed, negative for reverse). Default: 0.
186    #[serde(default)]
187    pub index: i32,
188    /// OpenCV matching method (cv::TemplateMatchModes). Default: 5 (TM_CCOEFF_NORMED).
189    #[serde(default = "default_template_method")]
190    pub method: i32,
191    /// Use green (0,255,0) as mask color. Default: false.
192    #[serde(default)]
193    pub green_mask: bool,
194}
195
196/// Feature-based matching - scale and rotation invariant image matching.
197///
198/// More robust than template matching for detecting objects under transformation.
199#[derive(Serialize, Deserialize, Debug, Clone)]
200pub struct FeatureMatch {
201    /// Template image paths relative to `image` folder. Required.
202    #[serde(deserialize_with = "scalar_or_vec")]
203    pub template: Vec<String>,
204    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
205    #[serde(default = "default_roi_zero")]
206    pub roi: Target,
207    /// Offset applied to the ROI.
208    #[serde(default)]
209    pub roi_offset: Rect,
210    /// Feature detector: "SIFT", "KAZE", "AKAZE", "BRISK", "ORB". Default: "SIFT".
211    #[serde(default = "default_detector")]
212    pub detector: String,
213    /// Result sorting method. Default: "Horizontal".
214    #[serde(default = "default_order_by")]
215    pub order_by: String,
216    /// Minimum feature point matches required. Default: 4.
217    #[serde(default = "default_feature_count")]
218    pub count: i32,
219    /// Which result to select. Default: 0.
220    #[serde(default)]
221    pub index: i32,
222    /// Use green (0,255,0) as mask color. Default: false.
223    #[serde(default)]
224    pub green_mask: bool,
225    /// KNN distance ratio threshold [0-1.0]. Default: 0.6.
226    #[serde(default = "default_feature_ratio")]
227    pub ratio: f64,
228}
229
230/// Color matching recognition - finds regions by color range.
231///
232/// Matches pixels within specified color bounds.
233#[derive(Serialize, Deserialize, Debug, Clone)]
234pub struct ColorMatch {
235    /// Lower color bounds. Required. Format depends on method.
236    #[serde(deserialize_with = "scalar_or_vec")]
237    pub lower: Vec<Vec<i32>>,
238    /// Upper color bounds. Required. Format depends on method.
239    #[serde(deserialize_with = "scalar_or_vec")]
240    pub upper: Vec<Vec<i32>>,
241    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
242    #[serde(default = "default_roi_zero")]
243    pub roi: Target,
244    /// Offset applied to the ROI.
245    #[serde(default)]
246    pub roi_offset: Rect,
247    /// Result sorting method. Default: "Horizontal".
248    #[serde(default = "default_order_by")]
249    pub order_by: String,
250    /// Color conversion code (cv::ColorConversionCodes). Default: 4 (RGB).
251    #[serde(default = "default_color_method")]
252    pub method: i32,
253    /// Minimum matching pixel count. Default: 1.
254    #[serde(default = "default_count_one")]
255    pub count: i32,
256    /// Which result to select. Default: 0.
257    #[serde(default)]
258    pub index: i32,
259    /// Only count connected pixels. Default: false.
260    #[serde(default)]
261    pub connected: bool,
262}
263
264/// Optical character recognition - finds and reads text.
265///
266/// Uses OCR model to detect and recognize text in the specified region.
267#[derive(Serialize, Deserialize, Debug, Clone, Default)]
268pub struct OCR {
269    /// Expected text patterns (supports regex). Default: match all.
270    #[serde(default, deserialize_with = "scalar_or_vec")]
271    pub expected: Vec<String>,
272    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
273    #[serde(default = "default_roi_zero")]
274    pub roi: Target,
275    /// Offset applied to the ROI.
276    #[serde(default)]
277    pub roi_offset: Rect,
278    /// Model confidence threshold. Default: 0.3.
279    #[serde(default = "default_ocr_threshold")]
280    pub threshold: f64,
281    /// Text replacement pairs [[from, to], ...] for fixing OCR errors.
282    #[serde(default)]
283    pub replace: Vec<Vec<String>>,
284    /// Result sorting method. Default: "Horizontal".
285    #[serde(default = "default_order_by")]
286    pub order_by: String,
287    /// Which result to select. Default: 0.
288    #[serde(default)]
289    pub index: i32,
290    /// Recognition only (skip detection, requires precise ROI). Default: false.
291    #[serde(default)]
292    pub only_rec: bool,
293    /// Model folder path relative to `model/ocr`. Default: root.
294    #[serde(default)]
295    pub model: String,
296}
297
298/// Neural network classification - classifies fixed regions.
299///
300/// Uses ONNX model to classify images at fixed positions.
301#[derive(Serialize, Deserialize, Debug, Clone)]
302pub struct NeuralNetworkClassify {
303    /// Model file path relative to `model/classify`. Required.
304    pub model: String,
305    /// Expected class indices to match. Default: match all.
306    #[serde(default, deserialize_with = "scalar_or_vec")]
307    pub expected: Vec<i32>,
308    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
309    #[serde(default = "default_roi_zero")]
310    pub roi: Target,
311    /// Offset applied to the ROI.
312    #[serde(default)]
313    pub roi_offset: Rect,
314    /// Class labels for debugging. Default: "Unknown".
315    #[serde(default)]
316    pub labels: Vec<String>,
317    /// Result sorting method. Default: "Horizontal".
318    #[serde(default = "default_order_by")]
319    pub order_by: String,
320    /// Which result to select. Default: 0.
321    #[serde(default)]
322    pub index: i32,
323}
324
325/// Neural network detection - detects objects anywhere on screen.
326///
327/// Uses YOLO-style ONNX model to detect and locate objects.
328#[derive(Serialize, Deserialize, Debug, Clone)]
329pub struct NeuralNetworkDetect {
330    /// Model file path relative to `model/detect`. Required.
331    pub model: String,
332    /// Expected class indices to match. Default: match all.
333    #[serde(default, deserialize_with = "scalar_or_vec")]
334    pub expected: Vec<i32>,
335    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
336    #[serde(default = "default_roi_zero")]
337    pub roi: Target,
338    /// Offset applied to the ROI.
339    #[serde(default)]
340    pub roi_offset: Rect,
341    /// Class labels (auto-read from model metadata). Default: "Unknown".
342    #[serde(default)]
343    pub labels: Vec<String>,
344    /// Confidence threshold(s). Default: [0.3].
345    #[serde(
346        default = "default_detect_threshold",
347        deserialize_with = "scalar_or_vec"
348    )]
349    pub threshold: Vec<f64>,
350    /// Result sorting method. Default: "Horizontal".
351    #[serde(default = "default_order_by")]
352    pub order_by: String,
353    /// Which result to select. Default: 0.
354    #[serde(default)]
355    pub index: i32,
356}
357
358/// Custom recognition - uses user-registered recognition handler.
359///
360/// Invokes a handler registered via `MaaResourceRegisterCustomRecognition`.
361#[derive(Serialize, Deserialize, Debug, Clone)]
362pub struct CustomRecognition {
363    /// Handler name (as registered). Required.
364    pub custom_recognition: String,
365    /// Recognition region. Default: \\[0,0,0,0\\] (full screen).
366    #[serde(default = "default_roi_zero")]
367    pub roi: Target,
368    /// Offset applied to the ROI.
369    #[serde(default)]
370    pub roi_offset: Rect,
371    /// Custom parameters passed to the handler.
372    #[serde(default)]
373    pub custom_recognition_param: Value,
374}
375
376/// Logical AND recognition - all sub-recognitions must match.
377///
378/// Combines multiple recognitions; succeeds only when all match.
379#[derive(Serialize, Deserialize, Debug, Clone, Default)]
380pub struct And {
381    /// Sub-recognition list. All must match. Required.
382    #[serde(default)]
383    pub all_of: Vec<Recognition>,
384    /// Which sub-recognition's bounding box to use. Default: 0.
385    #[serde(default)]
386    pub box_index: i32,
387}
388
389/// Logical OR recognition - first matching sub-recognition wins.
390///
391/// Combines multiple recognitions; succeeds when any one matches.
392#[derive(Serialize, Deserialize, Debug, Clone, Default)]
393pub struct Or {
394    /// Sub-recognition list. First match wins. Required.
395    #[serde(default)]
396    pub any_of: Vec<Recognition>,
397}
398
399// --- Action Enums ---
400
401/// Action types executed after successful recognition.
402///
403/// - [`DoNothing`] - No action
404/// - [`Click`] - Tap/click
405/// - [`LongPress`] - Long press
406/// - [`Swipe`] - Linear swipe
407/// - [`MultiSwipe`] - Multi-touch swipe
408/// - Touch actions: `TouchDown`, `TouchMove`, [`TouchUp`]
409/// - Key actions: `ClickKey`, [`LongPressKey`], `KeyDown`, `KeyUp`
410/// - [`InputText`] - Text input
411/// - App control: `StartApp`, `StopApp`
412/// - [`StopTask`] - Stop current task
413/// - [`Scroll`] - Mouse wheel scroll
414/// - [`Command`] - Execute local command
415/// - [`Shell`] - Execute ADB shell command
416/// - `Custom` - User-defined action
417#[derive(Serialize, Deserialize, Debug, Clone)]
418#[serde(tag = "type", content = "param")]
419pub enum Action {
420    DoNothing(DoNothing),
421    Click(Click),
422    LongPress(LongPress),
423    Swipe(Swipe),
424    MultiSwipe(MultiSwipe),
425    TouchDown(Touch),
426    TouchMove(Touch),
427    TouchUp(TouchUp),
428    ClickKey(KeyList),
429    LongPressKey(LongPressKey),
430    KeyDown(SingleKey),
431    KeyUp(SingleKey),
432    InputText(InputText),
433    StartApp(App),
434    StopApp(App),
435    StopTask(StopTask),
436    Scroll(Scroll),
437    Command(Command),
438    Shell(Shell),
439    Custom(CustomAction),
440}
441
442// --- Action Structs ---
443
444/// Do nothing action.
445#[derive(Serialize, Deserialize, Debug, Clone, Default)]
446pub struct DoNothing {}
447
448/// Stop current task chain action.
449#[derive(Serialize, Deserialize, Debug, Clone, Default)]
450pub struct StopTask {}
451
452/// Click/tap action.
453///
454/// Performs a single tap at the target position.
455#[derive(Serialize, Deserialize, Debug, Clone, Default)]
456pub struct Click {
457    /// Click target position. Default: recognized position.
458    #[serde(default)]
459    pub target: Target,
460    /// Offset applied to target.
461    #[serde(default)]
462    pub target_offset: Rect,
463    /// Touch contact/button index. Default: 0.
464    #[serde(default)]
465    pub contact: i32,
466    /// Touch pressure. Default: 1.
467    #[serde(default = "default_pressure")]
468    pub pressure: i32,
469}
470
471/// Long press action.
472///
473/// Performs a sustained press at the target position.
474#[derive(Serialize, Deserialize, Debug, Clone)]
475pub struct LongPress {
476    /// Press target position. Default: recognized position.
477    #[serde(default)]
478    pub target: Target,
479    /// Offset applied to target.
480    #[serde(default)]
481    pub target_offset: Rect,
482    /// Press duration in milliseconds. Default: 1000.
483    #[serde(default = "default_long_press_duration")]
484    pub duration: i32,
485    /// Touch contact/button index. Default: 0.
486    #[serde(default)]
487    pub contact: i32,
488    /// Touch pressure. Default: 1.
489    #[serde(default = "default_pressure")]
490    pub pressure: i32,
491}
492
493/// Linear swipe action.
494///
495/// Swipes from begin to end position(s). Supports waypoints.
496#[derive(Serialize, Deserialize, Debug, Clone)]
497pub struct Swipe {
498    /// Start time offset in ms (for MultiSwipe). Default: 0.
499    #[serde(default)]
500    pub starting: i32,
501    /// Swipe start position. Default: recognized position.
502    #[serde(default)]
503    pub begin: Target,
504    /// Offset applied to begin.
505    #[serde(default)]
506    pub begin_offset: Rect,
507    /// Swipe end position(s). Supports waypoints. Default: recognized position.
508    #[serde(
509        default = "default_target_list_true",
510        deserialize_with = "scalar_or_vec"
511    )]
512    pub end: Vec<Target>,
513    /// Offset(s) applied to end.
514    #[serde(default = "default_rect_list_zero", deserialize_with = "scalar_or_vec")]
515    pub end_offset: Vec<Rect>,
516    /// Hold time at end position(s) in ms. Default: \\[0\\].
517    #[serde(default = "default_i32_list_zero", deserialize_with = "scalar_or_vec")]
518    pub end_hold: Vec<i32>,
519    /// Duration(s) in milliseconds. Default: \\[200\\].
520    #[serde(default = "default_duration_list", deserialize_with = "scalar_or_vec")]
521    pub duration: Vec<i32>,
522    /// Hover only (no press). Default: false.
523    #[serde(default)]
524    pub only_hover: bool,
525    /// Touch contact/button index. Default: 0.
526    #[serde(default)]
527    pub contact: i32,
528    /// Touch pressure. Default: 1.
529    #[serde(default = "default_pressure")]
530    pub pressure: i32,
531}
532
533/// Multi-finger swipe action.
534///
535/// Performs multiple simultaneous swipes (e.g., pinch gestures).
536#[derive(Serialize, Deserialize, Debug, Clone)]
537pub struct MultiSwipe {
538    /// List of swipe configurations.
539    #[serde(default)]
540    pub swipes: Vec<Swipe>,
541}
542
543/// Touch down/move action - initiates or moves a touch point.
544///
545/// Used for custom touch sequences. Pair with TouchUp to complete.
546#[derive(Serialize, Deserialize, Debug, Clone)]
547pub struct Touch {
548    /// Touch contact index. Default: 0.
549    #[serde(default)]
550    pub contact: i32,
551    /// Touch target position. Default: recognized position.
552    #[serde(default)]
553    pub target: Target,
554    /// Offset applied to target.
555    #[serde(default)]
556    pub target_offset: Rect,
557    /// Touch pressure. Default: 0.
558    #[serde(default)]
559    pub pressure: i32,
560}
561
562/// Touch up action - releases a touch point.
563#[derive(Serialize, Deserialize, Debug, Clone)]
564pub struct TouchUp {
565    /// Touch contact index to release. Default: 0.
566    #[serde(default)]
567    pub contact: i32,
568}
569
570/// Long press key action.
571#[derive(Serialize, Deserialize, Debug, Clone)]
572pub struct LongPressKey {
573    /// Virtual key code(s) to press. Required.
574    #[serde(deserialize_with = "scalar_or_vec")]
575    pub key: Vec<i32>,
576    /// Press duration in milliseconds. Default: 1000.
577    #[serde(default = "default_long_press_duration")]
578    pub duration: i32,
579}
580
581/// Click key action - single key press.
582#[derive(Serialize, Deserialize, Debug, Clone)]
583pub struct KeyList {
584    /// Virtual key code(s) to click. Required.
585    #[serde(deserialize_with = "scalar_or_vec")]
586    pub key: Vec<i32>,
587}
588
589/// Single key action - for KeyDown/KeyUp.
590#[derive(Serialize, Deserialize, Debug, Clone)]
591pub struct SingleKey {
592    /// Virtual key code. Required.
593    pub key: i32,
594}
595
596/// Text input action.
597#[derive(Serialize, Deserialize, Debug, Clone)]
598pub struct InputText {
599    /// Text to input (ASCII recommended). Required.
600    pub input_text: String,
601}
602
603/// App control action - for StartApp/StopApp.
604#[derive(Serialize, Deserialize, Debug, Clone)]
605pub struct App {
606    /// Package name or activity (e.g., "com.example.app"). Required.
607    pub package: String,
608}
609
610/// Mouse scroll action (Win32 only).
611#[derive(Serialize, Deserialize, Debug, Clone, Default)]
612pub struct Scroll {
613    /// Scroll target position. Default: recognized position.
614    #[serde(default)]
615    pub target: Target,
616    /// Offset applied to target.
617    #[serde(default)]
618    pub target_offset: Rect,
619    /// Horizontal scroll delta. Default: 0.
620    #[serde(default)]
621    pub dx: i32,
622    /// Vertical scroll delta. Default: 0.
623    #[serde(default)]
624    pub dy: i32,
625}
626
627/// Execute local command action.
628#[derive(Serialize, Deserialize, Debug, Clone)]
629pub struct Command {
630    /// Program path to execute. Required.
631    pub exec: String,
632    /// Command arguments. Supports runtime placeholders.
633    #[serde(default)]
634    pub args: Vec<String>,
635    /// Run in background (don't wait). Default: false.
636    #[serde(default)]
637    pub detach: bool,
638}
639
640/// Execute ADB shell command action.
641#[derive(Serialize, Deserialize, Debug, Clone)]
642pub struct Shell {
643    /// Shell command to execute. Required.
644    pub cmd: String,
645    /// Command timeout in milliseconds. Default: 20000.
646    #[serde(default = "default_timeout")]
647    pub timeout: i32,
648}
649
650/// Custom action - uses user-registered action handler.
651///
652/// Invokes a handler registered via `MaaResourceRegisterCustomAction`.
653#[derive(Serialize, Deserialize, Debug, Clone)]
654pub struct CustomAction {
655    /// Handler name (as registered). Required.
656    pub custom_action: String,
657    /// Target position passed to handler. Default: recognized position.
658    #[serde(default)]
659    pub target: Target,
660    /// Custom parameters passed to the handler.
661    #[serde(default)]
662    pub custom_action_param: Value,
663    /// Offset applied to target.
664    #[serde(default)]
665    pub target_offset: Rect,
666}
667
668// --- Pipeline Data ---
669
670/// Complete pipeline node configuration.
671///
672/// Defines a node's recognition, action, and flow control parameters.
673#[derive(Serialize, Deserialize, Debug, Clone)]
674pub struct PipelineData {
675    /// Recognition algorithm configuration.
676    pub recognition: Recognition,
677    /// Action to execute on match.
678    pub action: Action,
679    /// Next nodes to check after action. Default: [].
680    #[serde(default)]
681    pub next: Vec<NodeAttr>,
682    /// Recognition rate limit in ms. Default: 1000.
683    #[serde(default = "default_rate_limit")]
684    pub rate_limit: i32,
685    /// Overall timeout in ms. Default: 20000.
686    #[serde(default = "default_timeout")]
687    pub timeout: i32,
688    /// Nodes to check on timeout/error. Default: [].
689    #[serde(default)]
690    pub on_error: Vec<NodeAttr>,
691    /// Anchor names for this node. Default: [].
692    #[serde(default)]
693    pub anchor: Vec<String>,
694    /// Invert recognition result. Default: false.
695    #[serde(default)]
696    pub inverse: bool,
697    /// Enable this node. Default: true.
698    #[serde(default = "default_enabled")]
699    pub enabled: bool,
700    /// Delay before action in ms. Default: 200.
701    #[serde(default = "default_pre_delay")]
702    pub pre_delay: i32,
703    /// Delay after action in ms. Default: 200.
704    #[serde(default = "default_post_delay")]
705    pub post_delay: i32,
706    /// Wait for screen stability before action.
707    #[serde(default)]
708    pub pre_wait_freezes: Option<WaitFreezes>,
709    /// Wait for screen stability after action.
710    #[serde(default)]
711    pub post_wait_freezes: Option<WaitFreezes>,
712    /// Action repeat count. Default: 1.
713    #[serde(default = "default_repeat")]
714    pub repeat: i32,
715    /// Delay between repeats in ms. Default: 0.
716    #[serde(default)]
717    pub repeat_delay: i32,
718    /// Wait for stability between repeats.
719    #[serde(default)]
720    pub repeat_wait_freezes: Option<WaitFreezes>,
721    /// Maximum successful hits. Default: UINT_MAX.
722    #[serde(default = "default_max_hit")]
723    pub max_hit: u32,
724    /// Focus flag for extra callbacks. Default: null.
725    #[serde(default)]
726    pub focus: Option<Value>,
727    /// Attached custom data (merged with defaults).
728    #[serde(default)]
729    pub attach: Option<Value>,
730}
731
732// --- Defaults Helper Functions ---
733
734fn default_wait_time() -> i32 {
735    1
736}
737fn default_wait_threshold() -> f64 {
738    0.95
739}
740fn default_wait_method() -> i32 {
741    5
742}
743fn default_rate_limit() -> i32 {
744    1000
745}
746fn default_timeout() -> i32 {
747    20000
748}
749fn default_threshold() -> Vec<f64> {
750    vec![0.7]
751}
752fn default_order_by() -> String {
753    "Horizontal".to_string()
754}
755fn default_template_method() -> i32 {
756    5
757}
758fn default_detector() -> String {
759    "SIFT".to_string()
760}
761fn default_feature_count() -> i32 {
762    4
763}
764fn default_feature_ratio() -> f64 {
765    0.6
766}
767fn default_color_method() -> i32 {
768    4
769} // RGB
770fn default_count_one() -> i32 {
771    1
772}
773fn default_ocr_threshold() -> f64 {
774    0.3
775}
776fn default_detect_threshold() -> Vec<f64> {
777    vec![0.3]
778}
779fn default_pressure() -> i32 {
780    1
781}
782fn default_long_press_duration() -> i32 {
783    1000
784}
785fn default_target_list_true() -> Vec<Target> {
786    vec![Target::Bool(true)]
787}
788fn default_rect_list_zero() -> Vec<Rect> {
789    vec![(0, 0, 0, 0)]
790}
791fn default_i32_list_zero() -> Vec<i32> {
792    vec![0]
793}
794fn default_duration_list() -> Vec<i32> {
795    vec![200]
796}
797fn default_enabled() -> bool {
798    true
799}
800fn default_pre_delay() -> i32 {
801    200
802}
803fn default_post_delay() -> i32 {
804    200
805}
806fn default_repeat() -> i32 {
807    1
808}
809fn default_max_hit() -> u32 {
810    u32::MAX
811}
812fn default_roi_zero() -> Target {
813    Target::Rect((0, 0, 0, 0))
814}