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#[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 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}