Skip to main content

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::Message(string) => {
115					let mut buffer = (-1 as i32).to_le_bytes().to_vec();
116
117					buffer.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
118					buffer.extend_from_slice(&string.as_bytes());
119
120					let cap_bytes = (buffer.capacity() as i32).to_le_bytes();
121					buffer[4..8].copy_from_slice(&cap_bytes);
122					let len_bytes = (buffer.len() as i32).to_le_bytes();
123					buffer[8..12].copy_from_slice(&len_bytes);
124
125					let ptr = buffer.as_ptr() as i32;
126					::core::mem::forget(buffer);
127					ptr
128				}
129				error => error.error_code(),
130			}
131		}
132
133		// once rust supports exporting a wasm start function, we should use that instead
134		#[unsafe(export_name = "start")]
135		pub extern "C" fn __start() {
136			unsafe {
137				SOURCE =
138					::core::option::Option::Some($crate::alloc::Box::new(<$source_type>::new()))
139			};
140		}
141
142		#[unsafe(no_mangle)]
143		#[unsafe(export_name = "free_result")]
144		pub unsafe extern "C" fn __wasm_free_result(ptr: i32) {
145			$crate::imports::std::free_result(ptr);
146		}
147
148		#[unsafe(no_mangle)]
149		#[unsafe(export_name = "get_search_manga_list")]
150		pub unsafe extern "C" fn __wasm_get_search_manga_list(
151			query_descriptor: i32,
152			page: i32,
153			filters_descriptor: i32,
154		) -> i32 {
155			let query = $crate::imports::std::read_string(query_descriptor);
156			let ::core::result::Result::Ok(filters) =
157				$crate::imports::std::read::<$crate::alloc::Vec<$crate::FilterValue>>(filters_descriptor)
158			else {
159				return -1;
160			};
161
162			let result = __source().get_search_manga_list(query, page, filters);
163			__handle_result(result)
164		}
165
166		#[unsafe(no_mangle)]
167		#[unsafe(export_name = "get_manga_update")]
168		pub unsafe extern "C" fn __wasm_get_manga_update(
169			manga_descriptor: i32,
170			needs_details: bool,
171			needs_chapters: bool,
172		) -> i32 {
173			let ::core::result::Result::Ok(manga) =
174				$crate::imports::std::read::<$crate::Manga>(manga_descriptor)
175			else {
176				return -1;
177			};
178
179			let result = __source().get_manga_update(manga, needs_details, needs_chapters);
180			__handle_result(result)
181		}
182
183		#[unsafe(no_mangle)]
184		#[unsafe(export_name = "get_page_list")]
185		pub unsafe extern "C" fn __wasm_get_page_list(
186			manga_descriptor: i32,
187			chapter_descriptor: i32,
188		) -> i32 {
189			let ::core::result::Result::Ok(manga) =
190				$crate::imports::std::read::<$crate::Manga>(manga_descriptor)
191			else {
192				return -1;
193			};
194			let ::core::result::Result::Ok(chapter) =
195				$crate::imports::std::read::<$crate::Chapter>(chapter_descriptor)
196			else {
197				return -2;
198			};
199
200			let result = __source()
201				.get_page_list(manga, chapter)
202				.map(|pages| {
203					pages.into_iter()
204						.map(|mut page| {
205							page.ensure_externally_managed();
206							page
207						})
208						.collect::<$crate::alloc::Vec<_>>()
209				});
210			__handle_result(result)
211		}
212
213		$(
214			register_source!(@single $param);
215		)*
216	};
217
218	(@single ListingProvider) => {
219		#[unsafe(no_mangle)]
220		#[unsafe(export_name = "get_manga_list")]
221		pub unsafe extern "C" fn __wasm_get_manga_list(listing_descriptor: i32, page: i32) -> i32 {
222			let ::core::result::Result::Ok(listing) =
223				$crate::imports::std::read::<$crate::Listing>(listing_descriptor)
224			else {
225				return -1;
226			};
227
228			use $crate::ListingProvider;
229			let result = __source().get_manga_list(listing, page);
230			__handle_result(result)
231		}
232	};
233
234	(@single Home) => {
235		#[unsafe(no_mangle)]
236		#[unsafe(export_name = "get_home")]
237		pub unsafe extern "C" fn __wasm_get_home() -> i32 {
238			use $crate::Home;
239			let result = __source().get_home();
240			__handle_result(result)
241		}
242	};
243
244	(@single DynamicListings) => {
245		#[unsafe(no_mangle)]
246		#[unsafe(export_name = "get_listings")]
247		pub unsafe extern "C" fn __wasm_get_listings() -> i32 {
248			use $crate::DynamicListings;
249			let result = __source().get_dynamic_listings();
250			__handle_result(result)
251		}
252	};
253
254	(@single DynamicFilters) => {
255		#[unsafe(no_mangle)]
256		#[unsafe(export_name = "get_filters")]
257		pub unsafe extern "C" fn __wasm_get_filters() -> i32 {
258			use $crate::DynamicFilters;
259			let result = __source().get_dynamic_filters();
260			__handle_result(result)
261		}
262	};
263
264	(@single DynamicSettings) => {
265		#[unsafe(no_mangle)]
266		#[unsafe(export_name = "get_settings")]
267		pub unsafe extern "C" fn __wasm_get_settings() -> i32 {
268			use $crate::DynamicSettings;
269			let result = __source().get_dynamic_settings();
270			__handle_result(result)
271		}
272	};
273
274	(@single PageImageProcessor) => {
275		#[unsafe(no_mangle)]
276		#[unsafe(export_name = "process_page_image")]
277		pub unsafe extern "C" fn __wasm_process_page_image(
278			response_descriptor: i32,
279			context_descriptor: i32,
280		) -> i32 {
281			let ::core::result::Result::Ok(response) =
282				$crate::imports::std::read::<$crate::ImageResponse>(response_descriptor)
283			else {
284				return -1;
285			};
286			let context: ::core::option::Option<$crate::PageContext> = if context_descriptor < 0 {
287				None
288			} else if let ::core::result::Result::Ok(context) =
289				$crate::imports::std::read::<$crate::PageContext>(context_descriptor)
290			{
291				Some(context)
292			} else {
293				return -2;
294			};
295
296			use $crate::PageImageProcessor;
297			let mut result = __source().process_page_image(response, context);
298			if let Ok(image_ref) = result.as_mut() {
299				image_ref.externally_managed = true;
300			}
301			__handle_result(result.map(|r| r.rid))
302		}
303	};
304
305	(@single CoverImageProcessor) => {
306		#[unsafe(no_mangle)]
307		#[unsafe(export_name = "process_cover_image")]
308		pub unsafe extern "C" fn __wasm_process_cover_image(response_descriptor: i32) -> i32 {
309			let ::core::result::Result::Ok(response) =
310				$crate::imports::std::read::<$crate::ImageResponse>(response_descriptor)
311			else {
312				return -1;
313			};
314
315			use $crate::CoverImageProcessor;
316			let mut result = __source().process_cover_image(response);
317			if let Ok(image_ref) = result.as_mut() {
318				image_ref.externally_managed = true;
319			}
320			__handle_result(result.map(|r| r.rid))
321		}
322	};
323
324	(@single ImageRequestProvider) => {
325		#[unsafe(no_mangle)]
326		#[unsafe(export_name = "get_image_request")]
327		pub unsafe extern "C" fn __wasm_get_image_request(
328			url_descriptor: i32,
329			context_descriptor: i32,
330		) -> i32 {
331			let ::core::result::Result::Ok(url) =
332				$crate::imports::std::read::<$crate::alloc::String>(url_descriptor)
333			else {
334				return -1;
335			};
336			let context: ::core::option::Option<$crate::PageContext> = if context_descriptor < 0 {
337				None
338			} else if let ::core::result::Result::Ok(context) =
339				$crate::imports::std::read::<$crate::PageContext>(context_descriptor)
340			{
341				Some(context)
342			} else {
343				return -2;
344			};
345
346			use $crate::ImageRequestProvider;
347			let mut result = __source().get_image_request(url, context);
348			if let Ok(request) = result.as_mut() {
349				request.should_close = false;
350			}
351			__handle_result(result.map(|r| r.rid))
352		}
353	};
354
355	(@single PageDescriptionProvider) => {
356		#[unsafe(no_mangle)]
357		#[unsafe(export_name = "get_page_description")]
358		pub unsafe extern "C" fn __wasm_get_page_description(page_descriptor: i32) -> i32 {
359			let ::core::result::Result::Ok(page) =
360				$crate::imports::std::read::<$crate::Page>(page_descriptor)
361			else {
362				return -1;
363			};
364			use $crate::PageDescriptionProvider;
365			let result = __source().get_page_description(page);
366			__handle_result(result)
367		}
368	};
369
370	(@single AlternateCoverProvider) => {
371		#[unsafe(no_mangle)]
372		#[unsafe(export_name = "get_alternate_covers")]
373		pub unsafe extern "C" fn __wasm_get_alternate_covers(manga_descriptor: i32) -> i32 {
374			let ::core::result::Result::Ok(manga) =
375				$crate::imports::std::read::<$crate::Manga>(manga_descriptor)
376			else {
377				return -1;
378			};
379			use $crate::AlternateCoverProvider;
380			let result = __source().get_alternate_covers(manga);
381			__handle_result(result)
382		}
383	};
384
385	(@single BaseUrlProvider) => {
386		#[unsafe(no_mangle)]
387		#[unsafe(export_name = "get_base_url")]
388		pub unsafe extern "C" fn __wasm_get_base_url() -> i32 {
389			use $crate::BaseUrlProvider;
390			let result = __source().get_base_url();
391			__handle_result(result)
392		}
393	};
394
395	(@single NotificationHandler) => {
396		#[unsafe(no_mangle)]
397		#[unsafe(export_name = "handle_notification")]
398		pub unsafe extern "C" fn __wasm_handle_notification(string_descriptor: i32) -> i32 {
399			let ::core::result::Result::Ok(notification) =
400				$crate::imports::std::read::<$crate::alloc::String>(string_descriptor)
401			else {
402				return -1;
403			};
404			use $crate::NotificationHandler;
405			__source().handle_notification(notification);
406			return 0;
407		}
408	};
409
410	(@single DeepLinkHandler) => {
411		#[unsafe(no_mangle)]
412		#[unsafe(export_name = "handle_deep_link")]
413		pub unsafe extern "C" fn __wasm_handle_deep_link(string_descriptor: i32) -> i32 {
414			let ::core::result::Result::Ok(url) =
415				$crate::imports::std::read::<$crate::alloc::String>(string_descriptor)
416			else {
417				return -1;
418			};
419			use $crate::DeepLinkHandler;
420			let result = __source().handle_deep_link(url);
421			__handle_result(result)
422		}
423	};
424
425	(@single BasicLoginHandler) => {
426		#[unsafe(no_mangle)]
427		#[unsafe(export_name = "handle_basic_login")]
428		pub unsafe extern "C" fn __wasm_handle_basic_login(
429			key_descriptor: i32,
430			username_descriptor: i32,
431			password_descriptor: i32,
432		) -> i32 {
433			let ::core::result::Result::Ok(key) =
434				$crate::imports::std::read::<$crate::alloc::String>(key_descriptor)
435			else {
436				return -1;
437			};
438			let ::core::result::Result::Ok(username) =
439				$crate::imports::std::read::<$crate::alloc::String>(username_descriptor)
440			else {
441				return -2;
442			};
443			let ::core::result::Result::Ok(password) =
444				$crate::imports::std::read::<$crate::alloc::String>(password_descriptor)
445			else {
446				return -3;
447			};
448			use $crate::BasicLoginHandler;
449			let result = __source().handle_basic_login(key, username, password);
450			__handle_result(result)
451		}
452	};
453
454	(@single WebLoginHandler) => {
455		#[unsafe(no_mangle)]
456		#[unsafe(export_name = "handle_web_login")]
457		pub unsafe extern "C" fn __wasm_handle_web_login(
458			key_descriptor: i32,
459			keys_descriptor: i32,
460			values_descriptor: i32,
461		) -> i32 {
462			let ::core::result::Result::Ok(key) =
463				$crate::imports::std::read::<$crate::alloc::String>(key_descriptor)
464			else {
465				return -1;
466			};
467			let ::core::result::Result::Ok(keys) = $crate::imports::std::read::<
468				$crate::alloc::Vec<$crate::alloc::String>,
469			>(keys_descriptor) else {
470				return -2;
471			};
472			let ::core::result::Result::Ok(values) = $crate::imports::std::read::<
473				$crate::alloc::Vec<$crate::alloc::String>,
474			>(values_descriptor) else {
475				return -3;
476			};
477			use $crate::WebLoginHandler;
478			let result = __source().handle_web_login(key, keys.into_iter().zip(values).collect());
479			__handle_result(result)
480		}
481	};
482
483	(@single MigrationHandler) => {
484		#[unsafe(no_mangle)]
485		#[unsafe(export_name = "handle_key_migration")]
486		pub unsafe extern "C" fn __wasm_handle_key_migration(
487			key_kind: i32,
488			manga_key_descriptor: i32,
489			chapter_key_descriptor: i32,
490		) -> i32 {
491			let ::core::result::Result::Ok(manga_key) =
492				$crate::imports::std::read::<$crate::alloc::String>(manga_key_descriptor)
493			else {
494				return -1;
495			};
496			use $crate::MigrationHandler;
497			let result = match key_kind {
498				// manga
499				0 => __source().handle_manga_migration(manga_key),
500				// chapter
501				1 => {
502					let ::core::result::Result::Ok(chapter_key) =
503						$crate::imports::std::read::<$crate::alloc::String>(chapter_key_descriptor)
504					else {
505						return -2;
506					};
507					__source().handle_chapter_migration(manga_key, chapter_key)
508				}
509				_ => return -3,
510			};
511			__handle_result(result)
512		}
513	};
514}