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