1use crate::{RequestContext, Resource};
3use fusor::OwnerHandle;
4use std::future::Future;
5use wasm_bindgen::{JsCast, JsValue};
6use wasm_bindgen_futures::JsFuture;
7use web_sys::{AbortController, Request, RequestInit, Response};
8
9pub fn read<K, T, E, F>(
11 owner: &OwnerHandle,
12 key: impl Fn() -> K + 'static,
13 load: impl Fn(K, RequestContext) -> F + 'static,
14) -> crate::AsyncValue<K, T, E>
15where
16 K: Clone + PartialEq + 'static,
17 T: 'static,
18 E: std::fmt::Display + 'static,
19 F: Future<Output = Result<T, E>> + 'static,
20{
21 crate::AsyncValue::new(owner, key, load, wasm_bindgen_futures::spawn_local)
22}
23
24pub fn resource<K, T, E, F>(
25 owner: &OwnerHandle,
26 key: impl Fn() -> Option<K> + 'static,
27 load: impl Fn(K, RequestContext) -> F + 'static,
28) -> Resource<K, T, E>
29where
30 K: Clone + PartialEq + 'static,
31 T: 'static,
32 E: 'static,
33 F: Future<Output = Result<T, E>> + 'static,
34{
35 Resource::new(owner, key, load, wasm_bindgen_futures::spawn_local)
36}
37
38impl RequestContext {
39 pub async fn get_text(&self, url: &str) -> Result<String, JsValue> {
42 if self.is_cancelled() {
43 return Err(JsValue::from_str("request cancelled"));
44 }
45 let controller = AbortController::new()?;
46 let options = RequestInit::new();
47 options.set_method("GET");
48 options.set_signal(Some(&controller.signal()));
49 let request = Request::new_with_str_and_init(url, &options)?;
50 let _cancel = self.on_cancel(move || controller.abort());
51 let window =
52 web_sys::window().ok_or_else(|| JsValue::from_str("Fetch requires a browser"))?;
53 let response: Response = JsFuture::from(window.fetch_with_request(&request))
54 .await?
55 .dyn_into()?;
56 if !response.ok() {
57 return Err(JsValue::from_str(&format!(
58 "GET {url}: HTTP {}",
59 response.status()
60 )));
61 }
62 JsFuture::from(response.text()?)
63 .await?
64 .as_string()
65 .ok_or_else(|| JsValue::from_str("Fetch returned a non-text body"))
66 }
67}