Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/io/wasm.rs
6600 views
1
use crate::io::{
2
get_meta_path, AssetReader, AssetReaderError, EmptyPathStream, PathStream, Reader, VecReader,
3
};
4
use alloc::{borrow::ToOwned, boxed::Box, format};
5
use js_sys::{Uint8Array, JSON};
6
use std::path::{Path, PathBuf};
7
use tracing::error;
8
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
9
use wasm_bindgen_futures::JsFuture;
10
use web_sys::Response;
11
12
/// Represents the global object in the JavaScript context
13
#[wasm_bindgen]
14
extern "C" {
15
/// The [Global](https://developer.mozilla.org/en-US/docs/Glossary/Global_object) object.
16
type Global;
17
18
/// The [window](https://developer.mozilla.org/en-US/docs/Web/API/Window) global object.
19
#[wasm_bindgen(method, getter, js_name = Window)]
20
fn window(this: &Global) -> JsValue;
21
22
/// The [WorkerGlobalScope](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope) global object.
23
#[wasm_bindgen(method, getter, js_name = WorkerGlobalScope)]
24
fn worker(this: &Global) -> JsValue;
25
}
26
27
/// Reader implementation for loading assets via HTTP in Wasm.
28
pub struct HttpWasmAssetReader {
29
root_path: PathBuf,
30
}
31
32
impl HttpWasmAssetReader {
33
/// Creates a new `WasmAssetReader`. The path provided will be used to build URLs to query for assets.
34
pub fn new<P: AsRef<Path>>(path: P) -> Self {
35
Self {
36
root_path: path.as_ref().to_owned(),
37
}
38
}
39
}
40
41
fn js_value_to_err(context: &str) -> impl FnOnce(JsValue) -> std::io::Error + '_ {
42
move |value| {
43
let message = match JSON::stringify(&value) {
44
Ok(js_str) => format!("Failed to {context}: {js_str}"),
45
Err(_) => {
46
format!("Failed to {context} and also failed to stringify the JSValue of the error")
47
}
48
};
49
50
std::io::Error::other(message)
51
}
52
}
53
54
impl HttpWasmAssetReader {
55
// Also used by [`WebAssetReader`](crate::web::WebAssetReader)
56
pub(crate) async fn fetch_bytes(&self, path: PathBuf) -> Result<impl Reader, AssetReaderError> {
57
// The JS global scope includes a self-reference via a specializing name, which can be used to determine the type of global context available.
58
let global: Global = js_sys::global().unchecked_into();
59
let promise = if !global.window().is_undefined() {
60
let window: web_sys::Window = global.unchecked_into();
61
window.fetch_with_str(path.to_str().unwrap())
62
} else if !global.worker().is_undefined() {
63
let worker: web_sys::WorkerGlobalScope = global.unchecked_into();
64
worker.fetch_with_str(path.to_str().unwrap())
65
} else {
66
let error = std::io::Error::other("Unsupported JavaScript global context");
67
return Err(AssetReaderError::Io(error.into()));
68
};
69
let resp_value = JsFuture::from(promise)
70
.await
71
.map_err(js_value_to_err("fetch path"))?;
72
let resp = resp_value
73
.dyn_into::<Response>()
74
.map_err(js_value_to_err("convert fetch to Response"))?;
75
match resp.status() {
76
200 => {
77
let data = JsFuture::from(resp.array_buffer().unwrap()).await.unwrap();
78
let bytes = Uint8Array::new(&data).to_vec();
79
let reader = VecReader::new(bytes);
80
Ok(reader)
81
}
82
// Some web servers, including itch.io's CDN, return 403 when a requested file isn't present.
83
// TODO: remove handling of 403 as not found when it's easier to configure
84
// see https://github.com/bevyengine/bevy/pull/19268#pullrequestreview-2882410105
85
403 | 404 => Err(AssetReaderError::NotFound(path)),
86
status => Err(AssetReaderError::HttpError(status)),
87
}
88
}
89
}
90
91
impl AssetReader for HttpWasmAssetReader {
92
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
93
let path = self.root_path.join(path);
94
self.fetch_bytes(path).await
95
}
96
97
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
98
let meta_path = get_meta_path(&self.root_path.join(path));
99
self.fetch_bytes(meta_path).await
100
}
101
102
async fn read_directory<'a>(
103
&'a self,
104
_path: &'a Path,
105
) -> Result<Box<PathStream>, AssetReaderError> {
106
let stream: Box<PathStream> = Box::new(EmptyPathStream);
107
error!("Reading directories is not supported with the HttpWasmAssetReader");
108
Ok(stream)
109
}
110
111
async fn is_directory<'a>(&'a self, _path: &'a Path) -> Result<bool, AssetReaderError> {
112
error!("Reading directories is not supported with the HttpWasmAssetReader");
113
Ok(false)
114
}
115
}
116
117