maa_framework/
instance.rs

1use std::{
2    collections::HashMap,
3    ffi::{c_void, CString},
4    fmt::Display,
5    ops::Deref,
6    ptr::null_mut,
7};
8
9use serde::{Deserialize, Serialize};
10#[cfg(feature = "tokio")]
11use tokio::task::JoinError;
12
13pub use internal::MaaTaskId;
14
15use crate::{
16    CallbackHandler,
17    controller::MaaControllerInstance,
18    error,
19    internal,
20    maa_bool,
21    MaaResult, MaaStatus, resource::MaaResourceInstance,
22};
23#[cfg(feature = "custom_action")]
24use crate::custom::custom_action::{
25    maa_custom_action_run, maa_custom_action_stop, MaaCustomAction,
26};
27#[cfg(feature = "custom_recognizer")]
28use crate::custom::custom_recognizer::{custom_recognier_analyze, MaaCustomRecognizer};
29
30pub trait TaskParam: Serialize {
31    fn get_param(&self) -> String {
32        serde_json::to_string(self).unwrap()
33    }
34}
35
36impl TaskParam for serde_json::Value {
37    fn get_param(&self) -> String {
38        self.to_string()
39    }
40}
41
42#[derive(Debug, Serialize, Deserialize)]
43pub enum MaaInstOption {
44    Invalid,
45}
46
47impl Display for MaaInstOption {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            MaaInstOption::Invalid => write!(f, "Invalid"),
51        }
52    }
53}
54
55impl MaaInstOption {
56    fn get_inner_key(&self) -> internal::MaaInstOption {
57        match self {
58            MaaInstOption::Invalid => internal::MaaInstOptionEnum_MaaInstOption_Invalid,
59        }
60    }
61}
62
63pub struct MaaTaskResult<'a, T> {
64    pub task_id: MaaTaskId,
65    pub instance: &'a MaaInstance<T>,
66}
67
68impl<'a, T> MaaTaskResult<'a, T> {
69    pub fn status(&self) -> MaaResult<MaaStatus> {
70        self.instance.task_status(self.task_id)
71    }
72
73    pub fn wait(&self) -> MaaResult<MaaStatus> {
74        self.instance.wait_task(self.task_id)
75    }
76
77    #[cfg(feature = "tokio")]
78    #[doc(cfg(feature = "tokio"))]
79    pub async fn wait_async(&self) -> Result<MaaResult<MaaStatus>, JoinError> {
80        tokio::spawn(async move {
81            self.instance.wait_task(self.task_id)
82        }).await
83    }
84
85    pub fn set_task_param(&self, param: &str) -> MaaResult<()> {
86        self.instance.set_task_param(self.task_id, param)
87    }
88}
89
90/// The MaaInstance struct is the main entry point for the Maa library.
91///
92/// It is used to create and manage the Maa instance for running tasks.
93///
94/// # Example
95///
96/// ```
97/// use maa_framework::instance::MaaInstance;
98///
99/// let instance = MaaInstance::new(None);
100/// // let param = serde_json::json!({"param": "value"});
101/// // instance.post_task("task", param).await;
102/// ```
103///
104/// # Note
105///
106/// [MaaInstance], [MaaResourceInstance] and [MaaControllerInstance] use the same mechanism to manage the lifetime of the underlying C++ object.
107/// That is, if the object is created from the Rust code (like `MaaInstance::new`), the object will be destroyed when it goes out of scope. In this case, it is your responsibility to ensure that the object is not used after it has been destroyed.
108/// If the object is created from the C++ code, then you will not have to worry about the lifetime of the object.
109#[derive(Debug)]
110pub struct MaaInstance<T> {
111    pub(crate) handle: internal::MaaInstanceHandle,
112    registered_custom_recognizers: HashMap<String, (*mut c_void, *mut c_void)>,
113    registered_custom_actions: HashMap<String, (*mut c_void, *mut c_void)>,
114    _phantom: std::marker::PhantomData<T>,
115}
116
117impl<T> Deref for MaaInstance<T> {
118    type Target = internal::MaaInstanceHandle;
119
120    fn deref(&self) -> &Self::Target {
121        &self.handle
122    }
123}
124
125unsafe impl<T> Send for MaaInstance<T> {}
126unsafe impl<T> Sync for MaaInstance<T> {}
127
128impl<T> MaaInstance<T> {
129    /// Create a new MaaInstance.
130    ///
131    /// # Parameters
132    ///
133    /// * `handler` - An optional callback handler for handling Maa events.
134    pub fn new(handler: Option<T>) -> Self
135    where
136        T: CallbackHandler,
137    {
138        let handle = unsafe {
139            match handler {
140                Some(handler) => {
141                    let callback_arg = Box::into_raw(Box::new(handler)) as *mut c_void;
142                    internal::MaaCreate(Some(internal::callback_handler::<T>), callback_arg)
143                }
144                None => internal::MaaCreate(None, null_mut()),
145            }
146        };
147
148        MaaInstance {
149            handle,
150            registered_custom_recognizers: HashMap::new(),
151            registered_custom_actions: HashMap::new(),
152            _phantom: std::marker::PhantomData,
153        }
154    }
155
156    pub fn set_option(&self, option: MaaInstOption) -> MaaResult<()> {
157        let key = option.get_inner_key();
158
159        let status = unsafe {
160            match option {
161                MaaInstOption::Invalid => internal::MaaSetOption(self.handle, key, null_mut(), 0),
162            }
163        };
164
165        if status != 0 {
166            Err(error::Error::MaaInstanceSetOptionError(option))
167        } else {
168            Ok(())
169        }
170    }
171
172    pub fn bind_resource(&self, res: &MaaResourceInstance<T>) -> MaaResult<()> {
173        let ret = unsafe { internal::MaaBindResource(self.handle, res.handle) };
174
175        if maa_bool!(ret) {
176            Ok(())
177        } else {
178            Err(error::Error::MaaInstanceBindResourceError)
179        }
180    }
181
182    pub fn bind_controller(&self, controller: &MaaControllerInstance<T>) -> MaaResult<()> {
183        let ret = unsafe { internal::MaaBindController(self.handle, controller.handle) };
184
185        if maa_bool!(ret) {
186            Ok(())
187        } else {
188            Err(error::Error::MaaInstanceBindControllerError)
189        }
190    }
191
192    pub fn inited(&self) -> bool {
193        let ret = unsafe { internal::MaaInited(self.handle) };
194        maa_bool!(ret)
195    }
196
197    pub fn post_task<P>(&self, entry: &str, param: P) -> MaaTaskResult<T>
198    where
199        P: TaskParam,
200    {
201        let entry = CString::new(entry).unwrap();
202        let param = param.get_param();
203        let param = CString::new(param).unwrap();
204        let task_id = unsafe { internal::MaaPostTask(self.handle, entry.as_ptr(), param.as_ptr()) };
205        MaaTaskResult {
206            task_id,
207            instance: self,
208        }
209    }
210
211    pub fn post_recognition(&self, entry: &str, param: &str) -> MaaTaskResult<T> {
212        let entry = CString::new(entry).unwrap();
213        let param = CString::new(param).unwrap();
214        let task_id = unsafe { internal::MaaPostRecognition(self.handle, entry.as_ptr(), param.as_ptr()) };
215        MaaTaskResult {
216            task_id,
217            instance: self,
218        }
219    }
220
221    pub fn post_action(&self, entry: &str, param: &str) -> MaaTaskResult<T> {
222        let entry = CString::new(entry).unwrap();
223        let param = CString::new(param).unwrap();
224        let task_id = unsafe { internal::MaaPostAction(self.handle, entry.as_ptr(), param.as_ptr()) };
225        MaaTaskResult {
226            task_id,
227            instance: self,
228        }
229    }
230
231    fn set_task_param(&self, task_id: MaaTaskId, param: &str) -> MaaResult<()> {
232        let param = internal::to_cstring(param);
233        let ret = unsafe { internal::MaaSetTaskParam(self.handle, task_id, param) };
234
235        if maa_bool!(ret) {
236            Ok(())
237        } else {
238            Err(error::Error::MaaInstanceSetTaskParamError(task_id))
239        }
240    }
241
242    fn task_status(&self, task_id: MaaTaskId) -> MaaResult<MaaStatus> {
243        let status = unsafe { internal::MaaTaskStatus(self.handle, task_id) };
244
245        MaaStatus::try_from(status)
246    }
247
248    fn wait_task(&self, task_id: MaaTaskId) -> MaaResult<MaaStatus> {
249        let status = unsafe { internal::MaaWaitTask(self.handle, task_id) };
250
251        MaaStatus::try_from(status)
252    }
253
254    #[deprecated(note = "Use `running` instead")]
255    pub fn task_all_finished(&self) -> bool {
256        let ret = unsafe { internal::MaaTaskAllFinished(self.handle) };
257        maa_bool!(ret)
258    }
259
260    pub fn running(&self) -> bool {
261        let ret = unsafe { internal::MaaRunning(self.handle) };
262        maa_bool!(ret)
263    }
264
265    pub fn post_stop(&self) -> MaaResult<()> {
266        let ret = unsafe { internal::MaaPostStop(self.handle) };
267
268        if maa_bool!(ret) {
269            Ok(())
270        } else {
271            Err(error::Error::MaaInstanceStopError)
272        }
273    }
274
275    pub fn stop(&self) -> MaaResult<()> {
276        let ret = unsafe { internal::MaaStop(self.handle) };
277
278        if maa_bool!(ret) {
279            Ok(())
280        } else {
281            Err(error::Error::MaaInstanceStopError)
282        }
283    }
284
285    pub fn resource(&self) -> MaaResourceInstance<T> {
286        let handle = unsafe { internal::MaaGetResource(self.handle) };
287        MaaResourceInstance::new_from_handle(handle)
288    }
289
290    pub fn controller(&self) -> MaaControllerInstance<T> {
291        let handle = unsafe { internal::MaaGetController(self.handle) };
292        MaaControllerInstance::new_from_handle(handle)
293    }
294
295    #[cfg(feature = "custom_recognizer")]
296    #[doc(cfg(feature = "custom_recognizer"))]
297    pub fn register_custom_recognizer<R>(&mut self, name: &str, recognizer: R) -> MaaResult<()>
298    where
299        R: MaaCustomRecognizer,
300    {
301        let name_str = internal::to_cstring(name);
302        let recognizer = Box::new(recognizer);
303        let recognizer = Box::into_raw(recognizer) as *mut c_void;
304
305        let recognizer_api = internal::MaaCustomRecognizerAPI {
306            analyze: Some(custom_recognier_analyze::<R>),
307        };
308
309        let recognizer_api = Box::new(recognizer_api);
310        let recognizer_api = Box::into_raw(recognizer_api) as *mut c_void;
311
312        self.registered_custom_recognizers
313            .insert(name.to_owned(), (recognizer, recognizer_api));
314
315        let ret = unsafe {
316            internal::MaaRegisterCustomRecognizer(
317                self.handle,
318                name_str,
319                recognizer_api.cast(),
320                recognizer,
321            )
322        };
323
324        if maa_bool!(ret) {
325            Ok(())
326        } else {
327            Err(error::Error::MaaInstanceRegisterCustomRecognizerError(
328                name.to_owned(),
329            ))
330        }
331    }
332
333    #[cfg(feature = "custom_recognizer")]
334    #[doc(cfg(feature = "custom_recognizer"))]
335    pub fn unregister_custom_recognizer(&mut self, name: &str) -> MaaResult<()> {
336        let name_str = internal::to_cstring(name);
337
338        let (recognizer, recognizer_api) = self.registered_custom_recognizers.remove(name).unwrap();
339
340        let ret = unsafe { internal::MaaUnregisterCustomRecognizer(self.handle, name_str) };
341
342        unsafe {
343            drop(Box::from_raw(
344                recognizer as *mut Box<dyn MaaCustomRecognizer>,
345            ));
346            drop(Box::from_raw(
347                recognizer_api as *mut internal::MaaCustomRecognizerAPI,
348            ));
349        }
350
351        if maa_bool!(ret) {
352            Ok(())
353        } else {
354            Err(error::Error::MaaInstanceUnregisterCustomRecognizerError(
355                name.to_owned(),
356            ))
357        }
358    }
359
360    #[cfg(feature = "custom_recognizer")]
361    #[doc(cfg(feature = "custom_recognizer"))]
362    pub fn clear_custom_recognizers(&mut self) -> MaaResult<()> {
363        let ret = unsafe { internal::MaaClearCustomRecognizer(self.handle) };
364
365        if !maa_bool!(ret) {
366            return Err(error::Error::MaaInstanceClearCustomRecognizerError);
367        }
368
369        for (_name, (recognizer, recognizer_api)) in self.registered_custom_recognizers.drain() {
370            unsafe {
371                drop(Box::from_raw(
372                    recognizer as *mut Box<dyn MaaCustomRecognizer>,
373                ));
374                drop(Box::from_raw(
375                    recognizer_api as *mut internal::MaaCustomRecognizerAPI,
376                ));
377            }
378        }
379
380        Ok(())
381    }
382
383    #[cfg(feature = "custom_action")]
384    #[doc(cfg(feature = "custom_action"))]
385    pub fn register_custom_action<A>(&mut self, name: &str, action: A) -> MaaResult<()>
386    where
387        A: MaaCustomAction,
388    {
389        let name_str = internal::to_cstring(name);
390        let action = Box::new(action);
391        let action = Box::into_raw(action) as *mut c_void;
392
393        let action_api = internal::MaaCustomActionAPI {
394            run: Some(maa_custom_action_run::<A>),
395            stop: Some(maa_custom_action_stop::<A>),
396        };
397
398        let action_api = Box::new(action_api);
399        let action_api = Box::into_raw(action_api) as *mut c_void;
400
401        self.registered_custom_actions
402            .insert(name.to_owned(), (action, action_api));
403
404        let ret = unsafe {
405            internal::MaaRegisterCustomAction(self.handle, name_str, action_api.cast(), action)
406        };
407
408        if maa_bool!(ret) {
409            Ok(())
410        } else {
411            Err(error::Error::MaaInstanceRegisterCustomActionError(
412                name.to_owned(),
413            ))
414        }
415    }
416
417    #[cfg(feature = "custom_action")]
418    #[doc(cfg(feature = "custom_action"))]
419    pub fn unregister_custom_action(&mut self, name: &str) -> MaaResult<()> {
420        let name_str = internal::to_cstring(name);
421
422        let (action, action_api) = self.registered_custom_actions.remove(name).unwrap();
423
424        let ret = unsafe { internal::MaaUnregisterCustomAction(self.handle, name_str) };
425
426        unsafe {
427            drop(Box::from_raw(action as *mut Box<dyn MaaCustomAction>));
428            drop(Box::from_raw(
429                action_api as *mut internal::MaaCustomActionAPI,
430            ));
431        }
432
433        if maa_bool!(ret) {
434            Ok(())
435        } else {
436            Err(error::Error::MaaInstanceUnregisterCustomActionError(
437                name.to_owned(),
438            ))
439        }
440    }
441
442    #[cfg(feature = "custom_action")]
443    #[doc(cfg(feature = "custom_action"))]
444    pub fn clear_custom_actions(&mut self) -> MaaResult<()> {
445        let ret = unsafe { internal::MaaClearCustomAction(self.handle) };
446
447        if !maa_bool!(ret) {
448            return Err(error::Error::MaaInstanceClearCustomActionError);
449        }
450
451        for (_name, (action, action_api)) in self.registered_custom_actions.drain() {
452            unsafe {
453                drop(Box::from_raw(action as *mut Box<dyn MaaCustomAction>));
454                drop(Box::from_raw(
455                    action_api as *mut internal::MaaCustomActionAPI,
456                ));
457            }
458        }
459
460        Ok(())
461    }
462}
463
464impl<T> Drop for MaaInstance<T> {
465    fn drop(&mut self) {
466        unsafe {
467            internal::MaaDestroy(self.handle);
468        }
469    }
470}