aidoku/macros/
mod.rs

1/// Prints to Aidoku logs.
2///
3/// # Examples
4///
5/// ```ignore
6/// println!(); // prints just a newline
7/// println!("hello there!");
8/// println!("format {} arguments", "some");
9/// let local_variable = "some";
10/// println!("format {local_variable} arguments");
11/// ```
12#[macro_export]
13macro_rules! println {
14	() => {
15		$crate::alloc::print("");
16	};
17	($($arg:tt)*) => {
18		$crate::imports::std::print(&$crate::prelude::format!($($arg)*));
19	};
20}
21
22/// Prints to Aidoku logs if debug assertions are enabled.
23#[macro_export]
24macro_rules! debug {
25	() => {
26		#[cfg(debug_assertions)]
27		{
28			$crate::prelude::println!();
29		}
30	};
31	($($arg:tt)*) => {
32		#[cfg(debug_assertions)]
33		{
34			$crate::prelude::println!($($arg)*);
35		}
36	};
37}
38
39/// Constructs an error with a message.
40///
41/// This macro is equivalent to
42/// <code>AidokuError::message(format!($args\...))</code>.
43#[macro_export]
44macro_rules! error {
45	($($arg:tt)*) => {
46		$crate::AidokuError::message($crate::prelude::format!($($arg)*))
47	};
48}
49
50/// Returns early with an error.
51///
52/// This macro is equivalent to
53/// <code>return Err(error!($args\...))</code>.
54///
55/// The surrounding function's or closure's return value is required to be
56/// <code>Result&lt;_, [aidoku::AidokuError][crate::AidokuError]&gt;</code>.
57#[macro_export]
58macro_rules! bail {
59	($($arg:tt)*) => {
60		return ::core::result::Result::Err($crate::error!($($arg)*));
61	};
62}
63
64/// Registers a source for use with Aidoku.
65///
66/// The first argument should be the struct that implements the Source trait, and the
67/// following arguments should be all the additional traits that the source implements.
68///
69/// # Examples
70///
71/// ```ignore
72/// struct TestSource;
73///
74/// impl Source for TestSource { /* ... */ }
75/// impl Home for TestSource { /* ... */ }
76///
77/// // register TestSource with the extra Home trait
78/// register_source!(TestSource, Home);
79/// ```
80#[macro_export]
81macro_rules! register_source {
82	($source_type:ty $(, $param:ident)*) => {
83		static mut SOURCE: ::core::option::Option<$crate::alloc::Box<$source_type>> =
84			::core::option::Option::None;
85
86		fn __source() -> &'static mut $source_type {
87			unsafe { SOURCE.as_deref_mut().unwrap() }
88		}
89
90		fn __handle_result<T: $crate::serde::Serialize>(
91			result: ::core::result::Result<T, $crate::imports::error::AidokuError>,
92		) -> i32 {
93			match &result {
94				::core::result::Result::Ok(result) => {
95					let mut bytes = $crate::postcard::to_allocvec(result).unwrap();
96
97				 	bytes.splice(0..0, [0,0,0,0,0,0,0,0]);
98					let len_bytes = (bytes.len() as i32).to_le_bytes();
99					bytes[0..4].copy_from_slice(&len_bytes);
100					let cap_bytes = (bytes.capacity() as i32).to_le_bytes();
101					bytes[4..8].copy_from_slice(&cap_bytes);
102
103					let ptr = bytes.as_ptr() as i32;
104					::core::mem::forget(bytes);
105					ptr
106				}
107				::core::result::Result::Err(err) => __handle_error(err),
108			}
109		}
110
111		fn __handle_error(error: &$crate::imports::error::AidokuError) -> i32 {
112			$crate::prelude::println!("Error: {:?}", error);
113			match error {
114				$crate::imports::error::AidokuError::Unimplemented => -2,
115				$crate::imports::error::AidokuError::RequestError(_) => -3,
116				$crate::imports::error::AidokuError::Message(string) => {
117					let mut buffer = (-1 as i32).to_le_bytes().to_vec();
118
119					buffer.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
120					buffer.extend_from_slice(&string.as_bytes());
121
122					let cap_bytes = (buffer.capacity() as i32).to_le_bytes();
123					buffer[4..8].copy_from_slice(&cap_bytes);
124					let len_bytes = (buffer.len() as i32).to_le_bytes();
125					buffer[8..12].copy_from_slice(&len_bytes);
126
127					let ptr = buffer.as_ptr() as i32;
128					::core::mem::forget(buffer);
129					ptr
130				}
131				_ => -1,
132			}
133		}
134
135		// once rust supports exporting a wasm start function, we should use that instead
136		#[unsafe(export_name = "start")]
137		pub extern "C" fn __start() {
138			unsafe {
139				SOURCE =
140					::core::option::Option::Some($crate::alloc::Box::new(<$source_type>::new()))
141			};
142		}
143
144		#[unsafe(no_mangle)]
145		#[unsafe(export_name = "free_result")]
146		pub unsafe extern "C" fn __wasm_free_result(ptr: i32) {
147			$crate::imports::std::free_result(ptr);
148		}
149
150		#[unsafe(no_mangle)]
151		#[unsafe(export_name = "get_search_manga_list")]
152		pub unsafe extern "C" fn __wasm_get_search_manga_list(
153			query_descriptor: i32,
154			page: i32,
155			filters_descriptor: i32,
156		) -> i32 {
157			let query = $crate::imports::std::read_string(query_descriptor);
158			let ::core::result::Result::Ok(filters) =
159				$crate::imports::std::read::<$crate::alloc::Vec<$crate::FilterValue>>(filters_descriptor)
160			else {
161				return -1;
162			};
163
164			let result = __source().get_search_manga_list(query, page, filters);
165			__handle_result(result)
166		}
167
168		#[unsafe(no_mangle)]
169		#[unsafe(export_name = "get_manga_update")]
170		pub unsafe extern "C" fn __wasm_get_manga_update(
171			manga_descriptor: i32,
172			needs_details: bool,
173			needs_chapters: bool,
174		) -> i32 {
175			let ::core::result::Result::Ok(manga) =
176				$crate::imports::std::read::<$crate::Manga>(manga_descriptor)
177			else {
178				return -1;
179			};
180
181			let result = __source().get_manga_update(manga, needs_details, needs_chapters);
182			__handle_result(result)
183		}
184
185		#[unsafe(no_mangle)]
186		#[unsafe(export_name = "get_page_list")]
187		pub unsafe extern "C" fn __wasm_get_page_list(
188			manga_descriptor: i32,
189			chapter_descriptor: i32,
190		) -> i32 {
191			let ::core::result::Result::Ok(manga) =
192				$crate::imports::std::read::<$crate::Manga>(manga_descriptor)
193			else {
194				return -1;
195			};
196			let ::core::result::Result::Ok(chapter) =
197				$crate::imports::std::read::<$crate::Chapter>(chapter_descriptor)
198			else {
199				return -2;
200			};
201
202			let result = __source()
203				.get_page_list(manga, chapter)
204				.map(|pages| {
205					pages.into_iter()
206						.map(|mut page| {
207							page.ensure_externally_managed();
208							page
209						})
210						.collect::<$crate::alloc::Vec<_>>()
211				});
212			__handle_result(result)
213		}
214
215		$(
216			register_source!(@single $param);
217		)*
218	};
219
220	(@single ListingProvider) => {
221		#[unsafe(no_mangle)]
222		#[unsafe(export_name = "get_manga_list")]
223		pub unsafe extern "C" fn __wasm_get_manga_list(listing_descriptor: i32, page: i32) -> i32 {
224			let ::core::result::Result::Ok(listing) =
225				$crate::imports::std::read::<$crate::Listing>(listing_descriptor)
226			else {
227				return -1;
228			};
229
230			use $crate::ListingProvider;
231			let result = __source().get_manga_list(listing, page);
232			__handle_result(result)
233		}
234	};
235
236	(@single Home) => {
237		#[unsafe(no_mangle)]
238		#[unsafe(export_name = "get_home")]
239		pub unsafe extern "C" fn __wasm_get_home() -> i32 {
240			use $crate::Home;
241			let result = __source().get_home();
242			__handle_result(result)
243		}
244	};
245
246	(@single DynamicListings) => {
247		#[unsafe(no_mangle)]
248		#[unsafe(export_name = "get_listings")]
249		pub unsafe extern "C" fn __wasm_get_listings() -> i32 {
250			use $crate::DynamicListings;
251			let result = __source().get_dynamic_listings();
252			__handle_result(result)
253		}
254	};
255
256	(@single DynamicFilters) => {
257		#[unsafe(no_mangle)]
258		#[unsafe(export_name = "get_filters")]
259		pub unsafe extern "C" fn __wasm_get_filters() -> i32 {
260			use $crate::DynamicFilters;
261			let result = __source().get_dynamic_filters();
262			__handle_result(result)
263		}
264	};
265
266	(@single DynamicSettings) => {
267		#[unsafe(no_mangle)]
268		#[unsafe(export_name = "get_settings")]
269		pub unsafe extern "C" fn __wasm_get_settings() -> i32 {
270			use $crate::DynamicSettings;
271			let result = __source().get_dynamic_settings();
272			__handle_result(result)
273		}
274	};
275
276	(@single PageImageProcessor) => {
277		#[unsafe(no_mangle)]
278		#[unsafe(export_name = "process_page_image")]
279		pub unsafe extern "C" fn __wasm_process_page_image(
280			response_descriptor: i32,
281			context_descriptor: i32,
282		) -> i32 {
283			let ::core::result::Result::Ok(response) =
284				$crate::imports::std::read::<$crate::ImageResponse>(response_descriptor)
285			else {
286				return -1;
287			};
288			let context: ::core::option::Option<$crate::PageContext> = if context_descriptor < 0 {
289				None
290			} else if let ::core::result::Result::Ok(context) =
291				$crate::imports::std::read::<$crate::PageContext>(context_descriptor)
292			{
293				Some(context)
294			} else {
295				return -2;
296			};
297
298			use $crate::PageImageProcessor;
299			let mut result = __source().process_page_image(response, context);
300			if let Ok(image_ref) = result.as_mut() {
301				image_ref.externally_managed = true;
302			}
303			__handle_result(result.map(|r| r.rid))
304		}
305	};
306
307	(@single ImageRequestProvider) => {
308		#[unsafe(no_mangle)]
309		#[unsafe(export_name = "get_image_request")]
310		pub unsafe extern "C" fn __wasm_get_image_request(
311			url_descriptor: i32,
312			context_descriptor: i32,
313		) -> i32 {
314			let ::core::result::Result::Ok(url) =
315				$crate::imports::std::read::<$crate::alloc::String>(url_descriptor)
316			else {
317				return -1;
318			};
319			let context: ::core::option::Option<$crate::PageContext> = if context_descriptor < 0 {
320				None
321			} else if let ::core::result::Result::Ok(context) =
322				$crate::imports::std::read::<$crate::PageContext>(context_descriptor)
323			{
324				Some(context)
325			} else {
326				return -2;
327			};
328
329			use $crate::ImageRequestProvider;
330			let mut result = __source().get_image_request(url, context);
331			if let Ok(request) = result.as_mut() {
332				request.should_close = false;
333			}
334			__handle_result(result.map(|r| r.rid))
335		}
336	};
337
338	(@single PageDescriptionProvider) => {
339		#[unsafe(no_mangle)]
340		#[unsafe(export_name = "get_page_description")]
341		pub unsafe extern "C" fn __wasm_get_page_description(page_descriptor: i32) -> i32 {
342			let ::core::result::Result::Ok(page) =
343				$crate::imports::std::read::<$crate::Page>(page_descriptor)
344			else {
345				return -1;
346			};
347			use $crate::PageDescriptionProvider;
348			let result = __source().get_page_description(page);
349			__handle_result(result)
350		}
351	};
352
353	(@single AlternateCoverProvider) => {
354		#[unsafe(no_mangle)]
355		#[unsafe(export_name = "get_alternate_covers")]
356		pub unsafe extern "C" fn __wasm_get_alternate_covers(manga_descriptor: i32) -> i32 {
357			let ::core::result::Result::Ok(manga) =
358				$crate::imports::std::read::<$crate::Manga>(manga_descriptor)
359			else {
360				return -1;
361			};
362			use $crate::AlternateCoverProvider;
363			let result = __source().get_alternate_covers(manga);
364			__handle_result(result)
365		}
366	};
367
368	(@single BaseUrlProvider) => {
369		#[unsafe(no_mangle)]
370		#[unsafe(export_name = "get_base_url")]
371		pub unsafe extern "C" fn __wasm_get_base_url() -> i32 {
372			use $crate::BaseUrlProvider;
373			let result = __source().get_base_url();
374			__handle_result(result)
375		}
376	};
377
378	(@single NotificationHandler) => {
379		#[unsafe(no_mangle)]
380		#[unsafe(export_name = "handle_notification")]
381		pub unsafe extern "C" fn __wasm_handle_notification(string_descriptor: i32) -> i32 {
382			let ::core::result::Result::Ok(notification) =
383				$crate::imports::std::read::<$crate::alloc::String>(string_descriptor)
384			else {
385				return -1;
386			};
387			use $crate::NotificationHandler;
388			__source().handle_notification(notification);
389			return 0;
390		}
391	};
392
393	(@single DeepLinkHandler) => {
394		#[unsafe(no_mangle)]
395		#[unsafe(export_name = "handle_deep_link")]
396		pub unsafe extern "C" fn __wasm_handle_deep_link(string_descriptor: i32) -> i32 {
397			let ::core::result::Result::Ok(url) =
398				$crate::imports::std::read::<$crate::alloc::String>(string_descriptor)
399			else {
400				return -1;
401			};
402			use $crate::DeepLinkHandler;
403			let result = __source().handle_deep_link(url);
404			__handle_result(result)
405		}
406	};
407
408	(@single BasicLoginHandler) => {
409		#[unsafe(no_mangle)]
410		#[unsafe(export_name = "handle_basic_login")]
411		pub unsafe extern "C" fn __wasm_handle_basic_login(
412			key_descriptor: i32,
413			username_descriptor: i32,
414			password_descriptor: i32,
415		) -> i32 {
416			let ::core::result::Result::Ok(key) =
417				$crate::imports::std::read::<$crate::alloc::String>(key_descriptor)
418			else {
419				return -1;
420			};
421			let ::core::result::Result::Ok(username) =
422				$crate::imports::std::read::<$crate::alloc::String>(username_descriptor)
423			else {
424				return -2;
425			};
426			let ::core::result::Result::Ok(password) =
427				$crate::imports::std::read::<$crate::alloc::String>(password_descriptor)
428			else {
429				return -3;
430			};
431			use $crate::BasicLoginHandler;
432			let result = __source().handle_basic_login(key, username, password);
433			__handle_result(result)
434		}
435	};
436
437	(@single WebLoginHandler) => {
438		#[unsafe(no_mangle)]
439		#[unsafe(export_name = "handle_web_login")]
440		pub unsafe extern "C" fn __wasm_handle_web_login(
441			key_descriptor: i32,
442			keys_descriptor: i32,
443			values_descriptor: i32,
444		) -> i32 {
445			let ::core::result::Result::Ok(key) =
446				$crate::imports::std::read::<$crate::alloc::String>(key_descriptor)
447			else {
448				return -1;
449			};
450			let ::core::result::Result::Ok(keys) = $crate::imports::std::read::<
451				$crate::alloc::Vec<$crate::alloc::String>,
452			>(keys_descriptor) else {
453				return -2;
454			};
455			let ::core::result::Result::Ok(values) = $crate::imports::std::read::<
456				$crate::alloc::Vec<$crate::alloc::String>,
457			>(values_descriptor) else {
458				return -3;
459			};
460			use $crate::WebLoginHandler;
461			let result = __source().handle_web_login(key, keys.into_iter().zip(values).collect());
462			__handle_result(result)
463		}
464	};
465
466	(@single MigrationHandler) => {
467		#[unsafe(no_mangle)]
468		#[unsafe(export_name = "handle_key_migration")]
469		pub unsafe extern "C" fn __wasm_handle_key_migration(
470			key_kind: i32,
471			manga_key_descriptor: i32,
472			chapter_key_descriptor: i32,
473		) -> i32 {
474			let ::core::result::Result::Ok(manga_key) =
475				$crate::imports::std::read::<$crate::alloc::String>(manga_key_descriptor)
476			else {
477				return -1;
478			};
479			use $crate::MigrationHandler;
480			let result = match key_kind {
481				// manga
482				0 => __source().handle_manga_migration(manga_key),
483				// chapter
484				1 => {
485					let ::core::result::Result::Ok(chapter_key) =
486						$crate::imports::std::read::<$crate::alloc::String>(chapter_key_descriptor)
487					else {
488						return -2;
489					};
490					__source().handle_chapter_migration(manga_key, chapter_key)
491				}
492				_ => return -3,
493			};
494			__handle_result(result)
495		}
496	};
497}