torigen_mounter 1.0.5

Core functionality for Torigen, a tool for mounting and managing Tor hidden services.
Documentation
#[macro_export]
macro_rules! export_wasm_extension {
    ($extension_type:ty) => {
        use std::sync::OnceLock;
        use torigen_mounter::core::wasm::{
            ExtensionMethods, WasmExtension, WasmExtensionProvider, WasmExtensionWrapper,
        };

        static EXTENSION: OnceLock<WasmExtensionWrapper<$extension_type>> = OnceLock::new();

        fn get_extension() -> &'static WasmExtensionWrapper<$extension_type> {
            EXTENSION.get_or_init(|| WasmExtensionWrapper::new(<$extension_type>::new()))
        }

        #[no_mangle]
        pub unsafe extern "C" fn extension_call(
            method_name_ptr: i32,
            method_name_len: i32,
            args_ptr: i32,
            args_len: i32,
            result_ptr: i32,
            result_len_ptr: i32,
        ) -> i32 {
            if result_ptr == 0 {
                return handle_size_request(
                    method_name_ptr,
                    method_name_len,
                    args_ptr,
                    args_len,
                    result_len_ptr,
                );
            }

            return handle_data_request(
                method_name_ptr,
                method_name_len,
                args_ptr,
                args_len,
                result_ptr,
                result_len_ptr,
            );
        }

        unsafe fn handle_size_request(
            method_name_ptr: i32,
            method_name_len: i32,
            args_ptr: i32,
            args_len: i32,
            result_len_ptr: i32,
        ) -> i32 {
            let method_name = match read_string_from_memory(method_name_ptr, method_name_len) {
                Ok(s) => s,
                Err(_) => return -1,
            };

            let args = if args_len > 0 {
                match read_string_from_memory(args_ptr, args_len) {
                    Ok(s) => s,
                    Err(_) => return -1,
                }
            } else {
                String::new()
            };

            let method = match ExtensionMethods::from_str(&method_name) {
                Some(m) => m,
                None => return -1,
            };

            let result = call_extension_method(method, &args);
            let result_bytes = result.as_bytes();

            write_u32_to_memory(result_len_ptr, result_bytes.len() as u32);

            1
        }

        unsafe fn handle_data_request(
            method_name_ptr: i32,
            method_name_len: i32,
            args_ptr: i32,
            args_len: i32,
            result_ptr: i32,
            result_len_ptr: i32,
        ) -> i32 {
            let method_name = match read_string_from_memory(method_name_ptr, method_name_len) {
                Ok(s) => s,
                Err(_) => return -1,
            };

            let args = if args_len > 0 {
                match read_string_from_memory(args_ptr, args_len) {
                    Ok(s) => s,
                    Err(_) => return -1,
                }
            } else {
                String::new()
            };

            let method = match ExtensionMethods::from_str(&method_name) {
                Some(m) => m,
                None => return -1,
            };
            let result = call_extension_method(method, &args);
            let result_bytes = result.as_bytes();

            let available_size = read_u32_from_memory(result_len_ptr) as usize;

            if result_bytes.len() > available_size {
                return -1;
            }

            write_bytes_to_memory(result_ptr, result_bytes);

            write_u32_to_memory(result_len_ptr, result_bytes.len() as u32);

            0
        }

        fn call_extension_method(method: ExtensionMethods, args: &str) -> String {
            match method {
                ExtensionMethods::GetSourceInfo => get_extension().get_source_info(),
                ExtensionMethods::GetMetadata => get_extension().get_metadata(),
                ExtensionMethods::GetHomepageRequest => get_extension().get_homepage_request(),
                ExtensionMethods::ProcessHomepageRes => get_extension().process_homepage_res(args),
                ExtensionMethods::GetViewmoreRequest => {
                    let parsed: Result<serde_json::Value, _> = serde_json::from_str(args);
                    match parsed {
                        Ok(json) => {
                            let section_id = json["section_id"].as_str().unwrap_or("");
                            let page = json["page"].as_u64().unwrap_or(1) as u32;
                            get_extension().get_viewmore_request(section_id, page)
                        }
                        Err(_) => serde_json::to_string(&"Parse error").unwrap_or_default(),
                    }
                }
                ExtensionMethods::ProcessViewmoreRes => get_extension().process_viewmore_res(args),
                ExtensionMethods::GetSearchRequest => get_extension().get_search_request(args),
                ExtensionMethods::ProcessSearchRes => get_extension().process_search_res(args),
                ExtensionMethods::GetSearchTagsRequest => get_extension().get_search_tags_request(),
                ExtensionMethods::ProcessSearchTagsRes => {
                    get_extension().process_search_tags_res(args)
                }
                ExtensionMethods::GetMangaRequest => get_extension().get_manga_request(args),
                ExtensionMethods::ProcessMangaRes => get_extension().process_manga_res(args),
                ExtensionMethods::GetChaptersRequest => get_extension().get_chapters_request(args),
                ExtensionMethods::ProcessChaptersRes => get_extension().process_chapters_res(args),
                ExtensionMethods::GetChapterDetailsRequest => {
                    let parsed: Result<serde_json::Value, _> = serde_json::from_str(args);
                    match parsed {
                        Ok(json) => {
                            let manga_id = json["manga_id"].as_str().unwrap_or("");
                            let chapter_id = json["chapter_id"].as_str().unwrap_or("");
                            get_extension().get_chapter_details_request(manga_id, chapter_id)
                        }
                        Err(_) => serde_json::to_string(&"Parse error").unwrap_or_default(),
                    }
                }
                ExtensionMethods::ProcessChapterDetailsRes => {
                    get_extension().process_chapter_details_res(args)
                }
            }
        }

        unsafe fn read_string_from_memory(ptr: i32, len: i32) -> Result<String, ()> {
            if ptr == 0 || len <= 0 {
                return Err(());
            }

            let slice = std::slice::from_raw_parts(ptr as *const u8, len as usize);
            std::str::from_utf8(slice)
                .map(|s| s.to_string())
                .map_err(|_| ())
        }

        unsafe fn write_bytes_to_memory(ptr: i32, data: &[u8]) {
            if ptr == 0 || data.is_empty() {
                return;
            }

            let dest = std::slice::from_raw_parts_mut(ptr as *mut u8, data.len());
            dest.copy_from_slice(data);
        }

        unsafe fn read_u32_from_memory(ptr: i32) -> u32 {
            if ptr == 0 {
                return 0;
            }

            let bytes = std::slice::from_raw_parts(ptr as *const u8, 4);
            u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
        }

        unsafe fn write_u32_to_memory(ptr: i32, value: u32) {
            if ptr == 0 {
                return;
            }

            let bytes = value.to_le_bytes();
            let dest = std::slice::from_raw_parts_mut(ptr as *mut u8, 4);
            dest.copy_from_slice(&bytes);
        }

        // Required WASM exports for memory management
        #[no_mangle]
        pub extern "C" fn allocate(size: u32) -> i32 {
            let layout = std::alloc::Layout::from_size_align(size as usize, 1).unwrap();
            unsafe { std::alloc::alloc(layout) as i32 }
        }

        #[no_mangle]
        pub extern "C" fn deallocate(ptr: i32, size: i32) {
            if ptr == 0 || size <= 0 {
                return;
            }

            let layout = std::alloc::Layout::from_size_align(size as usize, 1).unwrap();
            unsafe { std::alloc::dealloc(ptr as *mut u8, layout) }
        }
    };
}