Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-simview/src/backend.rs

//! **How a request leaves the app.** egui draws synchronously and a fetch is async, so
//! the app never waits: it [`send`](Backend::send)s and, each frame,
//! [`drain`](Backend::drain)s whatever came back. Two implementations: the browser's
//! `fetch` (wasm, same-origin, the session cookie rides along) and the in-process
//! [`MockBackend`](crate::mock::MockBackend) the tests and the native window use.

use crate::contract::{Req, Resp};

pub trait Backend {
    /// Start a request; its answer arrives through a later [`drain`](Self::drain).
    fn send(&self, req: Req);
    /// Every answer that has arrived since the last call, in arrival order.
    fn drain(&self) -> Vec<Resp>;
}

/// The browser: `window.fetch`, same-origin credentials.
#[cfg(target_arch = "wasm32")]
pub mod fetch {
    use std::cell::RefCell;
    use std::rc::Rc;

    use wasm_bindgen::JsCast as _;
    use wasm_bindgen_futures::JsFuture;

    use super::Backend;
    use crate::contract::{Req, Resp};

    #[derive(Default, Clone)]
    pub struct FetchBackend {
        inbox: Rc<RefCell<Vec<Resp>>>,
    }

    impl FetchBackend {
        pub fn new() -> Self {
            Self::default()
        }
    }

    async fn run(req: &Req) -> Result<(u16, Vec<u8>), String> {
        let (method, url) = req.http();
        let opts = web_sys::RequestInit::new();
        opts.set_method(method);
        opts.set_credentials(web_sys::RequestCredentials::SameOrigin);
        let request = web_sys::Request::new_with_str_and_init(&url, &opts).map_err(|e| format!("{e:?}"))?;
        let window = web_sys::window().ok_or("no window")?;
        let resp: web_sys::Response = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("{e:?}"))?
            .dyn_into()
            .map_err(|_| "not a Response".to_string())?;
        let buf = JsFuture::from(resp.array_buffer().map_err(|e| format!("{e:?}"))?).await.map_err(|e| format!("{e:?}"))?;
        Ok((resp.status(), js_sys::Uint8Array::new(&buf).to_vec()))
    }

    impl Backend for FetchBackend {
        fn send(&self, req: Req) {
            let inbox = self.inbox.clone();
            wasm_bindgen_futures::spawn_local(async move {
                let (status, body) = match run(&req).await {
                    Ok(x) => x,
                    // 0 = the request never got an HTTP answer (network, CORS, abort).
                    Err(e) => (0, e.into_bytes()),
                };
                inbox.borrow_mut().push(Resp { req, status, body });
            });
        }

        fn drain(&self) -> Vec<Resp> {
            std::mem::take(&mut *self.inbox.borrow_mut())
        }
    }
}