1use wasm_bindgen::{JsCast, prelude::*};
7use std::convert::TryFrom;
8use js_sys::{Array, Object};
9use std::sync::LazyLock;
10
11static AFRAME: LazyLock<Option<Aframe>> = LazyLock::new(Aframe::get);
12
13#[wasm_bindgen]
14extern
15{
16 #[wasm_bindgen(js_namespace = AFRAME)]
18 pub fn registerPrimitive(name: &str, definition: JsValue);
19
20 #[wasm_bindgen(js_namespace = AFRAME)]
22 pub fn registerComponent(name: &str, data: JsValue);
23
24 #[wasm_bindgen(js_namespace = AFRAME)]
26 pub fn registerSystem(name: &str, data: JsValue);
27
28 #[wasm_bindgen(js_namespace = AFRAME)]
30 pub fn registerShader(name: &str, data: JsValue);
31
32 #[wasm_bindgen(js_namespace = AFRAME)]
34 pub fn registerGeometry(name: &str, data: JsValue);
35
36 #[wasm_bindgen(js_namespace = AFRAME)]
38 pub fn registerElement(name: &str, data: JsValue);
39
40 }
53
54pub(crate) fn access_field(obj: &Object, field_name: &'static str) -> Option<JsValue>
56{
57 Object::entries(obj)
58 .iter()
59 .find(|e| e.dyn_ref::<Array>()
60 .filter(|entry| entry
61 .iter()
62 .next()
63 .and_then(|key| key.as_string())
64 .filter(|key_str| key_str == field_name)
65 .is_some())
66 .is_some())
67}
68
69pub fn three_js() -> Option<JsValue>
71{
72 AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "THREE"))
73}
74
75pub fn components() -> Option<JsValue>
77{
78 AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "components"))
79}
80
81pub fn geometries() -> Option<JsValue>
83{
84 AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "geometries"))
85}
86
87pub fn primitives() -> Option<JsValue>
89{
90 AFRAME.as_ref()
91 .and_then(|aframe| access_field(&aframe.0, "primitives"))
92 .and_then(|primitives|
93 {
94 primitives.unchecked_into::<Array>()
95 .iter()
96 .skip(1)
97 .next()
98 .and_then(|primitives| access_field(primitives.unchecked_ref(), "primitives"))
99 })
100}
101
102pub fn shaders() -> Option<JsValue>
104{
105 AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "shaders"))
106}
107
108pub fn systems() -> Option<JsValue>
110{
111 AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "systems"))
112}
113
114pub fn version() -> Option<JsValue>
116{
117 AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "version"))
118}
119
120pub fn utils() -> Option<JsValue>
121{
122 AFRAME.as_ref()
123 .and_then(|aframe| access_field(&aframe.0, "utils"))
124 .and_then(|utils| utils.unchecked_into::<Array>().iter().skip(1).next())
125}
126
127pub fn device() -> Option<JsValue>
128{
129 utils()
130 .and_then(|utils|
131 {
132 access_field(utils.unchecked_ref(), "device")
133 .and_then(|utils| utils.unchecked_into::<Array>().iter().skip(1).next())
134 })
135}
136
137struct Aframe(Object);
138unsafe impl Send for Aframe {}
139unsafe impl Sync for Aframe {}
140
141impl Aframe
142{
143 fn get() -> Option<Self>
144 {
145 web_sys::window()
146 .ok_or("Failed to access window")
147 .and_then(Aframe::try_from)
148 .ok()
149 }
150}
151
152impl TryFrom<web_sys::Window> for Aframe
153{
154 type Error = &'static str;
155
156 fn try_from(window: web_sys::Window) -> Result<Self, Self::Error>
157 {
158 window.get("AFRAME")
159 .map(Aframe)
160 .ok_or("Failed to access AFRAME global")
161 }
162}