1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use std::ops::Deref;

use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;

use crate::{request::Request, response::Response, AbortSignal, Result};

/// Construct a Fetch call from a URL string or a Request object. Call its `send` method to execute
/// the request.
pub enum Fetch {
    Url(url::Url),
    Request(Request),
}

impl Fetch {
    /// Execute a Fetch call and receive a Response.
    pub async fn send(&self) -> Result<Response> {
        match self {
            Fetch::Url(url) => fetch_with_str(url.as_ref(), None).await,
            Fetch::Request(req) => fetch_with_request(req, None).await,
        }
    }

    /// Execute a Fetch call and receive a Response.
    pub async fn send_with_signal(&self, signal: &AbortSignal) -> Result<Response> {
        match self {
            Fetch::Url(url) => fetch_with_str(url.as_ref(), Some(signal)).await,
            Fetch::Request(req) => fetch_with_request(req, Some(signal)).await,
        }
    }
}

async fn fetch_with_str(url: &str, signal: Option<&AbortSignal>) -> Result<Response> {
    let mut init = web_sys::RequestInit::new();
    init.signal(signal.map(|x| x.deref()));

    let worker: web_sys::WorkerGlobalScope = js_sys::global().unchecked_into();
    let promise = worker.fetch_with_str_and_init(url, &init);
    let resp = JsFuture::from(promise).await?;
    let resp: web_sys::Response = resp.dyn_into()?;
    Ok(resp.into())
}

async fn fetch_with_request(request: &Request, signal: Option<&AbortSignal>) -> Result<Response> {
    let mut init = web_sys::RequestInit::new();
    init.signal(signal.map(|x| x.deref()));

    let worker: web_sys::WorkerGlobalScope = js_sys::global().unchecked_into();
    let req = request.inner();
    let promise = worker.fetch_with_request_and_init(req, &init);
    let resp = JsFuture::from(promise).await?;
    let edge_response: web_sys::Response = resp.dyn_into()?;
    Ok(edge_response.into())
}