storm 0.11.0

A personal 2D game engine designed for performance
Documentation
use crate::asset::{AssetRequest, AssetStateContract, LoaderError};
use crate::App;
use alloc::vec::Vec;
use hashbrown::HashMap;
use js_sys::{Array, Uint8Array};
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};

#[wasm_bindgen(raw_module = "./storm.js")]
extern "C" {
    fn push_asset(index: usize, paths: Array);
    fn pull_assets() -> Array;
}

pub(crate) struct AssetState<A: App> {
    count: usize,
    pending: HashMap<usize, AssetRequest<A>>,
    finished: Vec<AssetRequest<A>>,
}

impl<A: App> AssetStateContract<A> for AssetState<A> {
    fn init() -> Self {
        AssetState {
            count: 0,
            pending: HashMap::with_capacity(16),
            finished: Vec::with_capacity(16),
        }
    }

    fn read(&mut self, request: AssetRequest<A>) {
        let paths = Array::new();
        for asset in &request.assets {
            paths.push(&JsValue::from_str(&asset.relative_path));
        }
        self.pending.insert(self.count, request);
        push_asset(self.count, paths);
        self.count += 1;
    }

    fn next(&mut self) -> Option<AssetRequest<A>> {
        if self.finished.is_empty() {
            for tuple in pull_assets().iter() {
                let tuple: Array = tuple.dyn_into().unwrap();
                let slot_key = tuple.get(0).as_f64().unwrap() as usize;
                let slot_request = self.pending.remove(&slot_key);
                match slot_request {
                    Some(mut request) => {
                        let responses: Array = tuple.get(1).dyn_into().unwrap();
                        for (index, response) in responses.iter().enumerate() {
                            if response.is_object() {
                                let contents: Uint8Array = response.dyn_into().unwrap();
                                let contents = contents.to_vec();
                                request.assets[index].result = Ok(contents);
                            } else {
                                let status = response.as_f64().unwrap() as u32;
                                let status = match status {
                                    400 => LoaderError::InvalidInput,
                                    401 | 402 | 403 => LoaderError::PermissionDenied,
                                    404 => LoaderError::NotFound,
                                    _ => LoaderError::Other,
                                };
                                request.assets[index].result = Err(status);
                            }
                        }
                        self.finished.push(request);
                    }
                    None => panic!(
                        "Got asset results from Javascript, but no pending Rust request. Key [{}]",
                        slot_key
                    ),
                }
            }
        }
        self.finished.pop()
    }
}