1use bytes::Bytes;
37use url::Url;
38
39#[cfg(not(target_os = "emscripten"))]
41pub async fn fetch(url: Url) -> anyhow::Result<Bytes> {
42 let client: reqwest::blocking::Client = freya_core::prelude::GlobalContexts::get()
43 .get_context_or_insert(reqwest::blocking::Client::new);
44 freya_core::prelude::thread(move || Ok(client.get(url).send()?.error_for_status()?.bytes()?))
45 .await
46}
47
48#[cfg(target_os = "emscripten")]
52pub async fn fetch(url: Url) -> anyhow::Result<Bytes> {
53 use std::ffi::{
54 CString,
55 c_char,
56 c_int,
57 c_void,
58 };
59
60 use anyhow::Context;
61 use futures_channel::oneshot::{
62 Sender,
63 channel,
64 };
65
66 unsafe extern "C" {
67 fn emscripten_async_wget_data(
68 url: *const c_char,
69 argument: *mut c_void,
70 on_load: extern "C" fn(*mut c_void, *mut c_void, c_int),
71 on_error: extern "C" fn(*mut c_void),
72 );
73 }
74
75 extern "C" fn on_load(argument: *mut c_void, data: *mut c_void, size: c_int) {
76 let sender = unsafe { Box::from_raw(argument.cast::<Sender<Option<Bytes>>>()) };
77 let data = unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size as usize) };
78 sender.send(Some(Bytes::copy_from_slice(data))).ok();
79 }
80
81 extern "C" fn on_error(argument: *mut c_void) {
82 let sender = unsafe { Box::from_raw(argument.cast::<Sender<Option<Bytes>>>()) };
83 sender.send(None).ok();
84 }
85
86 let c_url = CString::new(url.as_str())?;
87 let (sender, receiver) = channel::<Option<Bytes>>();
88 let argument = Box::into_raw(Box::new(sender)).cast();
89
90 unsafe { emscripten_async_wget_data(c_url.as_ptr(), argument, on_load, on_error) };
91
92 receiver
93 .await
94 .ok()
95 .flatten()
96 .with_context(|| format!("Failed to fetch {url}."))
97}