Skip to main content

maa_framework/
job.rs

1//! Asynchronous job management for tracking operation status and results.
2
3use std::ops::Deref;
4
5use crate::{
6    common::{MaaId, MaaStatus},
7    sys, MaaResult,
8};
9
10pub type StatusFn<'a> = Box<dyn Fn(MaaId) -> MaaStatus + Send + Sync + 'a>;
11pub type WaitFn<'a> = Box<dyn Fn(MaaId) -> MaaStatus + Send + Sync + 'a>;
12
13/// An asynchronous operation handle.
14///
15/// Use this to track the status of controller, resource, and tasker operations.
16pub struct Job<'a> {
17    pub id: MaaId,
18    status_fn: StatusFn<'a>,
19    wait_fn: WaitFn<'a>,
20}
21
22impl<'a> Job<'a> {
23    /// Create a new Job with custom status/wait functions.
24    pub fn new(id: MaaId, status_fn: StatusFn<'a>, wait_fn: WaitFn<'a>) -> Self {
25        Self {
26            id,
27            status_fn,
28            wait_fn,
29        }
30    }
31
32    /// Create a Job for a Tasker operation.
33    pub fn for_tasker(tasker: &crate::tasker::Tasker, id: MaaId) -> Job<'static> {
34        let tasker1 = tasker.clone();
35        let tasker2 = tasker.clone();
36        Job {
37            id,
38            status_fn: Box::new(move |job_id| {
39                MaaStatus(unsafe { sys::MaaTaskerStatus(tasker1.raw(), job_id) })
40            }),
41            wait_fn: Box::new(move |job_id| {
42                MaaStatus(unsafe { sys::MaaTaskerWait(tasker2.raw(), job_id) })
43            }),
44        }
45    }
46
47    /// Create a Job for a Controller operation.
48    pub fn for_controller(controller: &crate::controller::Controller, id: MaaId) -> Job<'static> {
49        let controller1 = controller.clone();
50        let controller2 = controller.clone();
51        Job {
52            id,
53            status_fn: Box::new(move |job_id| {
54                MaaStatus(unsafe { sys::MaaControllerStatus(controller1.raw(), job_id) })
55            }),
56            wait_fn: Box::new(move |job_id| {
57                MaaStatus(unsafe { sys::MaaControllerWait(controller2.raw(), job_id) })
58            }),
59        }
60    }
61
62    /// Create a Job for a Resource operation.
63    pub fn for_resource(resource: &crate::resource::Resource, id: MaaId) -> Job<'static> {
64        let resource1 = resource.clone();
65        let resource2 = resource.clone();
66        Job {
67            id,
68            status_fn: Box::new(move |job_id| {
69                MaaStatus(unsafe { sys::MaaResourceStatus(resource1.raw(), job_id) })
70            }),
71            wait_fn: Box::new(move |job_id| {
72                MaaStatus(unsafe { sys::MaaResourceWait(resource2.raw(), job_id) })
73            }),
74        }
75    }
76
77    /// Block until the operation completes.
78    pub fn wait(&self) -> MaaStatus {
79        (self.wait_fn)(self.id)
80    }
81
82    /// Get the current status without blocking.
83    pub fn status(&self) -> MaaStatus {
84        (self.status_fn)(self.id)
85    }
86
87    /// Returns `true` if the operation succeeded.
88    pub fn succeeded(&self) -> bool {
89        self.status() == MaaStatus::SUCCEEDED
90    }
91
92    /// Returns `true` if the operation failed.
93    pub fn failed(&self) -> bool {
94        self.status() == MaaStatus::FAILED
95    }
96
97    /// Returns `true` if the operation is running.
98    pub fn running(&self) -> bool {
99        self.status() == MaaStatus::RUNNING
100    }
101
102    /// Returns `true` if the operation is pending.
103    pub fn pending(&self) -> bool {
104        self.status() == MaaStatus::PENDING
105    }
106
107    /// Returns `true` if the operation has completed (success or failure).
108    pub fn done(&self) -> bool {
109        let s = self.status();
110        s == MaaStatus::SUCCEEDED || s == MaaStatus::FAILED
111    }
112}
113
114/// An asynchronous operation handle with typed result retrieval.
115///
116/// Similar to [`Job`] but includes a `get()` method to retrieve the operation result.
117pub struct JobWithResult<'a, T> {
118    job: Job<'a>,
119    get_fn: Box<dyn Fn(MaaId) -> MaaResult<Option<T>> + Send + Sync + 'a>,
120}
121
122impl<'a, T> Deref for JobWithResult<'a, T> {
123    type Target = Job<'a>;
124
125    fn deref(&self) -> &Self::Target {
126        &self.job
127    }
128}
129
130impl<'a, T> JobWithResult<'a, T> {
131    /// Create a new JobWithResult with custom status/wait/get functions.
132    pub fn new(
133        id: MaaId,
134        status_fn: StatusFn<'a>,
135        wait_fn: WaitFn<'a>,
136        get_fn: impl Fn(MaaId) -> MaaResult<Option<T>> + Send + Sync + 'a,
137    ) -> Self {
138        Self {
139            job: Job::new(id, status_fn, wait_fn),
140            get_fn: Box::new(get_fn),
141        }
142    }
143
144    /// Get the operation result.
145    ///
146    /// # Arguments
147    /// * `wait` - If `true`, blocks until the operation completes before getting the result
148    pub fn get(&self, wait: bool) -> MaaResult<Option<T>> {
149        if wait {
150            self.wait();
151        }
152        (self.get_fn)(self.job.id)
153    }
154}
155
156pub type OverridePipelineFn<'a> = Box<dyn Fn(MaaId, &str) -> MaaResult<bool> + Send + Sync + 'a>;
157
158/// Task job handle with extended capabilities.
159///
160/// Inherits from [`JobWithResult`], additionally providing task-specific operations.
161pub struct TaskJob<'a, T> {
162    job: JobWithResult<'a, T>,
163    override_fn: OverridePipelineFn<'a>,
164}
165
166impl<'a, T> Deref for TaskJob<'a, T> {
167    type Target = JobWithResult<'a, T>;
168
169    fn deref(&self) -> &Self::Target {
170        &self.job
171    }
172}
173
174impl<'a, T> TaskJob<'a, T> {
175    /// Create a new TaskJob.
176    pub fn new(job: JobWithResult<'a, T>, override_fn: OverridePipelineFn<'a>) -> Self {
177        Self { job, override_fn }
178    }
179
180    /// Override the pipeline for this task.
181    ///
182    /// Dynamically modifies the pipeline configuration during task execution.
183    ///
184    /// # Arguments
185    /// * `pipeline_override` - The JSON string for overriding.
186    ///
187    /// # Returns
188    /// * `true` if successful.
189    pub fn override_pipeline(&self, pipeline_override: &str) -> MaaResult<bool> {
190        (self.override_fn)(self.job.id, pipeline_override)
191    }
192}
193
194// === Type Aliases ===
195
196/// Controller operation job.
197///
198/// Returned by controller methods like `post_click()`, `post_swipe()`.
199pub type CtrlJob = Job<'static>;
200
201/// Resource loading job.
202///
203/// Returned by resource methods like `post_bundle()`.
204pub type ResJob = Job<'static>;
205
206/// Task job with result retrieval.
207///
208/// Returned by `Tasker::post_task()`.
209pub type TaskJobWithResult = TaskJob<'static, crate::common::TaskDetail>;
210
211/// Recognition job with result retrieval.
212///
213/// Returned by `Tasker::post_recognition()`.
214pub type RecoJobWithResult = TaskJob<'static, crate::common::RecognitionDetail>;
215
216/// Action job with result retrieval.
217///
218/// Returned by `Tasker::post_action()`.
219pub type ActionJobWithResult = TaskJob<'static, crate::common::ActionDetail>;